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

Last change on this file since 1326 was 1275, checked in by Maciej Komosinski, 16 months ago

More unification of floating point exception handling across platforms

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