3 * @brief Mbedthis Portable Runtime Universal Variable Type
9 * Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved.
10 * Copyright (c) Michael O'Brien, 1994-1995. All Rights Reserved.
12 * This software is distributed under commercial and open source licenses.
13 * You may use the GPL open source license described below or you may acquire
14 * a commercial license from Mbedthis Software. You agree to be fully bound
15 * by the terms of either license. Consult the LICENSE.TXT distributed with
16 * this software for full details.
18 * This software is open source; you can redistribute it and/or modify it
19 * under the terms of the GNU General Public License as published by the
20 * Free Software Foundation; either version 2 of the License, or (at your
21 * option) any later version. See the GNU General Public License for more
22 * details at: http://www.mbedthis.com/downloads/gplLicense.html
24 * This program is distributed WITHOUT ANY WARRANTY; without even the
25 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
27 * This GPL license does NOT permit incorporating this software into
28 * proprietary programs. If you are unable to comply with the GPL, you must
29 * acquire a commercial license to use this software. Commercial licenses
30 * for this software and support services are available from Mbedthis
31 * Software at http://www.mbedthis.com
36 /******************************* Documentation ********************************/
39 * This module is NOT multithreaded.
41 * Properties are variables that are stored in an object type variable.
42 * Properties can be primitive data types, other objects or methods.
43 * Properties are indexed by a character name.
46 /********************************** Includes **********************************/
50 /***************************** Forward Declarations ***************************/
52 static EjsProperty *allocProperty(Ejs *ep, EjsVar *op, const char *property,
53 int propertyIndex, EjsProperty *last);
54 static EjsVar *copyVar(EJS_LOC_DEC(ep, loc), EjsVar *dest,
55 const EjsVar *src, EjsCopyDepth copyDepth);
56 static EjsObj *createObj(EJS_LOC_DEC(ep, loc));
57 static char *getNextVarToken(char **next, char *tokBuf, int tokBufLen);
58 static int hash(const char *property);
59 static void unlinkProperty(EjsObj *obj, EjsPropLink *propLink);
60 static void linkPropertyBefore(EjsObj *obj, EjsPropLink *at,
61 EjsPropLink *propLink);
62 static int sortAllProperties(Ejs *ep, EjsProperty *p1,
63 EjsProperty *p2, const char *propertyName, int order);
64 static int sortByProperty(Ejs *ep, EjsProperty *p1, EjsProperty *p2,
65 const char *propertyName, int order);
66 static int dupString(MPR_LOC_DEC(ctx, loc), uchar **dest,
67 const void *src, int nbytes);
69 static void linkPropertyAfter(EjsObj *obj, EjsPropLink *at,
70 EjsPropLink *propLink);
73 static EjsProperty *hashLookup(EjsObj *obj, const char *property,
74 int *propertyIndex, EjsProperty **hashTail);
76 /******************************************************************************/
77 /********************************** Var Routines ******************************/
78 /******************************************************************************/
80 EjsType ejsGetVarType(EjsVar *vp)
87 /******************************************************************************/
89 void ejsFreeVar(Ejs *ep, EjsVar *vp)
93 ejsFree(ep, vp, EJS_SLAB_VAR);
97 /******************************************************************************/
100 * Clear the value by freeing any allocated data. This will release objects
101 * so that later garbage collection can reclaim storage if there are no other
105 void ejsZeroVar(Ejs *ep, EjsVar *vp)
107 vp->type = EJS_TYPE_UNDEFINED;
112 vp->ptr.destructor = 0;
113 vp->allocatedData = 0;
117 /******************************************************************************/
119 * Clear the value by freeing any allocated data. This will release objects
120 * so that later garbage collection can reclaim storage if there are no other
124 void ejsClearVar(Ejs *ep, EjsVar *vp)
132 if (! vp->allocatedData) {
133 vp->type = EJS_TYPE_UNDEFINED;
136 if (vp->type == EJS_TYPE_UNDEFINED) {
144 case EJS_TYPE_STRING:
149 case EJS_TYPE_OBJECT:
151 * Set the "alive" bit so that the GC will cleanup if no
154 if (vp->objectState) {
155 vp->objectState->alive = 1;
160 case EJS_TYPE_METHOD:
161 argList = vp->method.args;
163 * MOB OPT -- should be able to do just one mprFree(vp->method.args)
165 mprFree(vp->method.body);
167 for (i = 0; i < argList->length; i++) {
168 mprFree(argList->items[i]);
170 mprFree(vp->method.args);
178 if (vp->ptr.destructor) {
179 (vp->ptr.destructor)(ep, vp);
184 vp->type = EJS_TYPE_UNDEFINED;
185 vp->allocatedData = 0;
188 /******************************************************************************/
190 * Initialize an undefined value.
193 EjsVar *ejsCreateUndefinedVar(Ejs *ep)
199 vp = ejsAllocVar(EJS_LOC_ARGS(ep));
201 vp->type = EJS_TYPE_UNDEFINED;
206 /******************************************************************************/
208 * Initialize an null value.
211 EjsVar *ejsCreateNullVar(Ejs *ep)
217 vp = ejsAllocVar(EJS_LOC_ARGS(ep));
219 vp->type = EJS_TYPE_NULL;
224 /******************************************************************************/
226 EjsVar *ejsCreateBoolVar(Ejs *ep, int value)
232 vp = ejsAllocVar(EJS_LOC_ARGS(ep));
234 vp->type = EJS_TYPE_BOOL;
240 /******************************************************************************/
242 * Initialize a C method.
245 EjsVar *ejsCreateCMethodVar(Ejs *ep, EjsCMethod fn, void *userData, int flags)
251 vp = ejsAllocVar(EJS_LOC_ARGS(ep));
253 vp->type = EJS_TYPE_CMETHOD;
255 vp->cMethod.userData = userData;
261 /******************************************************************************/
263 * Initialize a C method.
266 EjsVar *ejsCreateStringCMethodVar(Ejs *ep, EjsStringCMethod fn,
267 void *userData, int flags)
274 vp = ejsAllocVar(EJS_LOC_ARGS(ep));
276 vp->type = EJS_TYPE_STRING_CMETHOD;
277 vp->cMethodWithStrings.fn = fn;
278 vp->cMethodWithStrings.userData = userData;
284 /******************************************************************************/
286 * Initialize an opaque pointer.
289 EjsVar *ejsCreatePtrVar(Ejs *ep, void *ptr, EjsDestructor destructor)
296 vp = ejsAllocVar(EJS_LOC_ARGS(ep));
298 vp->type = EJS_TYPE_PTR;
299 vp->ptr.userPtr = ptr;
300 vp->ptr.destructor = destructor;
301 vp->allocatedData = 1;
306 /******************************************************************************/
307 #if BLD_FEATURE_FLOATING_POINT
309 * Initialize a floating value.
312 EjsVar *ejsCreateFloatVar(Ejs *ep, double value)
318 vp = ejsAllocVar(EJS_LOC_ARGS(ep));
320 vp->type = EJS_TYPE_FLOAT;
321 vp->floating = value;
327 /******************************************************************************/
329 * Initialize an integer value.
332 EjsVar *ejsCreateIntegerVar(Ejs *ep, int value)
338 vp = ejsAllocVar(EJS_LOC_ARGS(ep));
340 vp->type = EJS_TYPE_INT;
346 /******************************************************************************/
347 #if BLD_FEATURE_INT64
349 * Initialize a 64-bit integer value.
352 EjsVar *ejsCreateInteger64Var(Ejs *ep, int64 value)
358 vp = ejsAllocVar(EJS_LOC_ARGS(ep));
360 vp->type = EJS_TYPE_INT64;
361 vp->integer64 = value;
366 #endif /* BLD_FEATURE_INT64 */
367 /******************************************************************************/
369 * Initialize an number variable. Type is defined by configure.
372 EjsVar *ejsCreateNumberVar(Ejs *ep, EjsNum value)
378 vp = ejsAllocVar(EJS_LOC_ARGS(ep));
382 vp->type = BLD_FEATURE_NUM_TYPE_ID;
383 #if BLD_FEATURE_NUM_TYPE_ID == EJS_TYPE_INT64
384 vp->integer64 = value;
385 #elif BLD_FEATURE_NUM_TYPE_ID == EJS_TYPE_FLOAT
394 /******************************************************************************/
396 * Initialize a (bare) JavaScript method. args and body can be null.
399 EjsVar *ejsCreateMethodVar(Ejs *ep, const char *body, MprArray *args, int flags)
406 vp = ejsAllocVar(EJS_LOC_ARGS(ep));
413 vp->type = EJS_TYPE_METHOD;
415 vp->allocatedData = 1;
417 vp->method.args = mprCreateItemArray(ep, EJS_INC_ARGS, EJS_MAX_ARGS);
418 if (vp->method.args == 0) {
419 mprAssert(vp->method.args);
425 for (i = 0; i < args->length; i++) {
426 mprAddItem(vp->method.args,
427 mprStrdup(vp->method.args, mprGetItem(args, i)));
430 vp->method.body = mprStrdup(vp->method.args, body);
432 if (vp->method.body == 0) {
441 /******************************************************************************/
443 * Initialize an object variable.
446 EjsVar *ejsCreateObjVarInternal(EJS_LOC_DEC(ep, loc))
452 vp = ejsAllocVar(EJS_LOC_PASS(ep, loc));
456 vp->type = EJS_TYPE_OBJECT;
457 vp->objectState = createObj(EJS_LOC_PASS(ep, loc));
458 if (vp->objectState == 0) {
462 vp->allocatedData = 1;
467 /******************************************************************************/
469 * Initialize a string value.
472 EjsVar *ejsCreateStringVarInternal(EJS_LOC_DEC(ep, loc), const char *value)
478 vp = ejsAllocVar(EJS_LOC_PASS(ep, loc));
482 vp->type = EJS_TYPE_STRING;
483 vp->string = mprStrdupInternal(EJS_LOC_PASS(ep, loc), value);
484 if (vp->string == 0) {
488 vp->length = strlen(vp->string);
489 vp->allocatedData = 1;
494 /******************************************************************************/
496 * Initialize a binary string value.
499 EjsVar *ejsCreateBinaryStringVar(Ejs *ep, const uchar *value, int len)
505 vp = ejsAllocVar(EJS_LOC_ARGS(ep));
507 vp->type = EJS_TYPE_STRING;
508 vp->length = dupString(MPR_LOC_ARGS(ep), &vp->ustring, value, len);
509 if (vp->length < 0) {
513 vp->allocatedData = 1;
518 /******************************************************************************/
520 void ejsSetClassName(Ejs *ep, EjsVar *vp, const char *name)
524 if (vp == 0 || !ejsVarIsObject(vp) || vp->objectState == 0) {
528 obj = vp->objectState;
530 if (obj->className) {
531 mprFree(obj->className);
533 obj->className = mprStrdup(ep, name);
536 /******************************************************************************/
538 EjsVar *ejsDupVarInternal(EJS_LOC_DEC(ep, loc), EjsVar *src,
539 EjsCopyDepth copyDepth)
543 vp = ejsAllocVar(EJS_LOC_PASS(ep, loc));
548 vp->type = EJS_TYPE_UNDEFINED;
550 return copyVar(EJS_LOC_PASS(ep, loc), vp, src, copyDepth);
553 /******************************************************************************/
555 * Set a var to a new value
558 EjsVar *ejsWriteVarInternal(EJS_LOC_DEC(ep, loc), EjsVar *dest,
559 const EjsVar *src, EjsCopyDepth copyDepth)
564 return copyVar(EJS_LOC_PASS(ep, loc), dest, src, copyDepth);
567 /******************************************************************************/
569 * Set a var using a new bool value
572 EjsVar *ejsWriteVarAsBoolean(Ejs *ep, EjsVar *dest, int value)
576 if (dest->type != EJS_TYPE_UNDEFINED) {
577 ejsClearVar(ep, dest);
580 dest->type = EJS_TYPE_BOOL;
581 dest->boolean = value;
582 dest->allocatedData = 0;
588 /******************************************************************************/
590 * Set a var using a new C Method
593 EjsVar *ejsWriteVarAsCMethod(Ejs *ep, EjsVar *dest, EjsCMethod fn,
594 void *userData, int flags)
598 if (dest->type != EJS_TYPE_UNDEFINED) {
599 ejsClearVar(ep, dest);
602 dest->type = EJS_TYPE_CMETHOD;
603 dest->cMethod.fn = fn;
604 dest->cMethod.userData = userData;
606 dest->allocatedData = 0;
611 /******************************************************************************/
612 #if BLD_FEATURE_FLOATING_POINT
614 * Set a var using a new float value
617 EjsVar *ejsWriteVarAsFloat(Ejs *ep, EjsVar *dest, double value)
621 if (dest->type != EJS_TYPE_UNDEFINED) {
622 ejsClearVar(ep, dest);
625 dest->type = EJS_TYPE_FLOAT;
626 dest->floating = value;
627 dest->allocatedData = 0;
634 /******************************************************************************/
636 * Set a var using a new integer value
639 EjsVar *ejsWriteVarAsInteger(Ejs *ep, EjsVar *dest, int value)
643 if (dest->type != EJS_TYPE_UNDEFINED) {
644 ejsClearVar(ep, dest);
647 dest->type = EJS_TYPE_INT;
648 dest->integer = value;
649 dest->allocatedData = 0;
655 /******************************************************************************/
656 #if BLD_FEATURE_INT64
658 * Set a var using a new integer value
661 EjsVar *ejsWriteVarAsInteger64(Ejs *ep, EjsVar *dest, int64 value)
665 if (dest->type != EJS_TYPE_UNDEFINED) {
666 ejsClearVar(ep, dest);
669 dest->type = EJS_TYPE_INT64;
670 dest->integer64 = value;
671 dest->allocatedData = 0;
678 /******************************************************************************/
680 * Set a var using a new Method
683 EjsVar *ejsWriteVarAsMethod(Ejs *ep, EjsVar *dest, const char *body,
686 EjsVar **srcArgs, *arg;
693 if (dest->type != EJS_TYPE_UNDEFINED) {
694 ejsClearVar(ep, dest);
697 dest->method.args = mprCreateItemArray(ep, EJS_INC_ARGS, EJS_MAX_ARGS);
698 if (dest->method.args == 0) {
702 dest->type = EJS_TYPE_METHOD;
705 srcArgs = (EjsVar**) args->items;
706 for (i = 0; i < args->length; i++) {
707 arg = ejsDupVar(ep, srcArgs[i], EJS_SHALLOW_COPY);
711 if (mprAddItem(dest->method.args, arg) < 0) {
717 dest->method.body = mprStrdup(dest->method.args, body);
718 if (dest->method.body == 0) {
722 dest->allocatedData = 1;
728 /******************************************************************************/
733 EjsVar *ejsWriteVarAsNull(Ejs *ep, EjsVar *dest)
737 if (dest->type != EJS_TYPE_UNDEFINED) {
738 ejsClearVar(ep, dest);
741 dest->type = EJS_TYPE_NULL;
742 dest->allocatedData = 0;
748 /******************************************************************************/
750 * Set a var using a new number value
753 EjsVar *ejsWriteVarAsNumber(Ejs *ep, EjsVar *dest, EjsNum value)
757 if (dest->type != EJS_TYPE_UNDEFINED) {
758 ejsClearVar(ep, dest);
761 dest->type = EJS_NUM_VAR;
762 dest->ejsNumber = value;
763 dest->allocatedData = 0;
769 /******************************************************************************/
771 * Set a var using a new C Method
774 EjsVar *ejsWriteVarAsStringCMethod(Ejs *ep, EjsVar *dest, EjsStringCMethod fn,
775 void *userData, int flags)
779 if (dest->type != EJS_TYPE_UNDEFINED) {
780 ejsClearVar(ep, dest);
783 dest->type = EJS_TYPE_CMETHOD;
784 dest->cMethodWithStrings.fn = fn;
785 dest->cMethodWithStrings.userData = userData;
787 dest->allocatedData = 0;
792 /******************************************************************************/
794 * Set a var using a new string value
797 EjsVar *ejsWriteVarAsStringInternal(EJS_LOC_DEC(ep, loc), EjsVar *dest,
803 if (dest->type != EJS_TYPE_UNDEFINED) {
804 ejsClearVar(ep, dest);
807 dest->string = mprStrdupInternal(EJS_LOC_PASS(ep, loc), value);
808 if (dest->string == 0) {
812 dest->length = strlen(dest->string);
814 dest->type = EJS_TYPE_STRING;
815 dest->allocatedData = 1;
821 /******************************************************************************/
823 * Set a var using a new string value
826 EjsVar *ejsWriteVarAsBinaryString(Ejs *ep, EjsVar *dest, const uchar *value,
832 ejsClearVar(ep, dest);
834 if (dest->type != EJS_TYPE_UNDEFINED) {
835 ejsClearVar(ep, dest);
838 dest->length = dupString(MPR_LOC_ARGS(ep), &dest->ustring, value, len);
839 if (dest->length < 0) {
843 dest->type = EJS_TYPE_STRING;
844 dest->allocatedData = 1;
850 /******************************************************************************/
852 * Set a var to undefined
855 EjsVar *ejsWriteVarAsUndefined(Ejs *ep, EjsVar *dest)
859 if (dest->type != EJS_TYPE_UNDEFINED) {
860 ejsClearVar(ep, dest);
863 dest->type = EJS_TYPE_UNDEFINED;
864 dest->allocatedData = 0;
870 /******************************************************************************/
872 * Convert a value to a text based representation of its value
873 * If you provide a format, you MUST ensure you know the type.
874 * Caller must free the result.
877 char *ejsFormatVar(Ejs *ep, const char *fmt, EjsVar *vp)
879 char *buf, *src, *value, *allocValue;
888 case EJS_TYPE_UNDEFINED:
897 if (fmt == NULL || *fmt == '\0') {
898 len = mprAllocSprintf(MPR_LOC_ARGS(ep), &buf, 0,
899 "[Opaque Pointer %p]", vp->ptr.userPtr);
901 len = mprAllocSprintf(MPR_LOC_ARGS(ep), &buf, 0, fmt, vp->ptr);
906 value = (vp->boolean) ? "true" : "false";
909 #if BLD_FEATURE_FLOATING_POINT
911 if (fmt == NULL || *fmt == '\0') {
914 len = mprAllocSprintf(MPR_LOC_ARGS(ep), &buf, 0, fmt, vp->floating);
919 if (fmt == NULL || *fmt == '\0') {
922 mprAllocSprintf(MPR_LOC_ARGS(ep), &buf, 0, fmt, vp->integer);
925 #if BLD_FEATURE_INT64
927 if (fmt == NULL || *fmt == '\0') {
930 mprAllocSprintf(MPR_LOC_ARGS(ep), &buf, 0, fmt, vp->integer64);
934 case EJS_TYPE_CMETHOD:
935 value = "[C Method]";
938 case EJS_TYPE_STRING_CMETHOD:
939 value = "[C StringMethod]";
942 case EJS_TYPE_METHOD:
943 value = ejsVarToString(ep, vp);
946 case EJS_TYPE_OBJECT:
947 value = ejsVarToString(ep, vp);
950 case EJS_TYPE_STRING:
954 if (fmt && *fmt && src) {
955 mprAllocSprintf(MPR_LOC_ARGS(ep), &buf, 0, fmt, src);
957 } else if (src == NULL) {
958 buf = mprStrdup(ep, "null");
962 if (dupString(MPR_LOC_ARGS(ep), &ubuf, src, vp->length) < 0) {
963 return mprStrdup(ep, "");
973 if (fmt == NULL || *fmt == '\0') {
974 len = mprAllocSprintf(MPR_LOC_ARGS(ep), &buf, 0, "%s", value);
976 len = mprAllocSprintf(MPR_LOC_ARGS(ep), &buf, 0, fmt, value);
986 /******************************************************************************/
988 * Convert the variable to a boolean. Only for primitive types.
991 int ejsVarToBoolean(EjsVar *vp)
996 case EJS_TYPE_UNDEFINED:
998 case EJS_TYPE_STRING_CMETHOD:
999 case EJS_TYPE_CMETHOD:
1000 case EJS_TYPE_METHOD:
1003 case EJS_TYPE_OBJECT:
1004 return (vp->objectState != NULL);
1007 return (vp->ptr.userPtr != NULL);
1012 #if BLD_FEATURE_FLOATING_POINT
1013 case EJS_TYPE_FLOAT:
1014 return (vp->floating != 0 && !ejsIsNan(vp->floating));
1018 return (vp->integer != 0);
1020 #if BLD_FEATURE_INT64
1021 case EJS_TYPE_INT64:
1022 return (vp->integer64 != 0);
1025 case EJS_TYPE_STRING:
1026 return (vp->length > 0);
1028 if (strcmp(vp->string, "true") == 0 ||
1029 strcmp(vp->string, "TRUE") == 0) {
1032 } else if (strcmp(vp->string, "false") == 0 ||
1033 strcmp(vp->string, "FALSE") == 0) {
1037 return atoi(vp->string);
1046 /******************************************************************************/
1047 #if BLD_FEATURE_FLOATING_POINT
1049 * Convert the variable to a floating point number. Only for primitive types.
1052 double ejsVarToFloat(EjsVar *vp)
1057 case EJS_TYPE_UNDEFINED:
1059 case EJS_TYPE_STRING_CMETHOD:
1060 case EJS_TYPE_CMETHOD:
1061 case EJS_TYPE_METHOD:
1062 case EJS_TYPE_OBJECT:
1067 return (vp->boolean) ? 1.0 : 0.0;
1069 case EJS_TYPE_FLOAT:
1070 return vp->floating;
1073 return (double) vp->integer;
1075 #if BLD_FEATURE_INT64
1076 case EJS_TYPE_INT64:
1077 return (double) vp->integer64;
1080 case EJS_TYPE_STRING:
1081 if (vp->length == 0) {
1084 return atof(vp->string);
1093 /******************************************************************************/
1095 * Convert the variable to an Integer type. Only works for primitive types.
1098 int ejsVarToInteger(EjsVar *vp)
1103 case EJS_TYPE_UNDEFINED:
1105 case EJS_TYPE_STRING_CMETHOD:
1106 case EJS_TYPE_CMETHOD:
1107 case EJS_TYPE_METHOD:
1108 case EJS_TYPE_OBJECT:
1112 return (vp->boolean) ? 1 : 0;
1114 #if BLD_FEATURE_FLOATING_POINT
1115 case EJS_TYPE_FLOAT:
1116 if (ejsIsNan(vp->floating)) {
1119 return (int) vp->floating;
1125 #if BLD_FEATURE_INT64
1126 case EJS_TYPE_INT64:
1127 return (int) vp->integer64;
1130 case EJS_TYPE_STRING:
1131 if (vp->length == 0) {
1134 return ejsParseInteger(vp->string);
1142 /******************************************************************************/
1143 #if BLD_FEATURE_INT64
1145 * Convert the variable to an Integer64 type. Only works for primitive types.
1148 int64 ejsVarToInteger64(EjsVar *vp)
1153 case EJS_TYPE_UNDEFINED:
1155 case EJS_TYPE_STRING_CMETHOD:
1156 case EJS_TYPE_CMETHOD:
1157 case EJS_TYPE_METHOD:
1158 case EJS_TYPE_OBJECT:
1163 return (vp->boolean) ? 1 : 0;
1165 #if BLD_FEATURE_FLOATING_POINT
1166 case EJS_TYPE_FLOAT:
1167 if (ejsIsNan(vp->floating)) {
1170 return (int64) vp->floating;
1176 case EJS_TYPE_INT64:
1177 return vp->integer64;
1179 case EJS_TYPE_STRING:
1180 if (vp->length == 0) {
1183 return ejsParseInteger64(vp->string);
1191 #endif /* BLD_FEATURE_INT64 */
1192 /******************************************************************************/
1194 * Convert the variable to a number type. Only works for primitive types.
1197 EjsNum ejsVarToNumber(EjsVar *vp)
1199 #if BLD_FEATURE_NUM_TYPE_ID == EJS_TYPE_INT64
1200 return ejsVarToInteger64(vp);
1201 #elif BLD_FEATURE_NUM_TYPE_ID == EJS_TYPE_FLOAT
1202 return ejsVarToFloat(vp);
1204 return ejsVarToInteger(vp);
1208 /******************************************************************************/
1210 * Convert a var to a string. Store the result in ep->castTemp. If allocated
1211 * set ep->castAlloc to TRUE. Caller must NOT free the result.
1214 char *ejsVarToString(Ejs *ep, EjsVar *vp)
1220 if (ep->castAlloc) {
1221 mprFree(ep->castTemp);
1227 case EJS_TYPE_UNDEFINED:
1228 ep->castTemp = "undefined";
1232 ep->castTemp = "null";
1236 len = mprAllocSprintf(MPR_LOC_ARGS(ep), &ep->castTemp, 0,
1237 "[Opaque Pointer %p]", vp->ptr.userPtr);
1243 ep->castTemp = "true";
1245 ep->castTemp = "false";
1249 #if BLD_FEATURE_FLOATING_POINT
1250 case EJS_TYPE_FLOAT:
1251 len = mprAllocSprintf(MPR_LOC_ARGS(ep), &ep->castTemp, 0,
1252 "%f", vp->floating);
1258 mprItoa(numBuf, sizeof(numBuf), vp->integer);
1259 ep->castTemp = mprStrdup(ep, numBuf);
1263 #if BLD_FEATURE_INT64
1264 case EJS_TYPE_INT64:
1265 mprAllocSprintf(MPR_LOC_ARGS(ep), &ep->castTemp, 0,
1266 "%Ld", vp->integer64);
1271 case EJS_TYPE_CMETHOD:
1272 ep->castTemp = "[C Method]";
1275 case EJS_TYPE_STRING_CMETHOD:
1276 ep->castTemp = "[C StringMethod]";
1279 case EJS_TYPE_METHOD:
1280 bp = mprCreateBuf(ep, 0, 0);
1281 mprPutStringToBuf(bp, "function (");
1282 for (i = 0; i < vp->method.args->length; i++) {
1283 mprPutStringToBuf(bp, vp->method.args->items[i]);
1284 if ((i + 1) < vp->method.args->length) {
1285 mprPutStringToBuf(bp, ", ");
1288 mprPutStringToBuf(bp, ") {");
1289 mprPutStringToBuf(bp, vp->method.body);
1290 mprPutStringToBuf(bp, "}");
1291 mprAddNullToBuf(bp);
1292 ep->castTemp = mprStealBuf(ep, bp);
1297 case EJS_TYPE_OBJECT:
1298 if (ejsRunMethod(ep, vp, "toString", 0) < 0) {
1299 return mprStrdup(ep, "[object Object]");
1301 ep->castTemp = mprStrdup(ep, ep->result->string);
1305 case EJS_TYPE_STRING:
1306 if (vp->string == 0) {
1307 ep->castTemp = "null";
1309 ep->castTemp = vp->string;
1317 mprAssert(ep->castTemp);
1318 return ep->castTemp;
1321 /******************************************************************************/
1323 char *ejsVarToStringEx(Ejs *ep, EjsVar *vp, bool *alloc)
1329 str = ejsVarToString(ep, vp);
1330 *alloc = ep->castAlloc;
1336 /******************************************************************************/
1338 * Parse a string based on formatting instructions and intelligently
1339 * create a variable.
1341 * Float format: [+|-]DIGITS][DIGITS][(e|E)[+|-]DIGITS]
1344 EjsVar *ejsParseVar(Ejs *ep, const char *buf, EjsType preferredType)
1352 type = preferredType;
1354 if (preferredType == EJS_TYPE_UNDEFINED) {
1356 if (*buf == '-' || *buf == '+') {
1359 } else if (!isdigit((int) *buf)) {
1360 if (strcmp(buf, "true") == 0 || strcmp(buf, "false") == 0) {
1361 type = EJS_TYPE_BOOL;
1363 type = EJS_TYPE_STRING;
1366 } else if (isdigit((int) *buf)) {
1369 if (*cp && tolower(cp[1]) == 'x') {
1372 for (cp = buf; *cp; cp++) {
1373 if (! isxdigit((int) *cp)) {
1378 #if BLD_FEATURE_FLOATING_POINT
1379 /* Could be integer or float */
1380 for (cp = buf; *cp; cp++) {
1381 if (! isdigit((int) *cp)) {
1382 int c = tolower(*cp);
1383 if (c == '.' || c == 'e' || c == 'f') {
1384 type = EJS_TYPE_FLOAT;
1395 case EJS_TYPE_OBJECT:
1396 case EJS_TYPE_UNDEFINED:
1403 return ejsCreateBoolVar(ep, ejsParseBoolean(buf));
1406 return ejsCreateIntegerVar(ep, ejsParseInteger(buf));
1408 #if BLD_FEATURE_INT64
1409 case EJS_TYPE_INT64:
1410 return ejsCreateInteger64Var(ep, ejsParseInteger64(buf));
1413 case EJS_TYPE_STRING:
1414 if (strcmp(buf, "null") == 0) {
1415 return ejsCreateNullVar(ep);
1417 } else if (strcmp(buf, "undefined") == 0) {
1418 return ejsCreateUndefinedVar(ep);
1421 return ejsCreateStringVar(ep, buf);
1423 #if BLD_FEATURE_FLOATING_POINT
1424 case EJS_TYPE_FLOAT:
1425 return ejsCreateFloatVar(ep, atof(buf));
1429 return ejsCreateUndefinedVar(ep);
1432 /******************************************************************************/
1434 * Convert the variable to a number type. Only works for primitive types.
1437 bool ejsParseBoolean(const char *s)
1439 if (s == 0 || *s == '\0') {
1442 if (strcmp(s, "false") == 0 || strcmp(s, "FALSE") == 0) {
1448 /******************************************************************************/
1450 * Convert the variable to a number type. Only works for primitive types.
1453 EjsNum ejsParseNumber(const char *s)
1455 #if BLD_FEATURE_NUM_TYPE_ID == EJS_TYPE_INT64
1456 return ejsParseInteger64(s);
1457 #elif BLD_FEATURE_NUM_TYPE_ID == EJS_TYPE_FLOAT
1458 return ejsParseFloat(s);
1460 return ejsParseInteger(s);
1464 /******************************************************************************/
1465 #if BLD_FEATURE_INT64
1467 * Convert the string buffer to an Integer64.
1470 int64 ejsParseInteger64(const char *str)
1474 int radix, c, negative;
1485 } else if (*cp == '+') {
1490 * Parse a number. Observe hex and octal prefixes (0x, 0)
1494 * Normal numbers (Radix 10)
1496 while (isdigit((int) *cp)) {
1497 num64 = (*cp - '0') + (num64 * 10);
1502 if (tolower(*cp) == 'x') {
1508 num64 = (c - '0') + (num64 * radix);
1509 } else if (c >= 'a' && c <= 'f') {
1510 num64 = (c - 'a' + 10) + (num64 * radix);
1521 if (isdigit(c) && c < '8') {
1522 num64 = (c - '0') + (num64 * radix);
1537 #endif /* BLD_FEATURE_INT64 */
1538 /******************************************************************************/
1540 * Convert the string buffer to an Integer.
1543 int ejsParseInteger(const char *str)
1547 int radix, c, negative;
1558 } else if (*cp == '+') {
1563 * Parse a number. Observe hex and octal prefixes (0x, 0)
1567 * Normal numbers (Radix 10)
1569 while (isdigit((int) *cp)) {
1570 num = (*cp - '0') + (num * 10);
1575 if (tolower(*cp) == 'x') {
1581 num = (c - '0') + (num * radix);
1582 } else if (c >= 'a' && c <= 'f') {
1583 num = (c - 'a' + 10) + (num * radix);
1594 if (isdigit(c) && c < '8') {
1595 num = (c - '0') + (num * radix);
1610 /******************************************************************************/
1611 #if BLD_FEATURE_FLOATING_POINT
1613 * Convert the string buffer to an Floating.
1616 double ejsParseFloat(const char *str)
1621 /******************************************************************************/
1623 int ejsIsNan(double f)
1631 return (f == FP_NAN);
1634 /******************************************************************************/
1636 int ejsIsInfinite(double f)
1644 return (f == FP_INFINITE);
1648 #endif /* BLD_FEATURE_FLOATING_POINT */
1650 /******************************************************************************/
1652 * Single point of control for all assignment to properties.
1654 * Copy an objects core value (only). This preserves the destination object's
1655 * name. This implements copy by reference for objects and copy by value for
1656 * strings and other types. Caller must free dest prior to calling.
1659 static EjsVar *copyVar(EJS_LOC_DEC(ep, loc), EjsVar *dest, const EjsVar *src,
1660 EjsCopyDepth copyDepth)
1665 const char **srcArgs;
1676 if (dest->type != EJS_TYPE_UNDEFINED) {
1677 ejsClearVar(ep, dest);
1680 dest->allocatedData = 0;
1682 switch (src->type) {
1684 case EJS_TYPE_UNDEFINED:
1689 dest->boolean = src->boolean;
1693 dest->ptr = src->ptr;
1694 if (dest->ptr.destructor) {
1695 dest->allocatedData = 1;
1699 case EJS_TYPE_STRING_CMETHOD:
1700 dest->cMethodWithStrings = src->cMethodWithStrings;
1703 case EJS_TYPE_CMETHOD:
1704 dest->cMethod = src->cMethod;
1707 #if BLD_FEATURE_FLOATING_POINT
1708 case EJS_TYPE_FLOAT:
1709 dest->floating = src->floating;
1714 dest->integer = src->integer;
1717 #if BLD_FEATURE_INT64
1718 case EJS_TYPE_INT64:
1719 dest->integer64 = src->integer64;
1723 case EJS_TYPE_OBJECT:
1724 if (copyDepth == EJS_SHALLOW_COPY) {
1727 * If doing a shallow copy and the src object is from the same
1728 * interpreter, or we are copying from the master interpreter, or
1729 * we are using a shared slab, then we can do a shallow copy.
1730 * Otherwise, we must do a deep copy.
1732 srcObj = src->objectState;
1733 if (srcObj->ejs == ep || srcObj->ejs == ep->service->master ||
1734 (ep->flags & EJS_FLAGS_SHARED_SLAB)) {
1735 dest->objectState = src->objectState;
1736 dest->allocatedData = 1;
1742 * Doing a deep or recursive deep. Can get here if doing a shallow
1743 * copy and the object is from another non-master interpeter and not
1744 * using a shared slab.
1746 * We must make sure the data is allocated using the right memory
1747 * context. It must be the same as the destination parent object.
1748 * Otherwise, when we free the property memory, the parent may
1749 * have a dangling pointer.
1751 if (dest->isProperty) {
1752 destp = ejsGetPropertyPtr(dest);
1753 if (destp->parentObj == 0) {
1757 mprAssert(destp->parentObj);
1758 ejsContext = destp->parentObj->ejs;
1759 mprAssert(ejsContext);
1766 dest->objectState = createObj(EJS_LOC_PASS(ejsContext, loc));
1767 if (dest->objectState == 0) {
1772 dest->objectState->baseClass = src->objectState->baseClass;
1773 dest->objectState->methods = src->objectState->methods;
1774 dest->objectState->noConstructor = src->objectState->noConstructor;
1775 dest->objectState->objName =
1776 mprStrdup(ejsContext, src->objectState->objName);
1778 if (dest->objectState->objName == 0) {
1782 if (ejsCopyProperties(ep, dest, src, copyDepth) == 0) {
1785 dest->allocatedData = 1;
1788 case EJS_TYPE_METHOD:
1789 dest->method.args = mprCreateItemArray(ep, EJS_INC_ARGS,
1791 if (dest->method.args == 0) {
1794 dest->allocatedData = 1;
1795 if (src->method.args) {
1796 srcArgs = (const char**) src->method.args->items;
1797 for (i = 0; i < src->method.args->length; i++) {
1798 str = mprStrdupInternal(EJS_LOC_PASS(dest->method.args,
1801 mprFree(dest->method.args);
1802 dest->method.args = 0;
1805 if (mprAddItem(dest->method.args, str) < 0) {
1806 mprFree(dest->method.args);
1807 dest->method.args = 0;
1812 dest->method.body = mprStrdup(dest->method.args, src->method.body);
1813 if (dest->method.body == 0) {
1814 mprFree(dest->method.args);
1815 dest->method.args = 0;
1818 dest->callsSuper = src->callsSuper;
1821 case EJS_TYPE_STRING:
1822 dest->length = src->length;
1824 /* Shallow, deep or recursive deep */
1825 dest->length = dupString(MPR_LOC_PASS(ep, loc), &dest->ustring,
1826 src->ustring, src->length);
1827 if (dest->length < 0) {
1830 dest->allocatedData = 1;
1833 dest->string = src->string;
1834 dest->allocatedData = 0;
1839 dest->type = src->type;
1840 dest->flags = src->flags;
1841 dest->isArray = src->isArray;
1846 /******************************************************************************/
1848 * Copy all properies in an object. Must preserve property order
1851 EjsVar *ejsCopyProperties(Ejs *ep, EjsVar *dest, const EjsVar *src,
1852 EjsCopyDepth copyDepth)
1854 EjsProperty *srcProp, *destProp, *last, *next;
1857 srcProp = ejsGetFirstProperty(src, EJS_ENUM_ALL);
1859 next = ejsGetNextProperty(srcProp, EJS_ENUM_ALL);
1860 if (srcProp->visited) {
1866 * This finds the last variable in the hash chain
1867 * FUTURE OPT. This is slow. If used double link, we could locate the
1870 destProp = hashLookup(dest->objectState, srcProp->name,
1871 &propertyIndex, &last);
1872 mprAssert(destProp == 0);
1874 destProp = allocProperty(ep, dest, srcProp->name, propertyIndex, last);
1875 if (destProp == 0) {
1876 mprAssert(destProp);
1881 * Recursively copy the object. If DEEP_COPY, then we
1882 * will do a shallow copy of the object contents. If
1883 * RECURSIVE_DEEP, then we do a deep copy at all levels.
1885 srcProp->visited = 1;
1887 if (copyVar(EJS_LOC_ARGS(ep), ejsGetVarPtr(destProp),
1888 ejsGetVarPtr(srcProp),
1889 (copyDepth == EJS_DEEP_COPY) ? EJS_SHALLOW_COPY : copyDepth)
1893 srcProp->visited = 0;
1900 /******************************************************************************/
1901 /********************************** Properties ********************************/
1902 /******************************************************************************/
1904 * Create a property in an object and return a pointer to it. If the property
1905 * already exists then just return a pointer to it (no error).
1906 * To test for existance of a property, use GetProperty
1909 static EjsProperty *hashLookup(EjsObj *obj, const char *property,
1910 int *propertyIndex, EjsProperty **hashTail)
1912 EjsProperty *prop, *last;
1916 mprAssert(property);
1918 if (obj == 0 || property == 0 || *property == '\0') {
1924 * Find the property in the hash chain if it exists
1926 index = hash(property);
1927 prop = obj->propertyHash[index];
1928 for (last = 0; prop != 0; last = prop, prop = prop->hashNext) {
1929 if (prop->name[0] == property[0] &&
1930 strcmp(prop->name, property) == 0) {
1934 if (propertyIndex) {
1935 *propertyIndex = index;
1944 /******************************************************************************/
1946 * Create a property in an object and return a pointer to it. If the property
1947 * already exists then just return a pointer to it (no error). If the property
1948 * does not exist, create an undefined variable. To test for existance of a
1949 * property, use GetProperty.
1952 EjsProperty *ejsCreateSimpleProperty(Ejs *ep, EjsVar *op, const char *property)
1954 EjsProperty *prop, *last;
1957 if (op == 0 || op->type != EJS_TYPE_OBJECT || property == 0 ||
1958 *property == '\0') {
1964 * Find the property in the hash chain if it exists
1966 prop = hashLookup(op->objectState, property, &propertyIndex, &last);
1970 * Create a new property
1972 prop = allocProperty(ep, op, property, propertyIndex, last);
1974 mprAssert(prop == 0);
1981 /******************************************************************************/
1983 * Create a property in an object and return a pointer to it. If the property
1984 * already exists then just return a pointer to it (no error).
1985 * To test for existance of a property, use GetProperty
1988 EjsProperty *ejsCreateSimpleNonUniqueProperty(Ejs *ep, EjsVar *op,
1989 const char *property)
1991 EjsProperty *prop, *last;
1994 if (op == 0 || op->type != EJS_TYPE_OBJECT || property == 0 ||
1995 *property == '\0') {
2003 propertyIndex = hash(property);
2004 prop = op->objectState->propertyHash[propertyIndex];
2005 for (last = 0; prop != 0; last = prop, prop = prop->hashNext) {
2009 return allocProperty(ep, op, property, propertyIndex, last);
2012 /******************************************************************************/
2014 * Find a property in an object and return a pointer to it.
2015 * This does NOT traverse base classes.
2018 EjsProperty *ejsGetSimpleProperty(Ejs *ep, EjsVar *op, const char *property)
2021 mprAssert(op->type == EJS_TYPE_OBJECT);
2022 mprAssert(property && *property);
2025 * This is an internal API. It has very little checking.
2027 return hashLookup(op->objectState, property, 0, 0);
2030 /******************************************************************************/
2033 * NOTE: There is no ejsSetSimpleProperty as all the ejsSetProperty routines
2034 * operate only on the instance and don't follow base classes. ie. there is
2035 * no simple version required. However, there is a ejsSetBaseProperty routine
2036 * that will follow base classes and is used to set static properties in base
2040 /******************************************************************************/
2041 /******************************* Property Access ******************************/
2042 /******************************************************************************/
2044 * The property get routines follow base classes and utilize the propery
2045 * method access routines. The property set routines do not follow base
2046 * classes. The property ejsSetBase... routines do follow base classes.
2050 * Find a property in an object and return a pointer to it.
2051 * This follows base classes.
2054 EjsProperty *ejsGetProperty(Ejs *ep, EjsVar *op, const char *property)
2057 int maxBaseClasses = 50;
2060 if (op->type != EJS_TYPE_OBJECT) {
2061 mprAssert(op->type == EJS_TYPE_OBJECT);
2064 mprAssert(op->objectState);
2066 vp = ejsGetPropertyMethod(ep, op, property);
2074 newOp = op->objectState->baseClass;
2076 if (op->objectState != ep->objectClass->objectState) {
2077 newOp = ep->objectClass;
2083 * A little bit of sanity checking
2085 if (--maxBaseClasses <= 0) {
2086 mprAssert(maxBaseClasses > 0);
2092 return ejsGetPropertyPtr(vp);
2095 /******************************************************************************/
2097 * Get the property's variable. Optionally create if it does not exist.
2100 EjsVar *ejsGetPropertyAsVar(Ejs *ep, EjsVar *vp, const char *property)
2102 return ejsGetVarPtr(ejsGetProperty(ep, vp, property));
2105 /******************************************************************************/
2107 * Get the property's value as a binary string.
2110 const uchar *ejsGetPropertyAsBinaryString(Ejs *ep, EjsVar *obj,
2111 const char *property, int *length)
2115 vp = ejsGetVarPtr(ejsGetProperty(ep, obj, property));
2116 if (vp == 0 || ejsVarIsUndefined(vp)) {
2120 if (vp->type == EJS_TYPE_STRING) {
2122 *length = vp->length;
2129 /******************************************************************************/
2131 * Get the property's value as a string.
2134 const char *ejsGetPropertyAsString(Ejs *ep, EjsVar *obj, const char *property)
2138 vp = ejsGetVarPtr(ejsGetProperty(ep, obj, property));
2139 if (vp == 0 || ejsVarIsUndefined(vp)) {
2143 if (vp->type == EJS_TYPE_STRING) {
2149 /******************************************************************************/
2151 * Get the property's value as a number.
2154 BLD_FEATURE_NUM_TYPE ejsGetPropertyAsNumber(Ejs *ep, EjsVar *obj,
2155 const char *property)
2159 vp = ejsGetVarPtr(ejsGetProperty(ep, obj, property));
2160 if (vp == 0 || ejsVarIsUndefined(vp)) {
2164 return ejsVarToNumber(vp);
2167 /******************************************************************************/
2169 * Get the property's value as a integer.
2172 int ejsGetPropertyAsInteger(Ejs *ep, EjsVar *obj, const char *property)
2176 vp = ejsGetVarPtr(ejsGetProperty(ep, obj, property));
2177 if (vp == 0 || ejsVarIsUndefined(vp)) {
2181 return ejsVarToInteger(vp);
2184 /******************************************************************************/
2186 * Get the property's value as a boolean.
2189 bool ejsGetPropertyAsBoolean(Ejs *ep, EjsVar *obj, const char *property)
2193 vp = ejsGetVarPtr(ejsGetProperty(ep, obj, property));
2194 if (vp == 0 || ejsVarIsUndefined(vp)) {
2198 return ejsVarToBoolean(vp);
2201 /******************************************************************************/
2203 * Get the property's value as a pointer.
2206 void *ejsGetPropertyAsPtr(Ejs *ep, EjsVar *obj, const char *property)
2210 vp = ejsGetVarPtr(ejsGetProperty(ep, obj, property));
2211 if (vp == 0 || ejsVarIsUndefined(vp)) {
2214 if (vp->type == EJS_TYPE_PTR) {
2215 return vp->ptr.userPtr;
2220 /******************************************************************************/
2222 * Create a property in the object. This will override any base class
2225 * MOB -- need to spell out the difference between ejsSetProperty and
2226 * ejsCreateProperty.
2229 EjsProperty *ejsCreateProperty(Ejs *ep, EjsVar *obj, const char *property)
2233 vp = ejsCreatePropertyMethod(ep, obj, property);
2234 return ejsGetPropertyPtr(vp);
2237 /******************************************************************************/
2239 * Set a property's variable value. Create the property if it does not exist.
2240 * This routine DOES follow base classes.
2243 EjsProperty *ejsSetBaseProperty(Ejs *ep, EjsVar *op, const char *property,
2244 const EjsVar *value)
2247 int maxBaseClasses = 50;
2250 if (op->type != EJS_TYPE_OBJECT) {
2251 mprAssert(op->type == EJS_TYPE_OBJECT);
2254 mprAssert(op->objectState);
2256 vp = ejsGetPropertyMethod(ep, op, property);
2261 vp = ejsSetPropertyMethod(ep, op, property, value);
2265 newOp = op->objectState->baseClass;
2267 if (op->objectState != ep->objectClass->objectState) {
2268 newOp = ep->objectClass;
2274 * A little bit of sanity checking
2276 if (--maxBaseClasses <= 0) {
2277 mprAssert(maxBaseClasses > 0);
2283 return ejsGetPropertyPtr(vp);
2286 /******************************************************************************/
2288 * Set a property's variable value. Create the property if it does not exist.
2289 * This does NOT follow base classes. Okay when updating instance properties,
2290 * but not for class (static) properties. This does a shallow copy which
2291 * will copy references.
2294 EjsProperty *ejsSetProperty(Ejs *ep, EjsVar *obj, const char *property,
2295 const EjsVar *value)
2299 vp = ejsSetPropertyMethod(ep, obj, property, value);
2301 return ejsGetPropertyPtr(vp);
2304 /******************************************************************************/
2306 * Set a property's variable value by assigning the given value. The caller
2307 * must NOT free value as it is assigned directly into the property's value.
2310 EjsProperty *ejsSetPropertyAndFree(Ejs *ep, EjsVar *obj,
2311 const char *property, EjsVar *value)
2315 vp = ejsSetPropertyMethod(ep, obj, property, value);
2317 ejsFree(ep, value, EJS_SLAB_VAR);
2319 return ejsGetPropertyPtr(vp);
2322 /******************************************************************************/
2324 EjsProperty *ejsSetPropertyToCMethod(Ejs *ep, EjsVar *vp, const char *prop,
2325 EjsCMethod fn, void *userData, int flags)
2329 ejsInitVar(&v, EJS_TYPE_CMETHOD);
2331 v.cMethod.userData = userData;
2334 return ejsSetProperty(ep, vp, prop, &v);
2337 /******************************************************************************/
2339 EjsProperty *ejsSetPropertyToBoolean(Ejs *ep, EjsVar *vp, const char *prop,
2344 ejsInitVar(&v, EJS_TYPE_BOOL);
2347 return ejsSetProperty(ep, vp, prop, &v);
2350 /******************************************************************************/
2351 #if BLD_FEATURE_FLOATING_POINT
2353 EjsProperty *ejsSetPropertyToFloat(Ejs *ep, EjsVar *vp, const char *prop,
2358 ejsInitVar(&v, EJS_TYPE_FLOAT);
2361 return ejsSetProperty(ep, vp, prop, &v);
2365 /******************************************************************************/
2367 EjsProperty *ejsSetPropertyToInteger(Ejs *ep, EjsVar *vp, const char *prop,
2372 ejsInitVar(&v, EJS_TYPE_INT);
2375 return ejsSetProperty(ep, vp, prop, &v);
2378 /******************************************************************************/
2379 #if BLD_FEATURE_INT64
2381 EjsProperty *ejsSetPropertyToInteger64(Ejs *ep, EjsVar *vp, const char *prop,
2386 ejsInitVar(&v, EJS_TYPE_INT64);
2387 v.integer64 = value;
2389 return ejsSetProperty(ep, vp, prop, &v);
2393 /******************************************************************************/
2395 EjsProperty *ejsSetPropertyToNull(Ejs *ep, EjsVar *vp, const char *prop)
2399 ejsInitVar(&v, EJS_TYPE_NULL);
2401 return ejsSetProperty(ep, vp, prop, &v);
2404 /******************************************************************************/
2406 EjsProperty *ejsSetPropertyToMethod(Ejs *ep, EjsVar *vp, const char *prop,
2407 const char *body, MprArray *args, int flags)
2409 return ejsSetPropertyAndFree(ep, vp, prop,
2410 ejsCreateMethodVar(ep, body, args, flags));
2413 /******************************************************************************/
2415 EjsProperty *ejsSetPropertyToNumber(Ejs *ep, EjsVar *vp, const char *prop,
2418 return ejsSetPropertyAndFree(ep, vp, prop, ejsCreateNumberVar(ep, value));
2421 /******************************************************************************/
2423 EjsProperty *ejsSetPropertyToStringCMethod(Ejs *ep, EjsVar *vp,
2424 const char *prop, EjsStringCMethod fn, void *userData, int flags)
2428 ejsInitVar(&v, EJS_TYPE_STRING_CMETHOD);
2429 v.cMethodWithStrings.fn = fn;
2430 v.cMethodWithStrings.userData = userData;
2433 return ejsSetProperty(ep, vp, prop, &v);
2436 /******************************************************************************/
2438 EjsProperty *ejsSetPropertyToString(Ejs *ep, EjsVar *vp, const char *prop,
2444 ejsInitVar(&v, EJS_TYPE_STRING);
2447 v.string = mprStrdupInternal(EJS_LOC_ARGS(ep), value);
2448 if (v.string == 0) {
2451 v.length = strlen(v.string);
2452 v.allocatedData = 1;
2454 pp = ejsSetProperty(ep, vp, prop, &v);
2461 /******************************************************************************/
2463 EjsProperty *ejsSetPropertyToBinaryString(Ejs *ep, EjsVar *vp,
2464 const char *prop, const uchar *value, int len)
2469 ejsInitVar(&v, EJS_TYPE_STRING);
2472 v.length = dupString(MPR_LOC_ARGS(ep), &v.ustring, value, len);
2476 v.allocatedData = 1;
2478 pp = ejsSetProperty(ep, vp, prop, &v);
2485 /******************************************************************************/
2487 EjsProperty *ejsSetPropertyToUndefined(Ejs *ep, EjsVar *vp, const char *prop)
2491 ejsInitVar(&v, EJS_TYPE_UNDEFINED);
2493 return ejsSetProperty(ep, vp, prop, &v);
2496 /******************************************************************************/
2498 EjsProperty *ejsSetPropertyToPtr(Ejs *ep, EjsVar *vp, const char *prop,
2499 void *ptr, EjsDestructor destructor)
2503 ejsInitVar(&v, EJS_TYPE_PTR);
2504 v.ptr.userPtr = ptr;
2505 v.ptr.destructor = destructor;
2506 v.allocatedData = 1;
2508 return ejsSetProperty(ep, vp, prop, &v);
2511 /******************************************************************************/
2513 EjsProperty *ejsSetPropertyToNewObj(Ejs *ep, EjsVar *vp, const char *prop,
2514 const char *className, MprArray *args)
2516 return ejsSetPropertyAndFree(ep, vp, prop,
2517 ejsCreateObjUsingArgv(ep, 0, className, args));
2520 /******************************************************************************/
2522 EjsProperty *ejsSetPropertyToObj(Ejs *ep, EjsVar *op, const char *prop)
2524 return ejsSetPropertyAndFree(ep, op, prop, ejsCreateObjVar(ep));
2527 /******************************************************************************/
2529 * Convenience routines
2532 EjsVar *ejsSetPropertyToObjAsVar(Ejs *ep, EjsVar *op, const char *prop)
2534 return ejsGetVarPtr(ejsSetPropertyToObj(ep, op, prop));
2537 /******************************************************************************/
2538 /******************************************************************************/
2539 /******************************************************************************/
2540 /******************************************************************************/
2542 * Create a script method
2545 EjsProperty *ejsDefineMethod(Ejs *ep, EjsVar *vp, const char *prop,
2546 const char *body, MprArray *args)
2549 vp = ejsGetGlobalObj(ep);
2551 return ejsSetPropertyToMethod(ep, vp, prop, body, args, 0);
2554 /******************************************************************************/
2556 * Create a C language method
2559 EjsProperty *ejsDefineCMethod(Ejs *ep, EjsVar *vp, const char *prop,
2560 EjsCMethod fn, int flags)
2563 vp = ejsGetGlobalObj(ep);
2565 return ejsSetPropertyToCMethod(ep, vp, prop, fn, 0, flags);
2568 /******************************************************************************/
2573 EjsProperty *ejsDefineAccessors(Ejs *ep, EjsVar *vp, const char *prop,
2574 const char *getBody, const char *setBody)
2581 vp = ejsGetGlobalObj(ep);
2584 if (ejsSetPropertyToMethod(ep, vp, prop, getBody, 0, EJS_GET_ACCESSOR) < 0){
2589 /* MOB -- OPT to use SLAB */
2590 /* MOB -- need to encapsulate this logic */
2592 if (mprAllocStrcat(MPR_LOC_ARGS(ep), &propName, EJS_MAX_ID+5, 0,
2593 "-set-", prop, NULL) < 0) {
2598 args = mprCreateItemArray(ep, EJS_INC_ARGS, EJS_MAX_ARGS);
2599 mprAddItem(args, mprStrdup(args, "value"));
2601 pp = ejsSetPropertyToMethod(ep, vp, propName, setBody, args,
2613 /******************************************************************************/
2615 * Define C accessors
2618 EjsProperty *ejsDefineCAccessors(Ejs *ep, EjsVar *vp, const char *prop,
2619 EjsCMethod getFn, EjsCMethod setFn, int flags)
2625 vp = ejsGetGlobalObj(ep);
2627 pp = ejsSetPropertyToCMethod(ep, vp, prop, getFn, 0,
2628 flags | EJS_GET_ACCESSOR);
2634 /* MOB -- OPT to use SLAB */
2635 if (mprAllocStrcat(MPR_LOC_ARGS(ep), &propName, EJS_MAX_ID + 5, 0,
2636 "-set-", prop, NULL) < 0) {
2640 pp = ejsSetPropertyToCMethod(ep, vp, propName, setFn, 0,
2641 flags | EJS_SET_ACCESSOR);
2651 /******************************************************************************/
2653 * Create a C language method with string arguments
2656 EjsProperty *ejsDefineStringCMethod(Ejs *ep, EjsVar *vp, const char *prop,
2657 EjsStringCMethod fn, int flags)
2660 vp = ejsGetGlobalObj(ep);
2662 return ejsSetPropertyToStringCMethod(ep, vp, prop, fn, 0, flags);
2665 /******************************************************************************/
2667 void ejsSetCMethodUserData(EjsVar *obj, void *userData)
2670 * This is a little dirty. We rely on the userData being in the same
2671 * place in the var structure.
2673 obj->cMethod.userData = userData;
2676 /******************************************************************************/
2678 void ejsSetVarFlags(EjsVar *obj, int flags)
2683 /******************************************************************************/
2685 void *ejsGetCMethodUserData(EjsVar *obj)
2687 return obj->cMethod.userData;
2690 /******************************************************************************/
2692 int ejsGetVarFlags(EjsVar *obj)
2697 /******************************************************************************/
2699 void ejsSetObjDestructor(Ejs *ep, EjsVar *obj, EjsDestructor destructor)
2701 obj->objectState->destructor = destructor;
2704 /******************************************************************************/
2706 void ejsClearObjDestructor(Ejs *ep, EjsVar *obj)
2708 obj->objectState->destructor = 0;
2711 /******************************************************************************/
2713 * Create a new property
2716 static EjsProperty *allocProperty(Ejs *ep, EjsVar *op, const char *property,
2717 int propertyIndex, EjsProperty *last)
2722 obj = op->objectState;
2725 * Allocate the property using the memory context of the owning object
2727 prop = ejsAllocProperty(EJS_LOC_ARGS(obj->ejs));
2731 if (mprStrcpy(prop->name, sizeof(prop->name), property) < 0) {
2732 ejsError(ep, EJS_REFERENCE_ERROR,
2733 "Property name %s is too long. Max is %d letters.",
2734 prop->name, EJS_MAX_ID);
2738 ejsSetVarName(ep, ejsGetVarPtr(prop), &prop->name[0]);
2744 last->hashNext = prop;
2746 obj->propertyHash[propertyIndex] = prop;
2750 prop->link.propertyName = prop->name;
2751 prop->link.property = prop;
2752 prop->link.head = &obj->link;
2756 * Inserting before the dummy head will append to the end
2758 linkPropertyBefore(obj, &obj->link, &prop->link);
2760 obj->numProperties++;
2761 prop->parentObj = obj;
2762 mprAssert(obj->ejs);
2767 /******************************************************************************/
2769 * Delete a property from this object
2772 int ejsDeleteProperty(Ejs *ep, EjsVar *vp, const char *property)
2774 EjsProperty *prop, *last;
2779 mprAssert(property && *property);
2780 mprAssert(vp->type == EJS_TYPE_OBJECT);
2782 if (vp->type != EJS_TYPE_OBJECT) {
2783 mprAssert(vp->type == EJS_TYPE_OBJECT);
2784 return MPR_ERR_BAD_ARGS;
2787 prop = hashLookup(vp->objectState, property, &propertyIndex, &last);
2788 if (prop == (EjsProperty*) 0) {
2789 return MPR_ERR_NOT_FOUND;
2791 obj = vp->objectState;
2794 if (prop->readonly) {
2795 mprAssert(! prop->readonly);
2796 return MPR_ERR_READ_ONLY;
2801 * If doing enumerations, then the object will mark preventDelete to
2802 * prevent any properties being deleted and thus disturbing the
2805 if (obj->preventDeleteProp) {
2806 obj->delayedDeleteProp = 1;
2807 prop->delayedDelete = 1;
2815 last->hashNext = prop->hashNext;
2817 obj->propertyHash[propertyIndex] = prop->hashNext;
2820 unlinkProperty(obj, &prop->link);
2821 obj->numProperties--;
2824 * Free any property data and return to the slab
2826 if (prop->var.type != EJS_TYPE_OBJECT) {
2827 ejsClearVar(ep, ejsGetVarPtr(prop));
2829 ejsFree(ep, prop, EJS_SLAB_PROPERTY);
2834 /******************************************************************************/
2836 * Remove a property's value from this object. The property is set to
2840 EjsVar *ejsClearProperty(Ejs *ep, EjsVar *vp, const char *property)
2845 mprAssert(property && *property);
2846 mprAssert(vp->type == EJS_TYPE_OBJECT);
2848 if (vp->type != EJS_TYPE_OBJECT) {
2849 mprAssert(vp->type == EJS_TYPE_OBJECT);
2853 prop = hashLookup(vp->objectState, property, 0, 0);
2854 if (prop == (EjsProperty*) 0) {
2858 if (prop->readonly) {
2859 mprAssert(! prop->readonly);
2864 ejsClearVar(ep, &prop->var);
2868 /******************************************************************************/
2870 * Unlink a property from the ordered list of properties
2873 static void unlinkProperty(EjsObj *obj, EjsPropLink *propLink)
2875 propLink->prev->next = propLink->next;
2876 propLink->next->prev = propLink->prev;
2879 /******************************************************************************/
2882 * Insert a link after a specified link.
2885 static void linkPropertyAfter(EjsObj *obj, EjsPropLink *at,
2886 EjsPropLink *propLink)
2888 propLink->next = at->next;
2889 propLink->prev = at;
2891 at->next->prev = propLink;
2892 at->next = propLink;
2896 /******************************************************************************/
2898 * Insert a link before a specified link.
2901 static void linkPropertyBefore(EjsObj *obj, EjsPropLink *at,
2902 EjsPropLink *propLink)
2904 propLink->prev = at->prev;
2905 propLink->next = at;
2907 at->prev->next = propLink;
2908 at->prev = propLink;
2911 /******************************************************************************/
2913 * This routine will sort properties in an object. If propertyName is not
2914 * null, then the properties in op must be objects with a property of the
2915 * name propertyName. If propertyName is null, then the properties of op
2916 * are directly sorted. If order is 1, they are sorted in ascending order.
2917 * If -1, they are sorted in descending order.
2919 * NOTE: arrays keep their original index values.
2922 void ejsSortProperties(Ejs *ep, EjsVar *op, EjsSortFn fn,
2923 const char *propertyName, int order)
2925 EjsProperty *p1, *p2, *tmp;
2926 EjsPropLink *l1, *l2, *oldL1Spot;
2929 obj = op->objectState;
2931 p1 = ejsGetFirstProperty(op, 0);
2933 if (p1->dontEnumerate) {
2934 p1 = ejsGetNextProperty(p1, 0);
2938 p2 = ejsGetFirstProperty(op, 0);
2939 while (p2 && p2 != p1) {
2941 if (p2->dontEnumerate) {
2942 p2 = ejsGetNextProperty(p2, 0);
2948 fn = sortByProperty;
2950 fn = sortAllProperties;
2954 if (fn(ep, p1, p2, propertyName, order) < 0) {
2960 * Swap the properties without disturbing the hash chains.
2961 * l1 is always after l2 in the list. Unlink l1 and remember
2964 oldL1Spot = l1->next;
2965 unlinkProperty(obj, l1);
2968 * Manually reinsert l1 by replacing l2 with l1. l2 is out of
2971 l2->prev->next = l1;
2972 l2->next->prev = l1;
2973 l1->prev = l2->prev;
2974 l1->next = l2->next;
2977 * Reinsert l2 before the spot where l1 was.
2979 linkPropertyBefore(obj, oldL1Spot, l2);
2982 * Swap the pointers so we continue to traverse correctly
2988 p2 = ejsGetNextProperty(p2, 0);
2990 p1 = ejsGetNextProperty(p1, 0);
2994 /******************************************************************************/
2996 * Sort properties. Strings are sorted in ascending ASCII collating sequence
2997 * Numbers are sorted in increasing numerical order.
2999 static int sortAllProperties(Ejs *ep, EjsProperty *p1, EjsProperty *p2,
3000 const char *propertyName, int order)
3006 v1 = ejsGetVarPtr(p1);
3007 v2 = ejsGetVarPtr(p2);
3009 if (v1->type == v2->type) {
3010 /* MOB -- should support Numbers */
3011 if (v1->type == EJS_TYPE_INT) {
3012 if (v1->integer < v2->integer) {
3015 } else if (v1->integer == v2->integer) {
3020 #if BLD_FEATURE_FLOATING_POINT
3021 } else if (v1->type == EJS_TYPE_FLOAT) {
3022 if (v1->floating < v2->floating) {
3025 } else if (v1->floating == v2->floating) {
3031 } else if (v1->type == EJS_TYPE_STRING) {
3032 /* MOB -- need binary support ? */
3033 return strcmp(v1->string, v2->string) * order;
3037 buf1 = ejsVarToStringEx(ep, v1, &buf1Alloc);
3038 buf2 = ejsVarToString(ep, v2);
3040 rc = strcmp(buf1, buf2);
3050 /* Type mismatch in array */
3056 /******************************************************************************/
3058 * Sort an object by a given property.
3060 static int sortByProperty(Ejs *ep, EjsProperty *p1, EjsProperty *p2,
3061 const char *propertyName, int order)
3063 EjsVar *o1, *o2, *v1, *v2;
3067 o1 = ejsGetVarPtr(p1);
3068 o2 = ejsGetVarPtr(p2);
3070 if (!ejsVarIsObject(o1) || !ejsVarIsObject(o2)) {
3071 mprAssert(ejsVarIsObject(o1));
3072 mprAssert(ejsVarIsObject(o2));
3076 v1 = ejsGetPropertyAsVar(ep, o1, propertyName);
3077 v2 = ejsGetPropertyAsVar(ep, o2, propertyName);
3079 if (v1 == 0 || v2 == 0) {
3080 /* Property name not found */
3084 if (v1->type != v2->type) {
3085 mprAssert(v1->type == v2->type);
3089 if (v1->type == v2->type) {
3090 /* MOB -- should support Numbers */
3091 if (v1->type == EJS_TYPE_INT) {
3092 if (v1->integer < v2->integer) {
3095 } else if (v1->integer == v2->integer) {
3100 #if BLD_FEATURE_FLOATING_POINT
3101 } else if (v1->type == EJS_TYPE_FLOAT) {
3102 if (v1->floating < v2->floating) {
3105 } else if (v1->floating == v2->floating) {
3111 } else if (v1->type == EJS_TYPE_STRING) {
3112 /* MOB -- need binary support ? */
3113 return strcmp(v1->string, v2->string) * order;
3116 buf1 = ejsVarToStringEx(ep, v1, &buf1Alloc);
3118 buf2 = ejsVarToString(ep, v2);
3120 rc = strcmp(buf1, buf2);
3130 /* Type mismatch in array */
3136 /******************************************************************************/
3138 * Set a property's name
3141 void ejsSetPropertyName(EjsProperty *pp, const char *property)
3143 mprStrcpy(pp->name, sizeof(pp->name), property);
3146 /******************************************************************************/
3148 int ejsMakePropertyEnumerable(EjsProperty *prop, int enumerate)
3152 oldValue = prop->dontEnumerate;
3153 prop->dontEnumerate = !enumerate;
3157 /******************************************************************************/
3159 void ejsMakePropertyPrivate(EjsProperty *prop, int isPrivate)
3161 prop->isPrivate = isPrivate;
3164 /******************************************************************************/
3166 * Make a variable read only. Can still be deleted.
3169 void ejsMakePropertyReadOnly(EjsProperty *prop, int readonly)
3171 prop->readonly = readonly;
3174 /******************************************************************************/
3176 int ejsMakeObjPermanent(EjsVar *vp, int permanent)
3180 if (vp && vp->type == EJS_TYPE_OBJECT) {
3181 oldValue = vp->objectState->permanent;
3182 vp->objectState->permanent = permanent;
3189 /******************************************************************************/
3191 int ejsMakeObjLive(EjsVar *vp, bool alive)
3196 if (vp && vp->type == EJS_TYPE_OBJECT) {
3197 oldValue = vp->objectState->alive;
3198 vp->objectState->alive = alive;
3205 /******************************************************************************/
3207 void ejsMakeClassNoConstructor(EjsVar *vp)
3209 mprAssert(vp->type == EJS_TYPE_OBJECT);
3211 if (vp->type == EJS_TYPE_OBJECT) {
3212 vp->objectState->noConstructor = 1;
3216 /******************************************************************************/
3218 * Get the count of properties.
3221 int ejsGetPropertyCount(EjsVar *vp)
3224 EjsPropLink *lp, *head;
3229 if (vp->type != EJS_TYPE_OBJECT) {
3235 head = &vp->objectState->link;
3236 for (lp = head->next; lp != head; lp = lp->next) {
3237 pp = ejsGetPropertyFromLink(lp);
3238 if (! pp->dontEnumerate) {
3245 /******************************************************************************/
3247 * Get the first property in an object. Used for walking all properties in an
3248 * object. This will only enumerate properties in this class and not in base
3252 EjsProperty *ejsGetFirstProperty(const EjsVar *op, int flags)
3256 EjsPropLink *head, *lp;
3259 mprAssert(op->type == EJS_TYPE_OBJECT);
3261 if (op->type != EJS_TYPE_OBJECT) {
3262 mprAssert(op->type == EJS_TYPE_OBJECT);
3268 obj = op->objectState;
3273 while (lp != head) {
3274 pp = ejsGetPropertyFromLink(lp);
3275 if (! pp->dontEnumerate || (flags & EJS_ENUM_HIDDEN)) {
3280 if (lp != head || op->type != EJS_TYPE_OBJECT ||
3281 !(flags & EJS_ENUM_CLASSES)) {
3285 op = obj->baseClass;
3287 } while (lp == 0 && op);
3292 /******************************************************************************/
3294 * Get the next property in sequence. This will only enumerate properties in
3295 * this class and not in base classes.
3298 EjsProperty *ejsGetNextProperty(EjsProperty *last, int flags)
3302 EjsPropLink *lp, *head;
3304 obj = last->parentObj;
3306 lp = last->link.next;
3311 while (lp != head) {
3312 pp = ejsGetPropertyFromLink(lp);
3313 if (! pp->dontEnumerate || (flags & EJS_ENUM_HIDDEN)) {
3318 if (lp != head || !(flags & EJS_ENUM_CLASSES)) {
3323 * Now iterate over properties in base classes (down the chain)
3325 if (obj->baseClass == 0) {
3329 obj = obj->baseClass->objectState;
3337 /******************************************************************************/
3339 * Find a variable given a variable name and return the parent object and
3340 * the variable itself. This routine supports literal variable and property
3341 * names that may be objects or arrays but may NOT have expressions.
3342 * Returns -1 on errors or if the variable is not found.
3343 * FUTURE -- Needs OPT
3346 EjsVar *ejsFindProperty(Ejs *ep, EjsVar **obj, char **property, EjsVar *global,
3347 EjsVar *local, const char *fullName, int create)
3349 EjsProperty *currentProp;
3351 /* MOB -- WARNING BIG */
3352 char tokBuf[EJS_MAX_ID], propertyName[EJS_MAX_ID];
3353 char *token, *next, *cp, *endp;
3355 mprAssert(fullName && *fullName);
3361 global = ep->global;
3371 if (fullName == 0) {
3375 next = (char*) fullName;
3376 token = getNextVarToken(&next, tokBuf, sizeof(tokBuf));
3377 mprStrcpy(propertyName, sizeof(propertyName), token);
3380 currentProp = ejsGetProperty(ep, local, token);
3383 if (currentProp == 0) {
3384 currentProp = ejsGetProperty(ep, global, token);
3385 currentObj = global;
3388 token = getNextVarToken(&next, tokBuf, sizeof(tokBuf));
3390 while (currentObj != 0 && token != 0 && *token) {
3392 if (currentProp == 0) {
3395 currentObj = ¤tProp->var;
3398 if (*token == '[') {
3399 token = getNextVarToken(&next, tokBuf, sizeof(tokBuf));
3401 mprStrcpy(propertyName, sizeof(propertyName), token);
3405 if ((endp = strchr(cp, '\"')) != 0) {
3408 } else if (*cp == '\'') {
3410 if ((endp = strchr(cp, '\'')) != 0) {
3415 currentProp = ejsGetProperty(ep, currentObj, propertyName);
3417 token = getNextVarToken(&next, tokBuf, sizeof(tokBuf));
3418 if (*token != ']') {
3422 } else if (*token == '.') {
3423 token = getNextVarToken(&next, tokBuf, sizeof(tokBuf));
3424 if (!isalpha((int) token[0]) &&
3425 token[0] != '_' && token[0] != '$') {
3429 mprStrcpy(propertyName, sizeof(propertyName), token);
3430 currentProp = ejsGetProperty(ep, currentObj, token);
3433 currentProp = ejsGetProperty(ep, currentObj, token);
3436 if (next == 0 || *next == '\0') {
3439 token = getNextVarToken(&next, tokBuf, sizeof(tokBuf));
3447 if (currentProp == 0 && currentObj >= 0 && create) {
3448 currentProp = ejsCreateSimpleProperty(ep, currentObj, propertyName);
3452 *property = currentProp->name;
3454 return ejsGetVarPtr(currentProp);
3457 /******************************************************************************/
3459 * Get the next token as part of a variable specification. This will return
3460 * a pointer to the next token and will return a pointer to the next token
3461 * (after this one) in "next". The tokBuf holds the parsed token.
3464 static char *getNextVarToken(char **next, char *tokBuf, int tokBufLen)
3470 while (isspace((int) *start) || *start == '\n' || *start == '\r') {
3475 if (*cp == '.' || *cp == '[' || *cp == ']') {
3478 while (*cp && *cp != '.' && *cp != '[' && *cp != ']' &&
3479 !isspace((int) *cp) && *cp != '\n' && *cp != '\r') {
3483 len = mprMemcpy(tokBuf, tokBufLen - 1, start, cp - start);
3490 /******************************************************************************/
3492 EjsVar *ejsGetGlobalClass(Ejs *ep)
3497 /******************************************************************************/
3498 /*************************** Property Access Methods **************************/
3499 /******************************************************************************/
3501 * Create an undefined property. This routine calls the object method hooks.
3504 /* MOB -- better suffix than "Method" */
3505 EjsVar *ejsCreatePropertyMethod(Ejs *ep, EjsVar *op, const char *property)
3511 mprAssert(property && *property);
3517 mprAssert(op->type == EJS_TYPE_OBJECT);
3518 mprAssert(op->objectState);
3520 if (op->objectState == 0) {
3524 if (op->objectState->methods == 0) {
3525 vp = ejsGetVarPtr(ejsCreateSimpleProperty(ep, op, property));
3527 vp = (op->objectState->methods->createProperty)(ep, op, property);
3532 op->objectState->hasErrors = 1;
3537 * FUTURE - find a better way.
3540 ejsSetArrayLength(ep, op, property, 0, 0);
3545 /******************************************************************************/
3547 int ejsDeletePropertyMethod(Ejs *ep, EjsVar *op, const char *property)
3553 mprAssert(property && *property);
3559 mprAssert(op->type == EJS_TYPE_OBJECT);
3560 mprAssert(op->objectState);
3562 if (op->objectState == 0) {
3566 if (op->objectState->methods == 0) {
3567 rc = ejsDeleteProperty(ep, op, property);
3569 rc = (op->objectState->methods->deleteProperty)(ep, op, property);
3573 op->objectState->hasErrors = 1;
3576 op->objectState->dirty = 1;
3581 /******************************************************************************/
3583 * Set the value of a property. Create if it does not exist
3584 * If the object has property accessor methods defined, use those.
3587 EjsVar *ejsSetPropertyMethod(Ejs *ep, EjsVar *op, const char *property,
3588 const EjsVar *value)
3594 mprAssert(property && *property);
3601 mprAssert(op->type == EJS_TYPE_OBJECT);
3602 mprAssert(op->objectState);
3604 if (op->objectState == 0) {
3608 if (op->objectState->methods == 0) {
3609 vp = ejsGetVarPtr(ejsCreateSimpleProperty(ep, op, property));
3610 if (vp && ejsWriteVar(ep, vp, (EjsVar*) value, EJS_SHALLOW_COPY) < 0) {
3612 op->objectState->hasErrors = 1;
3617 vp = (op->objectState->methods->setProperty)(ep, op, property, value);
3622 op->objectState->hasErrors = 1;
3626 if (vp->type == EJS_TYPE_OBJECT) {
3628 * We make an object alive (and subject to garbage collection) when
3629 * it is referenced in some other object. If this is undesirable, the
3630 * caller should make the object permanent while calling this routine
3631 * and then afterward clear the alive bit by calling ejsMakeObjLive().
3633 if (op->objectState != vp->objectState) {
3634 vp->objectState->alive = 1;
3638 EjsProperty *pp = ejsGetPropertyPtr(vp);
3639 ejsSetVarName(ep, vp, &pp->name[0]);
3640 if (value->propertyName == 0) {
3641 ejsSetVarName(ep, (EjsVar*) value, &pp->name[0]);
3648 * Trap assignments to array.length. MOB - find a better way.
3650 if (vp->isArrayLength) {
3651 ejsSetArrayLength(ep, op, 0, 0, value);
3654 op->objectState->dirty = 1;
3659 /******************************************************************************/
3661 EjsVar *ejsGetPropertyMethod(Ejs *ep, EjsVar *op, const char *property)
3665 mprAssert(property && *property);
3671 mprAssert(op->type == EJS_TYPE_OBJECT);
3672 mprAssert(op->objectState);
3674 if (op->objectState == 0) {
3678 if (op->objectState->methods == 0) {
3679 return ejsGetVarPtr(ejsGetSimpleProperty(ep, op, property));
3681 return (op->objectState->methods->getProperty)(ep, op, property);
3685 /******************************************************************************/
3686 /*************************** Advisory Locking Support *************************/
3687 /******************************************************************************/
3688 #if BLD_FEATURE_MULTITHREAD
3690 void ejsLockObj(EjsVar *vp)
3693 mprAssert(vp->type == EJS_TYPE_OBJECT);
3694 mprAssert(vp->objectState);
3696 if (vp->objectState->mutex == 0) {
3697 vp->objectState->mutex = mprCreateLock(vp->objectState->ejs);
3699 mprLock(vp->objectState->mutex);
3702 /******************************************************************************/
3704 void ejsUnlockObj(EjsVar *vp)
3707 mprAssert(vp->type == EJS_TYPE_OBJECT);
3708 mprAssert(vp->objectState);
3710 if (vp->objectState->mutex) {
3711 mprUnlock(vp->objectState->mutex);
3716 /******************************************************************************/
3717 /************************** Internal Support Routines *************************/
3718 /******************************************************************************/
3723 static EjsObj *createObj(EJS_LOC_DEC(ep, loc))
3728 op = (EjsObj*) ejsAllocObj(EJS_LOC_PASS(ep, loc));
3734 * The objectState holds the dummy head for the ordered list of properties
3737 lp->next = lp->prev = lp;
3741 * This makes it much easier to debug the list
3744 lp->propertyName = "dummyHead";
3750 /******************************************************************************/
3752 * Destroy an object. Called by the garbage collector if there are no more
3753 * references to an object.
3756 int ejsDestroyObj(Ejs *ep, EjsObj *obj)
3759 EjsPropLink *lp, *head, *nextLink;
3763 if (obj->destructor) {
3765 memset(&v, 0, sizeof(v));
3766 v.type = EJS_TYPE_OBJECT;
3767 v.objectState = obj;
3768 ejsSetVarName(ep, &v, "destructor");
3770 #if BLD_FEATURE_ALLOC_LEAK_TRACK
3771 v.gc.allocatedBy = "static";
3774 if ((obj->destructor)(ep, &v) < 0) {
3778 mprFree(obj->objName);
3782 * Just for safety. An object may be marked by a GC on the default
3783 * interpreter. After destroying, it won't be on the free list and so
3790 for (lp = head->next; lp != head; lp = nextLink) {
3792 pp = ejsGetPropertyFromLink(lp);
3793 nextLink = lp->next;
3796 * We don't unlink as we are destroying all properties.
3797 * If an object, we don't need to clear either.
3799 if (pp->var.type != EJS_TYPE_OBJECT) {
3800 ejsClearVar(ep, ejsGetVarPtr(pp));
3802 ejsFree(ep, pp, EJS_SLAB_PROPERTY);
3805 #if BLD_FEATURE_MULTITHREAD
3807 mprDestroyLock(obj->mutex);
3811 ejsFree(ep, obj, EJS_SLAB_OBJ);
3815 /******************************************************************************/
3817 * Fast hash. The history of this algorithm is part of lost computer science
3821 static int hash(const char *property)
3825 mprAssert(property);
3829 sum += (sum * 33) + *property++;
3832 return sum % EJS_OBJ_HASH_SIZE;
3835 /******************************************************************************/
3837 * Set a new length for an array. If create is non-null, then it is the name
3838 * of a new array index. If delete is set, it is the name of an index being
3839 * deleted. If setLength is set to a variable, it counts the new length for the
3840 * array. Note that create and delete are ignored if they are non-integer
3841 * array indexes (eg. normal properties).
3844 void ejsSetArrayLength(Ejs *ep, EjsVar *obj, const char *create,
3845 const char *delete, const EjsVar *setLength)
3849 int oldSize, newSize, i;
3851 vp = ejsGetPropertyAsVar(ep, obj, "length");
3852 oldSize = vp->integer;
3856 if (isdigit(*create)) {
3858 newSize = max(i + 1, oldSize);
3860 } else if (delete) {
3861 if (isdigit(*delete)) {
3863 newSize = (i == (oldSize - 1) ? oldSize - 1 : oldSize);
3866 newSize = setLength->integer;
3869 for (i = newSize; i < oldSize; i++) {
3870 mprItoa(idx, sizeof(idx), i);
3871 ejsDeleteProperty(ep, obj, idx);
3874 if (ejsWriteVarAsInteger(ep, vp, newSize) == 0) {
3879 /******************************************************************************/
3881 void ejsClearObjErrors(EjsVar *vp)
3883 if (vp == 0 || vp->type != EJS_TYPE_OBJECT || vp->objectState == 0) {
3887 vp->objectState->hasErrors = 0;
3890 /******************************************************************************/
3892 int ejsObjHasErrors(EjsVar *vp)
3894 if (vp == 0 || vp->type != EJS_TYPE_OBJECT || vp->objectState == 0) {
3898 return vp->objectState->hasErrors;
3901 /******************************************************************************/
3903 bool ejsIsObjDirty(EjsVar *vp)
3905 mprAssert(vp->type == EJS_TYPE_OBJECT && vp->objectState);
3907 if (vp->type == EJS_TYPE_OBJECT && vp->objectState) {
3908 return vp->objectState->dirty;
3913 /******************************************************************************/
3915 void ejsResetObjDirtyBit(EjsVar *vp)
3917 mprAssert(vp->type == EJS_TYPE_OBJECT && vp->objectState);
3919 if (vp->type == EJS_TYPE_OBJECT && vp->objectState) {
3920 vp->objectState->dirty = 0;
3924 /******************************************************************************/
3926 * Copy a string. Always null terminate.
3929 static int dupString(MPR_LOC_DEC(ctx, loc), uchar **dest, const void *src,
3936 *dest = mprMemdupInternal(MPR_LOC_PASS(ctx, loc), src, nbytes + 1);
3938 return MPR_ERR_MEMORY;
3942 *dest = (uchar*) mprAlloc(ctx, 1);
3946 (*dest)[nbytes] = '\0';
3951 /******************************************************************************/
3953 const char *ejsGetVarTypeAsString(EjsVar *vp)
3957 case EJS_TYPE_UNDEFINED:
3963 case EJS_TYPE_CMETHOD:
3965 case EJS_TYPE_FLOAT:
3969 case EJS_TYPE_INT64:
3971 case EJS_TYPE_OBJECT:
3973 case EJS_TYPE_METHOD:
3975 case EJS_TYPE_STRING:
3977 case EJS_TYPE_STRING_CMETHOD:
3978 return "string method";
3984 /******************************************************************************/
3986 void *ejsGetVarUserPtr(EjsVar *vp)
3989 mprAssert(vp->type == EJS_TYPE_PTR);
3991 if (!ejsVarIsPtr(vp)) {
3994 return vp->ptr.userPtr;
3997 /******************************************************************************/
3999 void ejsSetVarUserPtr(EjsVar *vp, void *data)
4002 mprAssert(vp->type == EJS_TYPE_PTR);
4004 vp->ptr.userPtr = data;
4007 /******************************************************************************/
4009 * Return TRUE if target is a subclass (or the same class) as baseClass.
4012 bool ejsIsSubClass(EjsVar *target, EjsVar *baseClass)
4015 if (target->objectState == baseClass->objectState) {
4018 target = target->objectState->baseClass;
4024 /******************************************************************************/
4031 * vim600: sw=4 ts=4 fdm=marker
4032 * vim<600: sw=4 ts=4