source: cpp/frams/util/extvalue.cpp @ 875

Last change on this file since 875 was 851, checked in by Maciej Komosinski, 6 years ago

Getting int or float value casted from null or invalid is an ERROR (but still returns 0)

  • Property svn:eol-style set to native
File size: 34.0 KB
RevLine 
[286]1// This file is a part of Framsticks SDK.  http://www.framsticks.com/
[845]2// Copyright (C) 1999-2019  Maciej Komosinski and Szymon Ulatowski.
[286]3// See LICENSE.txt for details.
[109]4
5#include "extvalue.h"
6#include <frams/param/param.h>
7#include "sstringutils.h"
8#include <ctype.h>
9#include <frams/vm/classes/collectionobj.h>
10#include <frams/vm/classes/3dobject.h>
[205]11#include <frams/vm/classes/genoobj.h>
[109]12#include <common/nonstd_math.h>
13#include <common/Convert.h>
[325]14#include <climits>
[333]15#include <errno.h>
[109]16
17#ifndef NO_BARRIER
18#include <frams/simul/barrier.h>
19#include <common/threads.h>
20#endif
21
22#ifdef MULTITHREADED
23#include <pthread.h>
24//this lock only protects against ref.counter corruption caused by concurrent reads.
25//read/write conficts and nonatomicity are handled by BarrierObject (at least in theory ;-))
[337]26static pthread_mutex_t extobject_ref_lock = PTHREAD_MUTEX_INITIALIZER;
[109]27#define REF_LOCK pthread_mutex_lock(&extobject_ref_lock)
28#define REF_UNLOCK pthread_mutex_unlock(&extobject_ref_lock)
29#else
30#define REF_LOCK
31#define REF_UNLOCK
32#endif
33
34void ExtObject::incref() const
35{
[337]36        if (subtype & 1)
[109]37        {
[337]38                REF_LOCK;
39                dbobject->refcount++;
40                REF_UNLOCK;
[109]41        }
42}
43
44void ExtObject::decref() const
45{
[337]46        if (subtype & 1)
[109]47        {
[337]48                REF_LOCK;
49                bool destroy = !--dbobject->refcount;
50                REF_UNLOCK;
51                //another thread can now access the object while we are deleting it
52                //but this is not a bug since we only guarantee read/read safety
53                if (destroy) delete dbobject;
[109]54        }
55}
56
[257]57bool ExtObject::operator==(const ExtObject& src) const
58{
[337]59        if (object != src.object) return false;
60        const char* n1 = interfaceName();
61        const char* n2 = src.interfaceName();
62        return (n1 == n2) || (strcmp(n1, n2) == 0);
[257]63}
64
[109]65bool ExtObject::makeUnique()
66{
[337]67        if (!(subtype & 1)) return false;
68        if (dbobject->refcount == 1) return false;
69        VectorObject* v = VectorObject::fromObject(*this, false);
70        if (v)
[109]71        {
[337]72                VectorObject* n = new VectorObject;
73                n->data.setSize(n->data.size());
74                for (int i = 0; i < v->data.size(); i++)
[109]75                {
[337]76                        ExtValue *x = (ExtValue*)v->data(i);
77                        n->data.set(i, x ? new ExtValue(*x) : NULL);
[109]78                }
[337]79                operator=(n->makeObject());
80                return true;
[109]81        }
[337]82        return false;
[109]83}
84
[171]85void* ExtObject::getTarget(const char* classname, bool through_barrier, bool warn) const
[109]86{
[337]87        if (!strcmp(interfaceName(), classname))
88                return getTarget();
[109]89#ifndef NO_BARRIER
[337]90        if (through_barrier)
[109]91        {
[337]92                BarrierObject *bo = BarrierObject::fromObject(*this);
93                if (bo)
94                        return bo->getSourceObject().getTarget(classname, true, warn);
[109]95        }
96#endif
[171]97
[337]98        if (warn)
[171]99        {
[375]100                logPrintf("ExtValue", "getObjectTarget", LOG_WARN, "%s object expected, %s found", classname, interfaceName());
[171]101        }
102
[337]103        return NULL;
[109]104}
105
[522]106bool ExtObject::callDelegate(const char* delegate, ExtValue *args, ExtValue *ret)
[478]107{
[522]108        Param tmp;
109        ParamInterface *pi = getParamInterface(tmp);
110        if (pi)
[478]111        {
[522]112                int f = pi->findId(delegate);
113                if (f >= 0)
[478]114                {
[522]115                        pi->call(f, args, ret);
116                        return true;
[478]117                }
118        }
[522]119        logPrintf("Genotype", "get", LOG_ERROR, "Could not call delegate '%s.%s'", pi ? pi->getName() : "NULL", delegate);
120        return false;
[478]121}
[109]122
123SString ExtObject::toString() const
124{
[337]125        if (isEmpty()) return SString("null");
126        Param tmp_param;
127        ParamInterface *p = getParamInterface(tmp_param);
128        int tostr = p->findId("toString");
129        if (tostr >= 0)
[109]130        {
[337]131                return SString(p->getString(tostr));
[109]132        }
[337]133        else
[109]134        {
[337]135                SString tmp("<");
136                tmp += p->getName();
137                tmp += SString::sprintf(" object at %p>", object ? object : paraminterface);
138                return tmp;
[109]139        }
140}
141
[371]142THREAD_LOCAL_DEF(ExtObject::Serialization, ExtObject_serialization);
[109]143
144void ExtObject::Serialization::begin()
145{
[337]146        if (level == 0)
147                refs.clear();
148        level++;
[109]149}
150
151int ExtObject::Serialization::add(const ExtObject &o)
152{
[337]153        if (o.isEmpty()) return -1;
154        for (int i = 0; i < (int)refs.size(); i++)
[109]155        {
[337]156                ExtObject& r = refs[i];
157                if (r == o) return i;
[109]158        }
[337]159        refs.push_back(o);
160        return -1;
[109]161}
162
[337]163void ExtObject::Serialization::replace(const ExtObject& o, const ExtObject& other)
[109]164{
[337]165        if (o.isEmpty()) return;
166        for (int i = 0; i < (int)refs.size(); i++)
[109]167        {
[337]168                ExtObject& r = refs[i];
169                if (r == o)
[109]170                {
[337]171                        r = other;
172                        return;
[109]173                }
174        }
175}
176
177void ExtObject::Serialization::remove(const ExtObject& o)
178{
[337]179        if (o.isEmpty()) return;
180        for (int i = 0; i < (int)refs.size(); i++)
[109]181        {
[337]182                ExtObject& r = refs[i];
183                if (o == r) refs.erase(refs.begin() + i);
[109]184        }
185}
186
187const ExtObject* ExtObject::Serialization::get(int ref)
188{
[337]189        if (ref < 0) return NULL;
190        if (ref >= (int)refs.size()) return NULL;
191        return &refs[ref];
[109]192}
193
194void ExtObject::Serialization::end()
195{
[337]196        level--;
197        if (level == 0)
198                refs.clear();
[109]199}
200
[464]201SString ExtObject::serialize_inner(SerializationFormat format) const
[109]202{
[371]203        int ref = tlsGetRef(ExtObject_serialization).add(*this);
[464]204        SString ret;
205
[337]206        if (ref >= 0)
[522]207        {
208                switch (format)
[464]209                {
[522]210                case NativeSerialization: return SString::sprintf("^%d", ref);
211                case JSONSerialization: return SString("null");
[464]212                }
[522]213        }
[109]214
[337]215        if (isEmpty()) return SString("null");
[464]216        {
[522]217                VectorObject *vec = VectorObject::fromObject(*this, false);
218                if (vec)
219                {
220                        ret = vec->serialize(format); goto finally;
221                }
[464]222        }
223        {
[522]224                DictionaryObject *dic = DictionaryObject::fromObject(*this, false);
225                if (dic)
226                {
227                        ret = dic->serialize(format); goto finally;
228                }
[464]229        }
230        {
[522]231                Param tmp_param;
232                ParamInterface *p = getParamInterface(tmp_param);
233                int m = p->findId("toVector");
234                if (m < 0)
235                        m = p->findId("toDictionary");
236                if (m >= 0)
237                {
238                        ExtObject o(p->getObject(m));
239                        switch (format)
[464]240                        {
[522]241                        case NativeSerialization: ret = SString(interfaceName()) + o.serialize(format); break;
242                        case JSONSerialization: ret = SString::sprintf("{\"class\":\"%s\",\"data\":%s}", interfaceName(), o.serialize(format).c_str()); break;
[464]243                        }
[522]244                        goto finally;
245                }
246                m = p->findId("toString");
247                if (m >= 0)
248                {
249                        SString str = p->getString(m);
250                        sstringQuote(str);
251                        switch (format)
[464]252                        {
[522]253                        case NativeSerialization: ret = SString(interfaceName()) + "\"" + str + "\""; break;
254                        case JSONSerialization: ret = SString::sprintf("{\"class\":\"%s\",\"data\":\"%s\"}", interfaceName(), str.c_str()); break;
[464]255                        }
[522]256                        goto finally;
257                }
[109]258        }
259
[371]260        tlsGetRef(ExtObject_serialization).remove(*this);//undo nonserializable reference
[522]261        switch (format)
262        {
263        case NativeSerialization: return SString(interfaceName()) + SString::sprintf("<%p>", object ? object : paraminterface); break;
264        case JSONSerialization: return SString::sprintf("{\"class\":\"%s\"}", interfaceName()); break;
265        }
[464]266
[522]267        finally: // not 100% "finally", the case of nonserializable reference (directly above) returns directly without going through finally
[464]268
[522]269        switch (format)
270        {
271        case JSONSerialization:
272                tlsGetRef(ExtObject_serialization).remove(*this);//JSON only tracks recursion, does not track reuse
273                break;
274        case NativeSerialization:; //nop (just to avoid compiler warning)
275        }
[464]276
[337]277        return ret;
[109]278}
279
[464]280SString ExtObject::serialize(SerializationFormat format) const
[109]281{
[371]282        tlsGetRef(ExtObject_serialization).begin();
[464]283        SString ret = serialize_inner(format);
[371]284        tlsGetRef(ExtObject_serialization).end();
[337]285        return ret;
[109]286}
287
288///////////////////////////////////////
289
[228]290SString ExtValue::typeDescription() const
291{
[337]292        switch (type)
[228]293        {
[337]294        case TInt: return SString("int");
295        case TDouble: return SString("float");
296        case TString: return SString("string");
297        case TUnknown: return SString("null");
298        case TInvalid: return SString("invalid");
299        case TObj: return getObject().isEmpty() ? SString("null") : SString(getObject().interfaceName());
[228]300        }
[337]301        return SString::empty();
[228]302}
303
[337]304SString ExtValue::typeAndValue() const
[171]305{
[337]306        SString msg = typeDescription();
[786]307        SString delimit("'");
[337]308        switch (type)
[171]309        {
[786]310        case TString:
311                delimit = "\"";
312        case TInt: case TDouble: case TObj:
313                msg += " ";
314                msg += sstringDelimitAndShorten(getString(), 50, (type == TString), delimit, delimit);
[337]315        default:;
316        }
317        return msg;
318}
319
320void *ExtValue::getObjectTarget(const char* classname, bool warn) const
321{
322        if (type != TObj)
323        {
324                if (warn)
[171]325                {
[337]326                        SString tmp = getString();
327                        if (tmp.len() > 30) tmp = tmp.substr(0, 30) + "...";
328                        if (type == TString) tmp = SString("\"") + tmp + SString("\"");
[375]329                        logPrintf("ExtValue", "getObjectTarget", LOG_WARN, "%s object expected, %s found", classname, tmp.c_str());
[171]330                }
[337]331                return NULL;
[171]332        }
333
[337]334        return getObject().getTarget(classname, true, warn);
[171]335}
336
[109]337void ExtValue::set(const ExtValue& src)
338{
[337]339        switch (src.type)
[109]340        {
341        case TString: sets(src.sdata()); break;
342        case TInt: seti(src.idata()); break;
343        case TDouble: setd(src.ddata()); break;
344        case TObj: seto(src.odata()); break;
[337]345        default:type = src.type; break;
[109]346        }
347}
348
349void ExtValue::setEmpty()
350{
[337]351        switch (type)
[109]352        {
353#ifdef EXTVALUEUNION
354        case TString: sdata().~SString(); break;
355        case TObj: odata().~ExtObject(); break;
356#else
357        case TString: delete s; break;
358        case TObj: delete o; break;
359#endif
360        default:;
361        }
[337]362        type = TUnknown;
[109]363}
364
[490]365void ExtValue::setError(const SString& msg)
366{
[522]367        ErrorObject *err = new ErrorObject;
368        err->message = msg;
369        setObject(ErrorObject::makeDynamicObject(err));
[490]370}
371
[333]372static ExtValue::CompareResult longsign(paInt x)
[109]373{
[337]374        if (x < 0) return ExtValue::ResultLower;
375        if (x > 0) return ExtValue::ResultHigher;
376        return ExtValue::ResultEqual;
[109]377}
378
[333]379static ExtValue::CompareResult compareNull(const ExtValue& v)
[144]380{
[337]381        if (v.isNull()) return ExtValue::ResultEqualUnordered;
382        if ((v.getType() == TInt) && (v.getInt() == 0)) return ExtValue::ResultUnequal_RelaxedEqual;
383        return ExtValue::ResultUnequal_RelaxedUnequal; //comparing anything else with null is valid but null is neither higher nor lower than numbers or strings
[144]384}
385
[490]386static ExtValue::CompareResult compareInvalid(const ExtValue& v)
387{
[522]388        if (v.getType() == TInvalid) return ExtValue::ResultEqualUnordered;
[490]389        if ((v.getType() == TInt) && (v.getInt() == 0)) return ExtValue::ResultUnequal_RelaxedEqual;
390        return ExtValue::ResultMismatch; //comparing anything else with invalid is invalid
391}
392
[337]393static ExtValue::CompareResult compareFloat(double a, double b)
[109]394{
[337]395        double t = a - b;
396        if (t < 0) return ExtValue::ResultLower;
397        else if (t > 0) return ExtValue::ResultHigher;
398        return ExtValue::ResultEqual;
[333]399}
400
[337]401static ExtValue::CompareResult compareString(const SString &a, const SString &b)
[333]402{
[348]403        const char* s1 = a.c_str();
404        const char* s2 = b.c_str();
[337]405        return longsign(strcmp(s1, s2));
[333]406}
407
408ExtValue::CompareResult ExtValue::compare(const ExtValue& src) const
409{
[337]410        if (isNull())
411                return compareNull(src);
412        else if (src.isNull())
413                return compareNull(*this);
[522]414        if (getType() == TInvalid)
[490]415                return compareInvalid(src);
[522]416        else if (src.getType() == TInvalid)
[490]417                return compareInvalid(*this);
[337]418        switch (type)
[109]419        {
[333]420
[109]421        case TInt:
[333]422
[337]423                if (src.getType() == TInt)
424                {
425                        paInt t = src.getInt();
426                        if (idata() > 0)
[333]427                        {
[337]428                                if (t > 0) return longsign(idata() - t); else return ResultHigher;
429                        }
[333]430                        else
[337]431                        {
432                                if (t <= 0) return longsign(idata() - t); else return ResultLower;
[333]433                        }
[337]434                }
435                else if (src.getType() == TDouble)
436                        return compareFloat((double)idata(), src.getDouble());
[845]437                else if ((getInt() == 0) && (src.getType() == TString))
438                        return ResultMismatch_RelaxedUnequal;
[333]439                else
440                        return ResultMismatch;//comparing numbers with other things is invalid
441                break;
442
[109]443        case TDouble:
[337]444                if ((src.getType() == TDouble) || (src.getType() == TInt))
445                        return compareFloat(getDouble(), src.getDouble());
[333]446                else
447                        return ResultMismatch;
448                break;
449
[109]450        case TString:
[337]451                if (src.getType() == TString)
452                        return compareString(sdata(), src.getString());
[845]453                else if ((src.type == TInt) && (src.getInt() == 0))
454                        return ResultMismatch_RelaxedUnequal;
[333]455                else
456                        return ResultMismatch;
457                break;
458
[337]459        case TObj:
[109]460        {
[337]461                if (src.type == TObj)
462                        return odata() == src.odata() ? ResultEqualUnordered : ResultUnequal_RelaxedUnequal;
463                if ((src.type == TInt) && (src.getInt() == 0))
464                        return ResultMismatch_RelaxedUnequal;
465                return ResultMismatch;
[109]466        }
467        default:;
468        }
[337]469        return ResultMismatch;
[109]470}
471
[337]472const char* ExtValue::cmp_op_names[] = { "==", "!=", ">=", "<=", ">", "<", "~=", "!~", NULL };
[333]473
[337]474int ExtValue::interpretCompare(CmpOperator op, CompareResult result, CmpContext *context)
[333]475{
[337]476        CompareResult error_threshold = ResultUnequal_RelaxedEqual;//error when ResultUnequal_RelaxedEqual or higher (not comparable)
477        int ret = 0;
478        switch (op)
[333]479        {
[337]480        case CmpEQ: ret = (result == ResultEqual) || (result == ResultEqualUnordered); error_threshold = ResultMismatch_RelaxedUnequal; break;
481        case CmpNE: ret = !((result == ResultEqual) || (result == ResultEqualUnordered)); error_threshold = ResultMismatch_RelaxedUnequal; break;
482        case CmpGT: ret = (result == ResultHigher); error_threshold = ResultEqualUnordered; break;
483        case CmpGE: ret = (result == ResultEqual) || (result == ResultHigher); error_threshold = ResultEqualUnordered; break;
484        case CmpLT: ret = (result == ResultLower); error_threshold = ResultEqualUnordered; break;
485        case CmpLE: ret = (result == ResultEqual) || (result == ResultLower); error_threshold = ResultEqualUnordered; break;
486        case CmpREQ: ret = (result == ResultEqual) || (result == ResultEqualUnordered) || (result == ResultUnequal_RelaxedEqual); error_threshold = ResultMismatch; break;
487        case CmpRNE: ret = !((result == ResultEqual) || (result == ResultEqualUnordered) || (result == ResultUnequal_RelaxedEqual)); error_threshold = ResultMismatch; break;
[333]488        default:;
489        }
[337]490        if (result >= error_threshold)
[333]491        {
[337]492                SString msg = "Type mismatch while comparing";
493                if (context)
[333]494                {
[337]495                        if (context->v1 && context->v2)
496                                msg += SString::sprintf(": %s %s %s",
[845]497                                        context->v1->typeAndValue().c_str(),
498                                        cmp_op_names[op - CmpFIRST],
499                                        context->v2->typeAndValue().c_str());
[333]500                }
[375]501                logPrintf("ExtValue", "interpretCompare", LOG_ERROR, "%s", msg.c_str());
[337]502                ret = -1;
[333]503        }
[337]504        return ret;
[333]505}
506
[109]507int ExtValue::operator==(const ExtValue& src) const
508{
[337]509        if (type != src.type) return 0;
510        switch (type)
[109]511        {
[337]512        case TInt: return idata() == src.idata();
513        case TDouble: return ddata() == src.ddata();
514        case TString: return sdata() == src.sdata();
515        case TObj: return odata() == src.odata();
[109]516        default:;
517        }
[337]518        return 1;
[109]519}
520
521void ExtValue::operator+=(const ExtValue& src)
522{
[337]523        // return = ok, break = fail
524        switch (type)
[109]525        {
[337]526        case TInt:
527                switch (src.getType())
528                {
529                case TDouble:
530                        setDouble(double(getInt()) + src.getDouble());
531                        return;
532                case TString:
533                        break;
534                default:
535                        idata() += src.getInt();
536                        return;
537                }
538                break;
539        case TDouble:
540                switch (src.getType())
541                {
542                case TString:
543                        break;
544                default:
545                        ddata() += src.getDouble();
546                        return;
547                }
548                break;
549        case TString: sdata() += src.getString(); return;
[109]550        case TObj:
[337]551        {
552                VectorObject *vec = VectorObject::fromObject(getObject(), false);
553                VectorObject *vec2 = VectorObject::fromObject(src.getObject(), false);
554                if (vec && vec2)
[109]555                {
[337]556                        for (int i = 0; i < vec2->data.size(); i++)
[109]557                        {
[337]558                                ExtValue *s = (ExtValue*)vec2->data(i);
559                                ExtValue *d = s ? new ExtValue(*s) : NULL;
560                                vec->data += d;
561                        }
[228]562                        return;
[109]563                }
[337]564        }
[845]565        //NO break;
[109]566        default:;
567        }
[375]568        logPrintf("ExtValue", "add", LOG_ERROR, "Can't add %s to %s", src.typeAndValue().c_str(), typeAndValue().c_str());
[109]569}
570
571void ExtValue::operator-=(const ExtValue& src)
572{
[337]573        // return = ok, break = fail
574        switch (type)
[109]575        {
[337]576        case TInt:
577                switch (src.getType())
578                {
579                case TInt:
580                        idata() -= src.getInt();
581                        return;
582                case TDouble:
583                        setDouble(double(getInt()) - src.getDouble());
584                        return;
585                default:;
586                }
[228]587                break;
[337]588        case TDouble:
589                switch (src.getType())
590                {
591                case TDouble:
592                case TInt:
593                        ddata() -= src.getDouble();
594                        return;
595                default:;
596                }
597                break;
[109]598        default:;
599        }
[375]600        logPrintf("ExtValue", "subtract", LOG_ERROR, "Can't subtract %s from %s", src.typeAndValue().c_str(), typeAndValue().c_str());
[109]601}
602
603void ExtValue::operator*=(const ExtValue& src)
604{
[337]605        // return = ok, break = fail
606        switch (type)
[109]607        {
[337]608        case TInt:
609                switch (src.getType())
610                {
611                case TInt:
612                        idata() *= src.getInt();
613                        return;
614                case TDouble:
615                        setDouble(double(getInt())*src.getDouble());
616                        return;
617                default:;
618                }
619                break;
620        case TDouble:
621                switch (src.getType())
622                {
623                case TInt:
624                case TDouble:
625                        ddata() *= src.getDouble();
626                        return;
627                default:;
628                }
629                break;
[109]630        case TString:
[337]631                switch (src.getType())
632                {
633                case TInt: case TDouble:
634                {
635                        SString t;
636                        for (int n = src.getInt(); n > 0; n--)
637                                t += getString();
638                        setString(t);
639                        return;
640                }
641                default:;
642                }
643                break;
644        case TObj:
[109]645        {
[337]646                VectorObject *vec = VectorObject::fromObject(getObject(), false);
647                if (vec)
[109]648                {
[337]649                        int n = src.getInt();
650                        int orig_size = vec->data.size();
651                        if (n <= 0)
[109]652                        {
[337]653                                vec->clear(); return;
654                        }
655                        for (; n > 1; n--)
656                        {
657                                for (int i = 0; i < orig_size; i++)
[109]658                                {
[337]659                                        ExtValue *s = (ExtValue*)vec->data(i);
660                                        ExtValue *d = s ? new ExtValue(*s) : NULL;
661                                        vec->data += d;
[109]662                                }
[337]663                        }
[228]664                        return;
665                }
[337]666        }
[845]667        //NO break;
[109]668        default:;
669        }
[375]670        logPrintf("ExtValue", "multiply", LOG_WARN, "Can't multiply %s by %s", typeAndValue().c_str(), src.typeAndValue().c_str());
[109]671}
672
673/*#include "fpu_control.h"
674#include <signal.h>
675
676static int fpuexception;
677void mathhandler(int sig)
678{
679printf("fpu exception!\n");
680fpuexception=1;
681signal(SIGFPE,SIG_IGN);
682} */
683
[337]684void ExtValue::divInt(paInt a)
685{
686        if (a)
687                idata() /= a;
688        else
689        {
[478]690                logPrintf("ExtValue", "divide", LOG_ERROR, "Division by zero: %d/0", idata());
[337]691                setInvalid();
692        }
693}
694
695void ExtValue::divDouble(double a)
696{
697        if (a == 0.0)
698        {
[478]699                logPrintf("ExtValue", "divide", LOG_ERROR, "Division by zero: %s/0.0", getString().c_str());
[337]700                setInvalid();
701        }
702        else
703        {
704                fpExceptDisable();
705                double tmp = getDouble() / a;
706                if (!finite(tmp))
707                {
[478]708                        logPrintf("ExtValue", "divide", LOG_ERROR, "Overflow %s/%g", getString().c_str(), a); setInvalid();
[337]709                }
710                else
711                        setDouble(tmp);
712                // niby dobrze ale lepiej byloby to robic bardziej systematycznie a nie tylko w dzieleniu?
713                //if (isnan(ddata())) //http://www.digitalmars.com/d/archives/c++/Traping_divide_by_zero_5728.html
[375]714                //        { logPrintf("ExtValue","divide",LOG_ERROR,"not-a-number",(const char*)getString()); setInvalid(); }
[337]715                fpExceptEnable();
716        }
717}
718
[109]719void ExtValue::operator/=(const ExtValue& src)
720{
[337]721        switch (type)
[109]722        {
723        case TInt:
[337]724                switch (src.getType())
725                {
726                case TInt:
727                        divInt(src.idata());
728                        return;
729                case TDouble:
730                        divDouble(src.ddata());
731                        return;
732                default:;
733                }
[109]734                break;
735
736        case TDouble:
[337]737                switch (src.getType())
[109]738                {
[337]739                case TInt:
740                        divDouble(src.getDouble());
741                        return;
742                case TDouble:
743                        divDouble(src.ddata());
744                        return;
745                default:;
[109]746                }
747                break;
748
749        default:;
750        }
[375]751        logPrintf("ExtValue", "divide", LOG_ERROR, "Can't divide %s by %s", typeAndValue().c_str(), src.typeAndValue().c_str());
[109]752}
753
[639]754SString ExtValue::formatTime(char fmt, double value)
[109]755{
[753]756        if (fmt == 'i')
[639]757        { //could be Convert::ctime()
[753]758                int d, h, m, ti = value;
759                int ms = 1000 * (value - ti);
760                d = ti / 86400; ti -= d * 86400;
761                h = ti / 3600; ti -= h * 3600;
762                m = ti / 60; ti -= m * 60;
763                SString ret;
764                if (d > 0) ret += SString::sprintf("%dd ", d);
765                if (h > 0) ret += SString::sprintf("%d:", h);
766                ret += SString::sprintf("%02d:%02d.%03d", m, ti, ms);
767                return ret;
[639]768        }
[753]769        time_t ti = value;
770        struct tm tm = Convert::localtime(ti);
771        switch (fmt)
[639]772        {
773        case 'T': return SString::sprintf("%04d-%02d-%02d %02d:%02d:%02d", 1900 + tm.tm_year, 1 + tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
774        case 't': return SString(Convert::asctime(tm).c_str());
775        case 'y': return SString::sprintf("%04d-%02d-%02d", 1900 + tm.tm_year, 1 + tm.tm_mon, tm.tm_mday);
[753]776        case 'm': return SString::sprintf("%04d-%02d-%02d %02d:%02d:%02d.%03d", 1900 + tm.tm_year, 1 + tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, int(1000 * (value - (double)ti)));
[639]777        }
[753]778        return SString();
[639]779}
780
781SString ExtValue::format(const SString& fmt, const ExtValue **values, int count)
782{
783        SString ret;
[337]784        // "..........%.........%..........%........"
785        //  ^_cur     ^_next
786        //  ^^^^^^^^^^___sub
787        //
788        // "..........%.........%..........%........"
789        //            ^-cur     ^-next
790        //            ^^^^^^^^^^___sub
[348]791        const char* begin = fmt.c_str(), *end = begin + fmt.len(), *curr = begin;
[337]792        int type = 0;
[109]793
[337]794        class Args
795        {
796                const ExtValue **values;
797                int count;
798                int arg;
799        public:
800                Args(const ExtValue **v, int c) :values(v), count(c), arg(0) {}
801                bool finished() { return arg >= count; }
802                const ExtValue *getNext() { const ExtValue *ret = NULL; if ((arg < count) && values[arg]) ret = values[arg]; arg++; return ret; }
803        };
804        Args args(values, count);
[109]805
[337]806        while (curr < end)
[109]807        {
[337]808                const char* next = strchr(curr, '%');
809                if (!next) next = end; else if ((next == curr) && (curr > begin))
[109]810                {
[337]811                        next = strchr(next + 1, '%'); if (!next) next = end;
[109]812                }
[337]813                type = 0;
814                if (curr > begin)
[109]815                {
[337]816                        type = 0;
817                        for (const char* t = curr; t < next; t++)
818                                switch (*t)
[845]819                                {
[337]820                                case 'd': case 'x': case 'X': case 'u': case 'p': case 'c': type = 'd'; t = next; break;
821                                case 'f': case 'g': case 'e': type = 'f'; t = next; break;
822                                case 's': type = 's'; t = next; break;
[639]823                                case 't': case 'T': case 'y': case 'i': case 'm': type = *t; t = next; break;
[337]824                                case '%': if (t > begin) { type = *t; t = next; } break;
[845]825                                }
[109]826                }
[337]827                if (curr > begin) curr--;
828                const ExtValue *a;
829                if (args.finished() && (type != 0) && (type != '%'))
[109]830                {
[337]831                        ret += fmt.substr((int)(curr - begin));
832                        break;
833                }
834                SString sub = fmt.substr((int)(curr - begin), (int)(next - curr));
835                switch (type)
836                {
[348]837                case 'd': a = args.getNext(); ret += SString::sprintf(sub.c_str(), a ? a->getInt() : 0); break;
838                case 'f': a = args.getNext(); ret += SString::sprintf(sub.c_str(), a ? a->getDouble() : 0); break;
839                case 's': {a = args.getNext(); SString tmp; if (a) tmp = a->getString(); ret += SString::sprintf(sub.c_str(), tmp.c_str()); } break;
[639]840                case 't': case 'T': case 'i': case 'y': case 'm':
[337]841                        a = args.getNext();
[753]842                        ret += formatTime(type, a ? a->getDouble() : 0);
[337]843                        ret += sub.substr(2);
[109]844                        break;
[337]845                case '%': ret += '%'; ret += sub.substr(2); break;
846                case 0: ret += sub; break;
[109]847                }
[337]848                curr = next + 1;
[109]849        }
[337]850        return ret;
[109]851}
852
853
[636]854void ExtValue::modInt(paInt a)
855{
856        if (a)
857                idata() %= a;
858        else
859        {
860                logPrintf("ExtValue", "modulo", LOG_ERROR, "Modulo by zero: %d%%0", idata());
861                setInvalid();
862        }
863}
864
865void ExtValue::modDouble(double a)
866{
867        if (a == 0.0)
868        {
869                logPrintf("ExtValue", "modulo", LOG_ERROR, "Modulo by zero: %s%%0.0", getString().c_str());
870                setInvalid();
871        }
872        else
[753]873                setDouble(fmod(ddata(), a));
[636]874}
875
[109]876void ExtValue::operator%=(const ExtValue& src)
877{
[337]878        switch (type)
[109]879        {
[636]880        case TInt: modInt(src.getInt()); break;
881        case TDouble: modDouble(src.getDouble()); break;
[109]882
883        case TString:
884        {
[337]885                VectorObject *vec = VectorObject::fromObject(src.getObject(), false);
886                if (vec)
887                        sdata() = format(sdata(), (const ExtValue**)&vec->data.getref(0), vec->data.size());
888                else
889                {
890                        const ExtValue *ptr = &src; sdata() = ExtValue::format(sdata(), &ptr, 1);
891                }
[109]892        }
[845]893        break;
[109]894
[228]895        case TObj: case TUnknown: case TInvalid:
[375]896                logPrintf("ExtValue", "modulo", LOG_WARN, "Can't apply modulo to %s", typeDescription().c_str());
[228]897
[109]898        default:;
899        }
900}
901
[326]902bool ExtValue::parseInt(const char* s, paInt &result, bool strict, bool error)
[144]903{
[325]904        ExtValue tmp;
905        const char* after = tmp.parseNumber(s, strict ? TInt : TUnknown);
[326]906        if ((after == NULL) || (after[0] != 0))
[337]907        {
908                if (error)
[375]909                        logPrintf("ExtValue", "parseInt", LOG_ERROR, "Could not parse '%s'%s", s, strict ? " (strict)" : "");
[326]910                return false;
[337]911        }
[325]912        result = tmp.getInt();
913        return true;
[144]914}
915
[326]916bool ExtValue::parseDouble(const char* s, double &result, bool error)
[325]917{
918        ExtValue tmp;
919        const char* after = tmp.parseNumber(s, TDouble);
[326]920        if ((after == NULL) || (after[0] != 0))
[337]921        {
922                if (error)
[375]923                        logPrintf("ExtValue", "parseDouble", LOG_ERROR, "Could not parse '%s'", s);
[326]924                return false;
[337]925        }
[325]926        result = tmp.getDouble();
927        return true;
928}
929
[337]930paInt ExtValue::getInt(const char* s, bool strict)
[325]931{
932        paInt result;
[326]933        if (parseInt(s, result, strict, true))
[325]934                return result;
935        return 0;
936}
937
[144]938double ExtValue::getDouble(const char* s)
939{
[325]940        double result;
[326]941        if (parseDouble(s, result, true))
[325]942                return result;
943        return 0;
[144]944}
945
[247]946paInt ExtValue::getInt() const
[109]947{
[325]948        switch (type)
[109]949        {
950        case TInt: return idata();
[794]951        case TDouble:
952        {
953                bool toobig = false;
954                if (((toobig = (ddata() > (double)INT_MAX)))
955                        || (ddata() < (double)INT_MIN))
956                {
957                        logPrintf("ExtValue", "getInt", LOG_ERROR, "Overflow when converting floating point value to integer; using %s allowed integer '%d' instead of '%s'", toobig ? "maximal" : "minimal", toobig ? INT_MAX : INT_MIN, getString().c_str());
958                        return toobig ? INT_MAX : INT_MIN;
959                }
960                return (int)ddata();
961        }
[348]962        case TString: return getInt(sdata().c_str());
[144]963        case TObj:
[478]964                logPrintf("ExtValue", "getInt", LOG_ERROR, "Getting integer value from object reference (%s)", getString().c_str());
[247]965                return (paInt)(intptr_t)odata().param;
[851]966        case TUnknown:
967        case TInvalid:
968                logPrintf("ExtValue", "getInt", LOG_ERROR, "Getting integer value from %s", getString().c_str());
969                return 0;
[109]970        default:;
971        }
[325]972        return 0;
[109]973}
[144]974
[109]975double ExtValue::getDouble() const
976{
[325]977        switch (type)
[109]978        {
979        case TDouble: return ddata();
980        case TInt: return (double)idata();
[348]981        case TString: return getDouble(sdata().c_str());
[144]982        case TObj:
[478]983                logPrintf("ExtValue", "getDouble", LOG_ERROR, "Getting floating point value from object reference (%s)", getString().c_str());
[247]984                return (double)(intptr_t)odata().param;
[851]985        case TUnknown:
986        case TInvalid:
987                logPrintf("ExtValue", "getInt", LOG_ERROR, "Getting floating point value from %s", getString().c_str());
988                return 0.0;
[109]989        default:;
990        }
[325]991        return 0.0;
[109]992}
[325]993
[109]994SString ExtValue::getString() const
995{
[325]996        switch (type)
[109]997        {
998        case TString: return sdata();
999        case TInt: return SString::valueOf(idata());
1000        case TDouble: return SString::valueOf(ddata());
1001        case TObj: return odata().toString();
1002        case TInvalid:  return SString("invalid");
1003        default: return SString("null");
1004        }
1005}
1006
1007const SString* ExtValue::getStringPtr() const
1008{
[325]1009        if (type == TString)
1010                return &sdata();
1011        return NULL;
[109]1012}
1013
[464]1014SString ExtValue::serialize(SerializationFormat format) const
[109]1015{
[337]1016        switch (type)
[109]1017        {
1018        case TString:
[337]1019        {
1020                SString q = sdata();
[109]1021                sstringQuote(q);
[337]1022                return SString("\"") + q + SString("\"");
1023        }
[109]1024        case TInt:
1025                return SString::valueOf(idata());
1026        case TDouble:
1027                return SString::valueOf(ddata());
1028        case TObj:
[464]1029                return odata().serialize(format);
[109]1030        case TInvalid:
[522]1031                if (format == NativeSerialization)
[464]1032                        return SString("invalid");
1033                // else null --v
[109]1034        default:
1035                return SString("null");
1036        }
1037}
1038
[325]1039/// returns the first character after the parsed number, or NULL if not a number
1040/// @param strict_type = restrict the allowed return value (TUnknown = unrestricted)
1041const char* ExtValue::parseNumber(const char* in, ExtPType strict_type)
[109]1042{
[325]1043        char* after;
1044        if (in == NULL) return NULL;
1045        if (in[0] == 0) return NULL;
1046        while (isspace(*in)) in++;
[326]1047        bool minus = (in[0] == '-');
1048        bool plus = (in[0] == '+');
[337]1049        if (((in[0] == '0') && ((in[1] == 'x') || (in[1] == 'X')))
1050                || (((minus || plus) && (in[1] == '0') && ((in[2] == 'x') || (in[2] == 'X')))))
[109]1051        {
[326]1052                in += (minus || plus) ? 3 : 2;
[325]1053                if (isspace(*in)) return NULL;
1054                errno = 0;
1055                unsigned long intvalue = strtoul(in, &after, 16);
1056                if ((after > in) && (errno == 0) && (intvalue <= 0xffffffff))
[109]1057                {
[325]1058                        if (strict_type == TDouble)
1059                                setDouble(minus ? -(double)intvalue : (double)intvalue);
1060                        else
[698]1061                                setInt(minus ? -(paInt)intvalue : (paInt)intvalue);
[325]1062                        return after;
[109]1063                }
[325]1064                else
1065                        return NULL;
1066        }
[109]1067
[325]1068        errno = 0;
1069        double fpvalue = strtod(in, &after);
1070        if ((after > in) && (errno == 0))
1071        {
1072                if (strict_type != TDouble)
[109]1073                {
[325]1074                        if ((memchr(in, '.', after - in) == NULL) && (memchr(in, 'e', after - in) == NULL) && (memchr(in, 'E', after - in) == NULL) // no "special" characters
1075                                && (fpvalue == floor(fpvalue)) // value is integer
1076                                && (fpvalue >= INT_MIN) && (fpvalue <= INT_MAX)) // within limits
1077                        {
1078                                setInt(fpvalue);
1079                                return after;
1080                        }
1081                        else if (strict_type == TInt)
1082                                return NULL;
[109]1083                }
[325]1084                setDouble(fpvalue);
1085                return after;
[109]1086        }
[325]1087        return NULL;
[109]1088}
1089
[222]1090PtrListTempl<ParamInterface*> &ExtValue::getDeserializableClasses()
[109]1091{
[337]1092        static PtrListTempl<ParamInterface*> classes;
1093        return classes;
[109]1094}
1095
1096ParamInterface *ExtValue::findDeserializableClass(const char* name)
1097{
[337]1098        FOREACH(ParamInterface*, cls, getDeserializableClasses())
1099                if (!strcmp(cls->getName(), name))
1100                        return cls;
1101        return NULL;
[109]1102}
1103
1104static const char* skipWord(const char* in)
1105{
[337]1106        while (isalpha(*in) || (*in == '_'))
1107                in++;
1108        return in;
[109]1109}
1110
1111//returns the first character after the parsed portion or NULL if invalid format
1112const char* ExtValue::deserialize_inner(const char* in)
1113{
[522]1114        while (isspace(*in)) in++;
[337]1115        const char* ret = parseNumber(in);
1116        if (ret)
1117                return ret;
1118        else if (*in == '\"')
[109]1119        {
[337]1120                ret = skipQuoteString(in + 1, NULL);
1121                SString s(in + 1, (int)(ret - (in + 1)));
1122                sstringUnquote(s);
1123                setString(s);
1124                if (*ret == '\"')
1125                        return ret + 1;
1126                else
[333]1127                {
[375]1128                        logPrintf("ExtValue", "deserialize", LOG_ERROR, "Missing '\"' in string: '%s'", ret);
[337]1129                        return NULL;
[333]1130                }
[109]1131        }
[337]1132        else if (*in == '[')
[109]1133        {
[337]1134                VectorObject *vec = new VectorObject;
1135                ExtObject o(&VectorObject::par, vec);
[371]1136                tlsGetRef(ExtObject_serialization).add(o);
[337]1137                const char* p = in + 1;
[753]1138                ExtValue tmp; bool first = true, comma = false;
1139                const char* prev_element = p;
1140                while (true)
[109]1141                {
[753]1142                        while (isspace(*p)) p++;
1143                        if (*p == 0)
1144                        {
1145                                logPrintf("ExtValue", "deserialize", LOG_ERROR, "Missing ']' in Vector");
1146                                return NULL;
1147                        }
1148                        if (*p == ']')
1149                        {
1150                                if (comma)
1151                                {
1152                                        logPrintf("ExtValue", "deserialize", LOG_ERROR, "No element after ',' in Vector");
1153                                        return NULL;
1154                                }
1155                                p++;
1156                                break;
1157                        }
1158                        if (!first && !comma)
1159                        {
1160                                logPrintf("ExtValue", "deserialize", LOG_ERROR, "Missing ',' in Vector: '%s'", prev_element);
1161                                return NULL;
1162                        }
[337]1163                        ret = tmp.deserialize(p);
[753]1164                        prev_element = p; first = false; comma = false;
[337]1165                        if (ret)
[109]1166                        {
[337]1167                                vec->data += new ExtValue(tmp);
1168                                p = ret;
[753]1169                                if (*p == ',') { p++; comma = true; }
[109]1170                        }
[337]1171                        else
[109]1172                        {
[337]1173                                p = NULL;
1174                                break;
[109]1175                        }
1176                }
[337]1177                setObject(o);
1178                return p;
[109]1179        }
[337]1180        else if (*in == '{')
[109]1181        {
[337]1182                DictionaryObject *dic = new DictionaryObject;
1183                ExtObject o(&DictionaryObject::par, dic);
[371]1184                tlsGetRef(ExtObject_serialization).add(o);
[337]1185                const char* p = in + 1;
1186                ExtValue args[2]/*={value,key}*/, dummy_ret;
[753]1187                bool first = true, comma = false;
1188                const char* prev_element = p;
1189                while (true)
[109]1190                {
[753]1191                        while (isspace(*p)) p++;
1192                        if (*p == 0)
1193                        {
1194                                logPrintf("ExtValue", "deserialize", LOG_ERROR, "Missing '}' in Dictionary");
1195                                return NULL;
1196                        }
1197                        if (*p == '}')
1198                        {
1199                                if (comma)
1200                                {
1201                                        logPrintf("ExtValue", "deserialize", LOG_ERROR, "No element after ',' in Dictionary");
1202                                        return NULL;
1203                                }
1204                                p++;
1205                                break;
1206                        }
1207                        if (!first && !comma)
1208                        {
1209                                logPrintf("ExtValue", "deserialize", LOG_ERROR, "Missing ',' in Dictionary: '%s'", prev_element);
1210                                return NULL;
1211                        }
[337]1212                        ret = args[1].deserialize(p);
[753]1213                        prev_element = p; first = false; comma = false;
[337]1214                        if ((!ret) || (args[1].getType() != TString)) { p = NULL; break; }
1215                        p = ret;
[375]1216                        if (*p != ':') { logPrintf("ExtValue", "deserialize", LOG_ERROR, "Missing ':' in Dictionary: '%s'", p); p = NULL; break; }
[337]1217                        p++;
1218                        ret = args[0].deserialize(p);
1219                        if (!ret) { p = NULL; break; }
1220                        p = ret;
1221                        dic->p_set(args, &dummy_ret);
[753]1222                        if (*p == ',') { p++; comma = true; }
[109]1223                }
[337]1224                setObject(o);
1225                return p;
[109]1226        }
[337]1227        else if (!strncmp(in, "null", 4))
[109]1228        {
[337]1229                setEmpty();
1230                return in + 4;
[109]1231        }
[472]1232        else if (!strncmp(in, "true", 4))
1233        {
1234                setInt(1);
1235                return in + 4;
1236        }
1237        else if (!strncmp(in, "false", 5))
1238        {
1239                setInt(0);
1240                return in + 5;
1241        }
[490]1242        else if (!strncmp(in, "invalid", 7))
[109]1243        {
[337]1244                setInvalid();
[490]1245                return in + 7;
[109]1246        }
[337]1247        else if (*in == '<')
[109]1248        { //unserializable object
[522]1249                const char* end = in + 1;
[490]1250                while (*end)
1251                        if (*end == '>')
[522]1252                        {
[845]1253                                setError(SString("Unserializable class: ") + SString(in + 1, end - in - 1));
1254                                return end + 1;
[522]1255                        }
[490]1256                        else
1257                                end++;
1258                logPrintf("ExtValue", "deserialize", LOG_ERROR, "Missing '>'");
1259                return NULL;
[109]1260        }
[337]1261        else if (*in == '^')
[109]1262        {
[337]1263                in++;
1264                ExtValue ref;
1265                ret = ref.parseNumber(in, TInt);
1266                if (ret && (ref.getType() == TInt))
[109]1267                {
[371]1268                        const ExtObject* o = tlsGetRef(ExtObject_serialization).get(ref.getInt());
[337]1269                        if (o)
[109]1270                        {
[337]1271                                setObject(*o);
1272                                return ret;
[109]1273                        }
1274                }
[375]1275                logPrintf("ExtValue", "deserialize", LOG_ERROR, "Invalid reference: '%s'", in - 1);
[337]1276                return NULL;
[109]1277        }
[337]1278        else if ((ret = skipWord(in)) && (ret != in))
[109]1279        {
[337]1280                SString clsname(in, (int)(ret - in));
1281                ExtValue tmp;
1282                ret = tmp.deserialize(ret);
[348]1283                ParamInterface *cls = findDeserializableClass(clsname.c_str());
[337]1284                if (cls && (tmp.getType() != TUnknown) && (tmp.getType() != TInvalid))
[109]1285                {
[337]1286                        VectorObject *vec = VectorObject::fromObject(tmp.getObject(), false);
1287                        if (vec)
[109]1288                        {
[337]1289                                int m = cls->findId("newFromVector");
1290                                if (m >= 0)
[109]1291                                {
[337]1292                                        cls->call(m, &tmp, this);
[371]1293                                        tlsGetRef(ExtObject_serialization).replace(tmp.getObject(), getObject());
[337]1294                                        return ret;
[109]1295                                }
1296                        }
[337]1297                        DictionaryObject *dic = DictionaryObject::fromObject(tmp.getObject(), false);
1298                        if (dic)
[109]1299                        {
[337]1300                                int m = cls->findId("newFromDictionary");
1301                                if (m >= 0)
[109]1302                                {
[337]1303                                        cls->call(m, &tmp, this);
[371]1304                                        tlsGetRef(ExtObject_serialization).replace(tmp.getObject(), getObject());
[337]1305                                        return ret;
[109]1306                                }
1307                        }
[337]1308                        if (tmp.getType() == TString)
[109]1309                        {
[337]1310                                int m = cls->findId("newFromString");
1311                                if (m >= 0)
[109]1312                                {
[337]1313                                        cls->call(m, &tmp, this);
[371]1314                                        tlsGetRef(ExtObject_serialization).replace(tmp.getObject(), getObject());
[337]1315                                        return ret;
[109]1316                                }
1317                        }
[371]1318                        tlsGetRef(ExtObject_serialization).remove(tmp.getObject());
[337]1319                        setEmpty();
1320                }
[109]1321                setEmpty();
[375]1322                logPrintf("ExtValue", "deserialize", LOG_WARN, "object of class \"%s\" could not be deserialized", clsname.c_str());
[337]1323                return ret;
1324        }
[375]1325        logPrintf("ExtValue", "deserialize", LOG_ERROR, "Bad syntax: '%s'", in);
[109]1326        setEmpty();
[337]1327        return NULL;
[109]1328}
1329
1330const char* ExtValue::deserialize(const char* in)
1331{
[371]1332        tlsGetRef(ExtObject_serialization).begin();
[337]1333        const char* ret = deserialize_inner(in);
[522]1334        if (ret) while (isspace(*ret)) ret++;
[371]1335        tlsGetRef(ExtObject_serialization).end();
[337]1336        return ret;
[109]1337}
1338
1339ExtObject ExtValue::getObject() const
1340{
[337]1341        if (type == TObj) return odata();
1342        return ExtObject();
[109]1343}
1344
1345ExtValue ExtValue::getExtType()
1346{
[337]1347        static const char* typenames[] = { "null", "int", "float", "string", "", "invalid" };
1348        if (getType() != TObj)
1349                return ExtValue(typenames[(int)getType()]);
1350        ExtObject& o = odata();
1351        return ExtValue(SString(o.isEmpty() ? "" : o.interfaceName()));
[109]1352}
1353
1354SString SString::valueOf(const ExtValue& v)
1355{
[337]1356        return v.getString();
[109]1357}
1358SString SString::valueOf(const ExtObject& v)
1359{
[337]1360        return v.toString();
[109]1361}
[490]1362
1363#define FIELDSTRUCT ErrorObject
[522]1364static ParamEntry errorobject_paramtab[] =
[490]1365{
[522]1366        { "Error", 1, 3, "Error", },
1367        { "message", 0, 0, "Message", "s", FIELD(message), },
1368        { "newFromString", 0, 0, "create new object", "p oError(s message)", PROCEDURE(p_newfromstring), },
1369        { "toString", 0, PARAM_READONLY | PARAM_NOSTATIC, "Textual form", "s", GETONLY(toString), },
1370        { 0, 0, 0, },
[490]1371};
1372#undef FIELDSTRUCT
1373
1374Param& ErrorObject::getParam()
1375{
1376        static Param param(errorobject_paramtab);
1377        return param;
1378}
1379
1380ExtObject ErrorObject::makeDynamicObject(ErrorObject* e)
1381{
1382        return ExtObject(&getParam(), (DestrBase*)e);
1383}
1384
[522]1385const SString ErrorObject::TO_STRING_PREFIX = "Error: ";
[490]1386
1387void ErrorObject::get_toString(ExtValue* ret)
1388{
[522]1389        ret->setString(TO_STRING_PREFIX + message);
[490]1390}
1391
1392void ErrorObject::p_newfromstring(ExtValue *args, ExtValue *ret)
1393{
[522]1394        ErrorObject *err = new ErrorObject();
1395        err->message = args[0].getString();
1396        if (err->message.startsWith(TO_STRING_PREFIX.c_str()))
1397                err->message = err->message.substr(TO_STRING_PREFIX.len());
1398        *ret = makeDynamicObject(err);
[490]1399}
1400
1401REGISTER_DESERIALIZABLE(ErrorObject)
Note: See TracBrowser for help on using the repository browser.