1 /**
2 * @file ejsVar.c
3 * @brief Mbedthis Portable Runtime Universal Variable Type
4 */
5
6 /*
7 * @copy default
8 *
9 * Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved.
10 * Copyright (c) Michael O'Brien, 1994-1995. All Rights Reserved.
11 *
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.
17 *
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
23 *
24 * This program is distributed WITHOUT ANY WARRANTY; without even the
25 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
26 *
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
32 *
33 * @end
34 */
35
36 /******************************* Documentation ********************************/
37
38 /*
39 * This module is NOT multithreaded.
40 *
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.
44 */
45
46 /********************************** Includes **********************************/
47
48 #include "ejs.h"
49
50 /***************************** Forward Declarations ***************************/
51
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);
68 #if UNUSED && KEEP
69 static void linkPropertyAfter(EjsObj *obj, EjsPropLink *at,
70 EjsPropLink *propLink);
71 #endif
72
73 static EjsProperty *hashLookup(EjsObj *obj, const char *property,
74 int *propertyIndex, EjsProperty **hashTail);
75
76 /******************************************************************************/
77 /********************************** Var Routines ******************************/
78 /******************************************************************************/
79
ejsGetVarType(EjsVar * vp)80 EjsType ejsGetVarType(EjsVar *vp)
81 {
82 mprAssert(vp);
83
84 return vp->type;
85 }
86
87 /******************************************************************************/
88
ejsFreeVar(Ejs * ep,EjsVar * vp)89 void ejsFreeVar(Ejs *ep, EjsVar *vp)
90 {
91 if (vp) {
92 ejsClearVar(ep, vp);
93 ejsFree(ep, vp, EJS_SLAB_VAR);
94 }
95 }
96
97 /******************************************************************************/
98 #if UNUSED
99 /*
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
102 * object references.
103 */
104
ejsZeroVar(Ejs * ep,EjsVar * vp)105 void ejsZeroVar(Ejs *ep, EjsVar *vp)
106 {
107 vp->type = EJS_TYPE_UNDEFINED;
108 vp->objectState = 0;
109 vp->method.body = 0;
110 vp->method.args = 0;
111 vp->callsSuper = 0;
112 vp->ptr.destructor = 0;
113 vp->allocatedData = 0;
114 }
115
116 #endif
117 /******************************************************************************/
118 /*
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
121 * object references.
122 */
123
ejsClearVar(Ejs * ep,EjsVar * vp)124 void ejsClearVar(Ejs *ep, EjsVar *vp)
125 {
126 MprArray *argList;
127 int i;
128
129 mprAssert(vp);
130 mprAssert(ep);
131
132 if (! vp->allocatedData) {
133 vp->type = EJS_TYPE_UNDEFINED;
134 return;
135 }
136 if (vp->type == EJS_TYPE_UNDEFINED) {
137 return;
138 }
139
140 switch (vp->type) {
141 default:
142 break;
143
144 case EJS_TYPE_STRING:
145 mprFree(vp->string);
146 vp->string = 0;
147 break;
148
149 case EJS_TYPE_OBJECT:
150 /*
151 * Set the "alive" bit so that the GC will cleanup if no
152 * other references.
153 */
154 if (vp->objectState) {
155 vp->objectState->alive = 1;
156 }
157 vp->objectState = 0;
158 break;
159
160 case EJS_TYPE_METHOD:
161 argList = vp->method.args;
162 /*
163 * MOB OPT -- should be able to do just one mprFree(vp->method.args)
164 */
165 mprFree(vp->method.body);
166 if (argList) {
167 for (i = 0; i < argList->length; i++) {
168 mprFree(argList->items[i]);
169 }
170 mprFree(vp->method.args);
171 }
172 vp->method.args = 0;
173 vp->method.body = 0;
174 vp->callsSuper = 0;
175 break;
176
177 case EJS_TYPE_PTR:
178 if (vp->ptr.destructor) {
179 (vp->ptr.destructor)(ep, vp);
180 }
181 break;
182 }
183
184 vp->type = EJS_TYPE_UNDEFINED;
185 vp->allocatedData = 0;
186 }
187
188 /******************************************************************************/
189 /*
190 * Initialize an undefined value.
191 */
192
ejsCreateUndefinedVar(Ejs * ep)193 EjsVar *ejsCreateUndefinedVar(Ejs *ep)
194 {
195 EjsVar *vp;
196
197 mprAssert(ep);
198
199 vp = ejsAllocVar(EJS_LOC_ARGS(ep));
200 if (vp) {
201 vp->type = EJS_TYPE_UNDEFINED;
202 }
203 return vp;
204 }
205
206 /******************************************************************************/
207 /*
208 * Initialize an null value.
209 */
210
ejsCreateNullVar(Ejs * ep)211 EjsVar *ejsCreateNullVar(Ejs *ep)
212 {
213 EjsVar *vp;
214
215 mprAssert(ep);
216
217 vp = ejsAllocVar(EJS_LOC_ARGS(ep));
218 if (vp) {
219 vp->type = EJS_TYPE_NULL;
220 }
221 return vp;
222 }
223
224 /******************************************************************************/
225
ejsCreateBoolVar(Ejs * ep,int value)226 EjsVar *ejsCreateBoolVar(Ejs *ep, int value)
227 {
228 EjsVar *vp;
229
230 mprAssert(ep);
231
232 vp = ejsAllocVar(EJS_LOC_ARGS(ep));
233 if (vp) {
234 vp->type = EJS_TYPE_BOOL;
235 vp->boolean = value;
236 }
237 return vp;
238 }
239
240 /******************************************************************************/
241 /*
242 * Initialize a C method.
243 */
244
ejsCreateCMethodVar(Ejs * ep,EjsCMethod fn,void * userData,int flags)245 EjsVar *ejsCreateCMethodVar(Ejs *ep, EjsCMethod fn, void *userData, int flags)
246 {
247 EjsVar *vp;
248
249 mprAssert(ep);
250
251 vp = ejsAllocVar(EJS_LOC_ARGS(ep));
252 if (vp) {
253 vp->type = EJS_TYPE_CMETHOD;
254 vp->cMethod.fn = fn;
255 vp->cMethod.userData = userData;
256 vp->flags = flags;
257 }
258 return vp;
259 }
260
261 /******************************************************************************/
262 /*
263 * Initialize a C method.
264 */
265
ejsCreateStringCMethodVar(Ejs * ep,EjsStringCMethod fn,void * userData,int flags)266 EjsVar *ejsCreateStringCMethodVar(Ejs *ep, EjsStringCMethod fn,
267 void *userData, int flags)
268 {
269 EjsVar *vp;
270
271 mprAssert(ep);
272 mprAssert(fn);
273
274 vp = ejsAllocVar(EJS_LOC_ARGS(ep));
275 if (vp) {
276 vp->type = EJS_TYPE_STRING_CMETHOD;
277 vp->cMethodWithStrings.fn = fn;
278 vp->cMethodWithStrings.userData = userData;
279 vp->flags = flags;
280 }
281 return vp;
282 }
283
284 /******************************************************************************/
285 /*
286 * Initialize an opaque pointer.
287 */
288
ejsCreatePtrVar(Ejs * ep,void * ptr,EjsDestructor destructor)289 EjsVar *ejsCreatePtrVar(Ejs *ep, void *ptr, EjsDestructor destructor)
290 {
291 EjsVar *vp;
292
293 mprAssert(ep);
294 mprAssert(ptr);
295
296 vp = ejsAllocVar(EJS_LOC_ARGS(ep));
297 if (vp) {
298 vp->type = EJS_TYPE_PTR;
299 vp->ptr.userPtr = ptr;
300 vp->ptr.destructor = destructor;
301 vp->allocatedData = 1;
302 }
303 return vp;
304 }
305
306 /******************************************************************************/
307 #if BLD_FEATURE_FLOATING_POINT
308 /*
309 * Initialize a floating value.
310 */
311
ejsCreateFloatVar(Ejs * ep,double value)312 EjsVar *ejsCreateFloatVar(Ejs *ep, double value)
313 {
314 EjsVar *vp;
315
316 mprAssert(ep);
317
318 vp = ejsAllocVar(EJS_LOC_ARGS(ep));
319 if (vp) {
320 vp->type = EJS_TYPE_FLOAT;
321 vp->floating = value;
322 }
323 return vp;
324 }
325
326 #endif
327 /******************************************************************************/
328 /*
329 * Initialize an integer value.
330 */
331
ejsCreateIntegerVar(Ejs * ep,int value)332 EjsVar *ejsCreateIntegerVar(Ejs *ep, int value)
333 {
334 EjsVar *vp;
335
336 mprAssert(ep);
337
338 vp = ejsAllocVar(EJS_LOC_ARGS(ep));
339 if (vp) {
340 vp->type = EJS_TYPE_INT;
341 vp->integer = value;
342 }
343 return vp;
344 }
345
346 /******************************************************************************/
347 #if BLD_FEATURE_INT64
348 /*
349 * Initialize a 64-bit integer value.
350 */
351
ejsCreateInteger64Var(Ejs * ep,int64 value)352 EjsVar *ejsCreateInteger64Var(Ejs *ep, int64 value)
353 {
354 EjsVar *vp;
355
356 mprAssert(ep);
357
358 vp = ejsAllocVar(EJS_LOC_ARGS(ep));
359 if (vp) {
360 vp->type = EJS_TYPE_INT64;
361 vp->integer64 = value;
362 }
363 return vp;
364 }
365
366 #endif /* BLD_FEATURE_INT64 */
367 /******************************************************************************/
368 /*
369 * Initialize an number variable. Type is defined by configure.
370 */
371
ejsCreateNumberVar(Ejs * ep,EjsNum value)372 EjsVar *ejsCreateNumberVar(Ejs *ep, EjsNum value)
373 {
374 EjsVar *vp;
375
376 mprAssert(ep);
377
378 vp = ejsAllocVar(EJS_LOC_ARGS(ep));
379 mprAssert(vp);
380
381 if (vp) {
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
386 vp->float = value;
387 #else
388 vp->integer = value;
389 #endif
390 }
391 return vp;
392 }
393
394 /******************************************************************************/
395 /*
396 * Initialize a (bare) JavaScript method. args and body can be null.
397 */
398
ejsCreateMethodVar(Ejs * ep,const char * body,MprArray * args,int flags)399 EjsVar *ejsCreateMethodVar(Ejs *ep, const char *body, MprArray *args, int flags)
400 {
401 EjsVar *vp;
402 int i;
403
404 mprAssert(ep);
405
406 vp = ejsAllocVar(EJS_LOC_ARGS(ep));
407 mprAssert(vp);
408
409 if (vp == 0) {
410 return 0;
411 }
412
413 vp->type = EJS_TYPE_METHOD;
414
415 vp->allocatedData = 1;
416
417 vp->method.args = mprCreateItemArray(ep, EJS_INC_ARGS, EJS_MAX_ARGS);
418 if (vp->method.args == 0) {
419 mprAssert(vp->method.args);
420 ejsFreeVar(ep, vp);
421 return 0;
422 }
423
424 if (args) {
425 for (i = 0; i < args->length; i++) {
426 mprAddItem(vp->method.args,
427 mprStrdup(vp->method.args, mprGetItem(args, i)));
428 }
429 }
430 vp->method.body = mprStrdup(vp->method.args, body);
431
432 if (vp->method.body == 0) {
433 ejsFreeVar(ep, vp);
434 return 0;
435 }
436 vp->flags = flags;
437
438 return vp;
439 }
440
441 /******************************************************************************/
442 /*
443 * Initialize an object variable.
444 */
445
ejsCreateObjVarInternal(EJS_LOC_DEC (ep,loc))446 EjsVar *ejsCreateObjVarInternal(EJS_LOC_DEC(ep, loc))
447 {
448 EjsVar *vp;
449
450 mprAssert(ep);
451
452 vp = ejsAllocVar(EJS_LOC_PASS(ep, loc));
453 mprAssert(vp);
454
455 if (vp) {
456 vp->type = EJS_TYPE_OBJECT;
457 vp->objectState = createObj(EJS_LOC_PASS(ep, loc));
458 if (vp->objectState == 0) {
459 ejsFreeVar(ep, vp);
460 return 0;
461 }
462 vp->allocatedData = 1;
463 }
464 return vp;
465 }
466
467 /******************************************************************************/
468 /*
469 * Initialize a string value.
470 */
471
ejsCreateStringVarInternal(EJS_LOC_DEC (ep,loc),const char * value)472 EjsVar *ejsCreateStringVarInternal(EJS_LOC_DEC(ep, loc), const char *value)
473 {
474 EjsVar *vp;
475
476 mprAssert(ep);
477
478 vp = ejsAllocVar(EJS_LOC_PASS(ep, loc));
479 mprAssert(vp);
480
481 if (vp) {
482 vp->type = EJS_TYPE_STRING;
483 vp->string = mprStrdupInternal(EJS_LOC_PASS(ep, loc), value);
484 if (vp->string == 0) {
485 ejsFreeVar(ep, vp);
486 return 0;
487 }
488 vp->length = strlen(vp->string);
489 vp->allocatedData = 1;
490 }
491 return vp;
492 }
493
494 /******************************************************************************/
495 /*
496 * Initialize a binary string value.
497 */
498
ejsCreateBinaryStringVar(Ejs * ep,const uchar * value,int len)499 EjsVar *ejsCreateBinaryStringVar(Ejs *ep, const uchar *value, int len)
500 {
501 EjsVar *vp;
502
503 mprAssert(ep);
504
505 vp = ejsAllocVar(EJS_LOC_ARGS(ep));
506 if (vp) {
507 vp->type = EJS_TYPE_STRING;
508 vp->length = dupString(MPR_LOC_ARGS(ep), &vp->ustring, value, len);
509 if (vp->length < 0) {
510 ejsFreeVar(ep, vp);
511 return 0;
512 }
513 vp->allocatedData = 1;
514 }
515 return vp;
516 }
517
518 /******************************************************************************/
519
ejsSetClassName(Ejs * ep,EjsVar * vp,const char * name)520 void ejsSetClassName(Ejs *ep, EjsVar *vp, const char *name)
521 {
522 EjsObj *obj;
523
524 if (vp == 0 || !ejsVarIsObject(vp) || vp->objectState == 0) {
525 mprAssert(0);
526 return;
527 }
528 obj = vp->objectState;
529
530 if (obj->className) {
531 mprFree(obj->className);
532 }
533 obj->className = mprStrdup(ep, name);
534 }
535
536 /******************************************************************************/
537
ejsDupVarInternal(EJS_LOC_DEC (ep,loc),EjsVar * src,EjsCopyDepth copyDepth)538 EjsVar *ejsDupVarInternal(EJS_LOC_DEC(ep, loc), EjsVar *src,
539 EjsCopyDepth copyDepth)
540 {
541 EjsVar *vp;
542
543 vp = ejsAllocVar(EJS_LOC_PASS(ep, loc));
544 if (vp == 0) {
545 return 0;
546 }
547
548 vp->type = EJS_TYPE_UNDEFINED;
549
550 return copyVar(EJS_LOC_PASS(ep, loc), vp, src, copyDepth);
551 }
552
553 /******************************************************************************/
554 /*
555 * Set a var to a new value
556 */
557
ejsWriteVarInternal(EJS_LOC_DEC (ep,loc),EjsVar * dest,const EjsVar * src,EjsCopyDepth copyDepth)558 EjsVar *ejsWriteVarInternal(EJS_LOC_DEC(ep, loc), EjsVar *dest,
559 const EjsVar *src, EjsCopyDepth copyDepth)
560 {
561 mprAssert(dest);
562 mprAssert(src);
563
564 return copyVar(EJS_LOC_PASS(ep, loc), dest, src, copyDepth);
565 }
566
567 /******************************************************************************/
568 /*
569 * Set a var using a new bool value
570 */
571
ejsWriteVarAsBoolean(Ejs * ep,EjsVar * dest,int value)572 EjsVar *ejsWriteVarAsBoolean(Ejs *ep, EjsVar *dest, int value)
573 {
574 mprAssert(dest);
575
576 if (dest->type != EJS_TYPE_UNDEFINED) {
577 ejsClearVar(ep, dest);
578 }
579
580 dest->type = EJS_TYPE_BOOL;
581 dest->boolean = value;
582 dest->allocatedData = 0;
583 dest->flags = 0;
584
585 return dest;
586 }
587
588 /******************************************************************************/
589 /*
590 * Set a var using a new C Method
591 */
592
ejsWriteVarAsCMethod(Ejs * ep,EjsVar * dest,EjsCMethod fn,void * userData,int flags)593 EjsVar *ejsWriteVarAsCMethod(Ejs *ep, EjsVar *dest, EjsCMethod fn,
594 void *userData, int flags)
595 {
596 mprAssert(dest);
597
598 if (dest->type != EJS_TYPE_UNDEFINED) {
599 ejsClearVar(ep, dest);
600 }
601
602 dest->type = EJS_TYPE_CMETHOD;
603 dest->cMethod.fn = fn;
604 dest->cMethod.userData = userData;
605 dest->flags = flags;
606 dest->allocatedData = 0;
607
608 return dest;
609 }
610
611 /******************************************************************************/
612 #if BLD_FEATURE_FLOATING_POINT
613 /*
614 * Set a var using a new float value
615 */
616
ejsWriteVarAsFloat(Ejs * ep,EjsVar * dest,double value)617 EjsVar *ejsWriteVarAsFloat(Ejs *ep, EjsVar *dest, double value)
618 {
619 mprAssert(dest);
620
621 if (dest->type != EJS_TYPE_UNDEFINED) {
622 ejsClearVar(ep, dest);
623 }
624
625 dest->type = EJS_TYPE_FLOAT;
626 dest->floating = value;
627 dest->allocatedData = 0;
628 dest->flags = 0;
629
630 return dest;
631 }
632
633 #endif
634 /******************************************************************************/
635 /*
636 * Set a var using a new integer value
637 */
638
ejsWriteVarAsInteger(Ejs * ep,EjsVar * dest,int value)639 EjsVar *ejsWriteVarAsInteger(Ejs *ep, EjsVar *dest, int value)
640 {
641 mprAssert(dest);
642
643 if (dest->type != EJS_TYPE_UNDEFINED) {
644 ejsClearVar(ep, dest);
645 }
646
647 dest->type = EJS_TYPE_INT;
648 dest->integer = value;
649 dest->allocatedData = 0;
650 dest->flags = 0;
651
652 return dest;
653 }
654
655 /******************************************************************************/
656 #if BLD_FEATURE_INT64
657 /*
658 * Set a var using a new integer value
659 */
660
ejsWriteVarAsInteger64(Ejs * ep,EjsVar * dest,int64 value)661 EjsVar *ejsWriteVarAsInteger64(Ejs *ep, EjsVar *dest, int64 value)
662 {
663 mprAssert(dest);
664
665 if (dest->type != EJS_TYPE_UNDEFINED) {
666 ejsClearVar(ep, dest);
667 }
668
669 dest->type = EJS_TYPE_INT64;
670 dest->integer64 = value;
671 dest->allocatedData = 0;
672 dest->flags = 0;
673
674 return dest;
675 }
676
677 #endif
678 /******************************************************************************/
679 /*
680 * Set a var using a new Method
681 */
682
ejsWriteVarAsMethod(Ejs * ep,EjsVar * dest,const char * body,MprArray * args)683 EjsVar *ejsWriteVarAsMethod(Ejs *ep, EjsVar *dest, const char *body,
684 MprArray *args)
685 {
686 EjsVar **srcArgs, *arg;
687 int i;
688
689 mprAssert(ep);
690 mprAssert(dest);
691 mprAssert(body);
692
693 if (dest->type != EJS_TYPE_UNDEFINED) {
694 ejsClearVar(ep, dest);
695 }
696
697 dest->method.args = mprCreateItemArray(ep, EJS_INC_ARGS, EJS_MAX_ARGS);
698 if (dest->method.args == 0) {
699 return 0;
700 }
701
702 dest->type = EJS_TYPE_METHOD;
703
704 if (args) {
705 srcArgs = (EjsVar**) args->items;
706 for (i = 0; i < args->length; i++) {
707 arg = ejsDupVar(ep, srcArgs[i], EJS_SHALLOW_COPY);
708 if (arg == 0) {
709 return 0;
710 }
711 if (mprAddItem(dest->method.args, arg) < 0) {
712 return 0;
713 }
714 }
715 }
716
717 dest->method.body = mprStrdup(dest->method.args, body);
718 if (dest->method.body == 0) {
719 return 0;
720 }
721
722 dest->allocatedData = 1;
723 dest->flags = 0;
724
725 return dest;
726 }
727
728 /******************************************************************************/
729 /*
730 * Set a var to null
731 */
732
ejsWriteVarAsNull(Ejs * ep,EjsVar * dest)733 EjsVar *ejsWriteVarAsNull(Ejs *ep, EjsVar *dest)
734 {
735 mprAssert(dest);
736
737 if (dest->type != EJS_TYPE_UNDEFINED) {
738 ejsClearVar(ep, dest);
739 }
740
741 dest->type = EJS_TYPE_NULL;
742 dest->allocatedData = 0;
743 dest->flags = 0;
744
745 return dest;
746 }
747
748 /******************************************************************************/
749 /*
750 * Set a var using a new number value
751 */
752
ejsWriteVarAsNumber(Ejs * ep,EjsVar * dest,EjsNum value)753 EjsVar *ejsWriteVarAsNumber(Ejs *ep, EjsVar *dest, EjsNum value)
754 {
755 mprAssert(dest);
756
757 if (dest->type != EJS_TYPE_UNDEFINED) {
758 ejsClearVar(ep, dest);
759 }
760
761 dest->type = EJS_NUM_VAR;
762 dest->ejsNumber = value;
763 dest->allocatedData = 0;
764 dest->flags = 0;
765
766 return dest;
767 }
768
769 /******************************************************************************/
770 /*
771 * Set a var using a new C Method
772 */
773
ejsWriteVarAsStringCMethod(Ejs * ep,EjsVar * dest,EjsStringCMethod fn,void * userData,int flags)774 EjsVar *ejsWriteVarAsStringCMethod(Ejs *ep, EjsVar *dest, EjsStringCMethod fn,
775 void *userData, int flags)
776 {
777 mprAssert(dest);
778
779 if (dest->type != EJS_TYPE_UNDEFINED) {
780 ejsClearVar(ep, dest);
781 }
782
783 dest->type = EJS_TYPE_CMETHOD;
784 dest->cMethodWithStrings.fn = fn;
785 dest->cMethodWithStrings.userData = userData;
786 dest->flags = flags;
787 dest->allocatedData = 0;
788
789 return dest;
790 }
791
792 /******************************************************************************/
793 /*
794 * Set a var using a new string value
795 */
796
ejsWriteVarAsStringInternal(EJS_LOC_DEC (ep,loc),EjsVar * dest,const char * value)797 EjsVar *ejsWriteVarAsStringInternal(EJS_LOC_DEC(ep, loc), EjsVar *dest,
798 const char *value)
799 {
800 mprAssert(dest);
801 mprAssert(value);
802
803 if (dest->type != EJS_TYPE_UNDEFINED) {
804 ejsClearVar(ep, dest);
805 }
806
807 dest->string = mprStrdupInternal(EJS_LOC_PASS(ep, loc), value);
808 if (dest->string == 0) {
809 return 0;
810 }
811
812 dest->length = strlen(dest->string);
813
814 dest->type = EJS_TYPE_STRING;
815 dest->allocatedData = 1;
816 dest->flags = 0;
817
818 return dest;
819 }
820
821 /******************************************************************************/
822 /*
823 * Set a var using a new string value
824 */
825
ejsWriteVarAsBinaryString(Ejs * ep,EjsVar * dest,const uchar * value,int len)826 EjsVar *ejsWriteVarAsBinaryString(Ejs *ep, EjsVar *dest, const uchar *value,
827 int len)
828 {
829 mprAssert(dest);
830 mprAssert(value);
831
832 ejsClearVar(ep, dest);
833
834 if (dest->type != EJS_TYPE_UNDEFINED) {
835 ejsClearVar(ep, dest);
836 }
837
838 dest->length = dupString(MPR_LOC_ARGS(ep), &dest->ustring, value, len);
839 if (dest->length < 0) {
840 return 0;
841 }
842
843 dest->type = EJS_TYPE_STRING;
844 dest->allocatedData = 1;
845 dest->flags = 0;
846
847 return dest;
848 }
849
850 /******************************************************************************/
851 /*
852 * Set a var to undefined
853 */
854
ejsWriteVarAsUndefined(Ejs * ep,EjsVar * dest)855 EjsVar *ejsWriteVarAsUndefined(Ejs *ep, EjsVar *dest)
856 {
857 mprAssert(dest);
858
859 if (dest->type != EJS_TYPE_UNDEFINED) {
860 ejsClearVar(ep, dest);
861 }
862
863 dest->type = EJS_TYPE_UNDEFINED;
864 dest->allocatedData = 0;
865 dest->flags = 0;
866
867 return dest;
868 }
869
870 /******************************************************************************/
871 /*
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.
875 */
876
ejsFormatVar(Ejs * ep,const char * fmt,EjsVar * vp)877 char *ejsFormatVar(Ejs *ep, const char *fmt, EjsVar *vp)
878 {
879 char *buf, *src, *value, *allocValue;
880 uchar *ubuf;
881 int len;
882
883 buf = 0;
884 allocValue = 0;
885 value = 0;
886
887 switch (vp->type) {
888 case EJS_TYPE_UNDEFINED:
889 value = "undefined";
890 break;
891
892 case EJS_TYPE_NULL:
893 value = "null";
894 break;
895
896 case EJS_TYPE_PTR:
897 if (fmt == NULL || *fmt == '\0') {
898 len = mprAllocSprintf(MPR_LOC_ARGS(ep), &buf, 0,
899 "[Opaque Pointer %p]", vp->ptr.userPtr);
900 } else {
901 len = mprAllocSprintf(MPR_LOC_ARGS(ep), &buf, 0, fmt, vp->ptr);
902 }
903 goto done;
904
905 case EJS_TYPE_BOOL:
906 value = (vp->boolean) ? "true" : "false";
907 break;
908
909 #if BLD_FEATURE_FLOATING_POINT
910 case EJS_TYPE_FLOAT:
911 if (fmt == NULL || *fmt == '\0') {
912 fmt = "%f";
913 }
914 len = mprAllocSprintf(MPR_LOC_ARGS(ep), &buf, 0, fmt, vp->floating);
915 goto done;
916 #endif
917
918 case EJS_TYPE_INT:
919 if (fmt == NULL || *fmt == '\0') {
920 fmt = "%d";
921 }
922 mprAllocSprintf(MPR_LOC_ARGS(ep), &buf, 0, fmt, vp->integer);
923 goto done;
924
925 #if BLD_FEATURE_INT64
926 case EJS_TYPE_INT64:
927 if (fmt == NULL || *fmt == '\0') {
928 fmt = "%Ld";
929 }
930 mprAllocSprintf(MPR_LOC_ARGS(ep), &buf, 0, fmt, vp->integer64);
931 goto done;
932 #endif
933
934 case EJS_TYPE_CMETHOD:
935 value = "[C Method]";
936 break;
937
938 case EJS_TYPE_STRING_CMETHOD:
939 value = "[C StringMethod]";
940 break;
941
942 case EJS_TYPE_METHOD:
943 value = ejsVarToString(ep, vp);
944 break;
945
946 case EJS_TYPE_OBJECT:
947 value = ejsVarToString(ep, vp);
948 break;
949
950 case EJS_TYPE_STRING:
951 src = vp->string;
952 mprAssert(src);
953
954 if (fmt && *fmt && src) {
955 mprAllocSprintf(MPR_LOC_ARGS(ep), &buf, 0, fmt, src);
956
957 } else if (src == NULL) {
958 buf = mprStrdup(ep, "null");
959
960 } else {
961 ubuf = (uchar*) buf;
962 if (dupString(MPR_LOC_ARGS(ep), &ubuf, src, vp->length) < 0) {
963 return mprStrdup(ep, "");
964 }
965 buf = (char*) ubuf;
966 }
967 break;
968
969 default:
970 mprAssert(0);
971 }
972
973 if (fmt == NULL || *fmt == '\0') {
974 len = mprAllocSprintf(MPR_LOC_ARGS(ep), &buf, 0, "%s", value);
975 } else {
976 len = mprAllocSprintf(MPR_LOC_ARGS(ep), &buf, 0, fmt, value);
977 }
978
979 done:
980 if (allocValue) {
981 mprFree(allocValue);
982 }
983 return buf;
984 }
985
986 /******************************************************************************/
987 /*
988 * Convert the variable to a boolean. Only for primitive types.
989 */
990
ejsVarToBoolean(EjsVar * vp)991 int ejsVarToBoolean(EjsVar *vp)
992 {
993 mprAssert(vp);
994
995 switch (vp->type) {
996 case EJS_TYPE_UNDEFINED:
997 case EJS_TYPE_NULL:
998 case EJS_TYPE_STRING_CMETHOD:
999 case EJS_TYPE_CMETHOD:
1000 case EJS_TYPE_METHOD:
1001 return 0;
1002
1003 case EJS_TYPE_OBJECT:
1004 return (vp->objectState != NULL);
1005
1006 case EJS_TYPE_PTR:
1007 return (vp->ptr.userPtr != NULL);
1008
1009 case EJS_TYPE_BOOL:
1010 return vp->boolean;
1011
1012 #if BLD_FEATURE_FLOATING_POINT
1013 case EJS_TYPE_FLOAT:
1014 return (vp->floating != 0 && !ejsIsNan(vp->floating));
1015 #endif
1016
1017 case EJS_TYPE_INT:
1018 return (vp->integer != 0);
1019
1020 #if BLD_FEATURE_INT64
1021 case EJS_TYPE_INT64:
1022 return (vp->integer64 != 0);
1023 #endif
1024
1025 case EJS_TYPE_STRING:
1026 return (vp->length > 0);
1027 #if UNUSED
1028 if (strcmp(vp->string, "true") == 0 ||
1029 strcmp(vp->string, "TRUE") == 0) {
1030 return 1;
1031
1032 } else if (strcmp(vp->string, "false") == 0 ||
1033 strcmp(vp->string, "FALSE") == 0) {
1034 return 0;
1035
1036 } else {
1037 return atoi(vp->string);
1038 }
1039 #endif
1040 }
1041
1042 /* Not reached */
1043 return 0;
1044 }
1045
1046 /******************************************************************************/
1047 #if BLD_FEATURE_FLOATING_POINT
1048 /*
1049 * Convert the variable to a floating point number. Only for primitive types.
1050 */
1051
ejsVarToFloat(EjsVar * vp)1052 double ejsVarToFloat(EjsVar *vp)
1053 {
1054 mprAssert(vp);
1055
1056 switch (vp->type) {
1057 case EJS_TYPE_UNDEFINED:
1058 case EJS_TYPE_NULL:
1059 case EJS_TYPE_STRING_CMETHOD:
1060 case EJS_TYPE_CMETHOD:
1061 case EJS_TYPE_METHOD:
1062 case EJS_TYPE_OBJECT:
1063 case EJS_TYPE_PTR:
1064 return 0;
1065
1066 case EJS_TYPE_BOOL:
1067 return (vp->boolean) ? 1.0 : 0.0;
1068
1069 case EJS_TYPE_FLOAT:
1070 return vp->floating;
1071
1072 case EJS_TYPE_INT:
1073 return (double) vp->integer;
1074
1075 #if BLD_FEATURE_INT64
1076 case EJS_TYPE_INT64:
1077 return (double) vp->integer64;
1078 #endif
1079
1080 case EJS_TYPE_STRING:
1081 if (vp->length == 0) {
1082 return 0.0;
1083 } else {
1084 return atof(vp->string);
1085 }
1086 }
1087
1088 /* Not reached */
1089 return 0;
1090 }
1091
1092 #endif
1093 /******************************************************************************/
1094 /*
1095 * Convert the variable to an Integer type. Only works for primitive types.
1096 */
1097
ejsVarToInteger(EjsVar * vp)1098 int ejsVarToInteger(EjsVar *vp)
1099 {
1100 mprAssert(vp);
1101
1102 switch (vp->type) {
1103 case EJS_TYPE_UNDEFINED:
1104 case EJS_TYPE_NULL:
1105 case EJS_TYPE_STRING_CMETHOD:
1106 case EJS_TYPE_CMETHOD:
1107 case EJS_TYPE_METHOD:
1108 case EJS_TYPE_OBJECT:
1109 return 0;
1110
1111 case EJS_TYPE_BOOL:
1112 return (vp->boolean) ? 1 : 0;
1113
1114 #if BLD_FEATURE_FLOATING_POINT
1115 case EJS_TYPE_FLOAT:
1116 if (ejsIsNan(vp->floating)) {
1117 return 0;
1118 }
1119 return (int) vp->floating;
1120 #endif
1121
1122 case EJS_TYPE_INT:
1123 return vp->integer;
1124
1125 #if BLD_FEATURE_INT64
1126 case EJS_TYPE_INT64:
1127 return (int) vp->integer64;
1128 #endif
1129
1130 case EJS_TYPE_STRING:
1131 if (vp->length == 0) {
1132 return 0;
1133 } else {
1134 return ejsParseInteger(vp->string);
1135 }
1136 }
1137
1138 /* Not reached */
1139 return 0;
1140 }
1141
1142 /******************************************************************************/
1143 #if BLD_FEATURE_INT64
1144 /*
1145 * Convert the variable to an Integer64 type. Only works for primitive types.
1146 */
1147
ejsVarToInteger64(EjsVar * vp)1148 int64 ejsVarToInteger64(EjsVar *vp)
1149 {
1150 mprAssert(vp);
1151
1152 switch (vp->type) {
1153 case EJS_TYPE_UNDEFINED:
1154 case EJS_TYPE_NULL:
1155 case EJS_TYPE_STRING_CMETHOD:
1156 case EJS_TYPE_CMETHOD:
1157 case EJS_TYPE_METHOD:
1158 case EJS_TYPE_OBJECT:
1159 case EJS_TYPE_PTR:
1160 return 0;
1161
1162 case EJS_TYPE_BOOL:
1163 return (vp->boolean) ? 1 : 0;
1164
1165 #if BLD_FEATURE_FLOATING_POINT
1166 case EJS_TYPE_FLOAT:
1167 if (ejsIsNan(vp->floating)) {
1168 return 0;
1169 }
1170 return (int64) vp->floating;
1171 #endif
1172
1173 case EJS_TYPE_INT:
1174 return vp->integer;
1175
1176 case EJS_TYPE_INT64:
1177 return vp->integer64;
1178
1179 case EJS_TYPE_STRING:
1180 if (vp->length == 0) {
1181 return 0;
1182 } else {
1183 return ejsParseInteger64(vp->string);
1184 }
1185 }
1186
1187 /* Not reached */
1188 return 0;
1189 }
1190
1191 #endif /* BLD_FEATURE_INT64 */
1192 /******************************************************************************/
1193 /*
1194 * Convert the variable to a number type. Only works for primitive types.
1195 */
1196
ejsVarToNumber(EjsVar * vp)1197 EjsNum ejsVarToNumber(EjsVar *vp)
1198 {
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);
1203 #else
1204 return ejsVarToInteger(vp);
1205 #endif
1206 }
1207
1208 /******************************************************************************/
1209 /*
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.
1212 */
1213
ejsVarToString(Ejs * ep,EjsVar * vp)1214 char *ejsVarToString(Ejs *ep, EjsVar *vp)
1215 {
1216 MprBuf *bp;
1217 char numBuf[16];
1218 int len, i;
1219
1220 if (ep->castAlloc) {
1221 mprFree(ep->castTemp);
1222 }
1223 ep->castTemp = 0;
1224 ep->castAlloc = 0;
1225
1226 switch (vp->type) {
1227 case EJS_TYPE_UNDEFINED:
1228 ep->castTemp = "undefined";
1229 break;
1230
1231 case EJS_TYPE_NULL:
1232 ep->castTemp = "null";
1233 break;
1234
1235 case EJS_TYPE_PTR:
1236 len = mprAllocSprintf(MPR_LOC_ARGS(ep), &ep->castTemp, 0,
1237 "[Opaque Pointer %p]", vp->ptr.userPtr);
1238 ep->castAlloc = 1;
1239 break;
1240
1241 case EJS_TYPE_BOOL:
1242 if (vp->boolean) {
1243 ep->castTemp = "true";
1244 } else {
1245 ep->castTemp = "false";
1246 }
1247 break;
1248
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);
1253 ep->castAlloc = 1;
1254 break;
1255 #endif
1256
1257 case EJS_TYPE_INT:
1258 mprItoa(numBuf, sizeof(numBuf), vp->integer);
1259 ep->castTemp = mprStrdup(ep, numBuf);
1260 ep->castAlloc = 1;
1261 break;
1262
1263 #if BLD_FEATURE_INT64
1264 case EJS_TYPE_INT64:
1265 mprAllocSprintf(MPR_LOC_ARGS(ep), &ep->castTemp, 0,
1266 "%Ld", vp->integer64);
1267 ep->castAlloc = 1;
1268 break;
1269 #endif
1270
1271 case EJS_TYPE_CMETHOD:
1272 ep->castTemp = "[C Method]";
1273 break;
1274
1275 case EJS_TYPE_STRING_CMETHOD:
1276 ep->castTemp = "[C StringMethod]";
1277 break;
1278
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, ", ");
1286 }
1287 }
1288 mprPutStringToBuf(bp, ") {");
1289 mprPutStringToBuf(bp, vp->method.body);
1290 mprPutStringToBuf(bp, "}");
1291 mprAddNullToBuf(bp);
1292 ep->castTemp = mprStealBuf(ep, bp);
1293 ep->castAlloc = 1;
1294 mprFree(bp);
1295 break;
1296
1297 case EJS_TYPE_OBJECT:
1298 if (ejsRunMethod(ep, vp, "toString", 0) < 0) {
1299 return mprStrdup(ep, "[object Object]");
1300 }
1301 ep->castTemp = mprStrdup(ep, ep->result->string);
1302 ep->castAlloc = 1;
1303 break;
1304
1305 case EJS_TYPE_STRING:
1306 if (vp->string == 0) {
1307 ep->castTemp = "null";
1308 } else {
1309 ep->castTemp = vp->string;
1310 }
1311 break;
1312
1313 default:
1314 mprAssert(0);
1315 }
1316
1317 mprAssert(ep->castTemp);
1318 return ep->castTemp;
1319 }
1320
1321 /******************************************************************************/
1322
ejsVarToStringEx(Ejs * ep,EjsVar * vp,bool * alloc)1323 char *ejsVarToStringEx(Ejs *ep, EjsVar *vp, bool *alloc)
1324 {
1325 char *str;
1326
1327 mprAssert(alloc);
1328
1329 str = ejsVarToString(ep, vp);
1330 *alloc = ep->castAlloc;
1331 ep->castAlloc = 0;
1332 ep->castTemp = 0;
1333 return str;
1334 }
1335
1336 /******************************************************************************/
1337 /*
1338 * Parse a string based on formatting instructions and intelligently
1339 * create a variable.
1340 *
1341 * Float format: [+|-]DIGITS][DIGITS][(e|E)[+|-]DIGITS]
1342 */
1343
ejsParseVar(Ejs * ep,const char * buf,EjsType preferredType)1344 EjsVar *ejsParseVar(Ejs *ep, const char *buf, EjsType preferredType)
1345 {
1346 EjsType type;
1347 const char *cp;
1348 int isHex;
1349
1350 mprAssert(buf);
1351
1352 type = preferredType;
1353
1354 if (preferredType == EJS_TYPE_UNDEFINED) {
1355 isHex = 0;
1356 if (*buf == '-' || *buf == '+') {
1357 type = EJS_NUM_VAR;
1358
1359 } else if (!isdigit((int) *buf)) {
1360 if (strcmp(buf, "true") == 0 || strcmp(buf, "false") == 0) {
1361 type = EJS_TYPE_BOOL;
1362 } else {
1363 type = EJS_TYPE_STRING;
1364 }
1365
1366 } else if (isdigit((int) *buf)) {
1367 type = EJS_NUM_VAR;
1368 cp = buf;
1369 if (*cp && tolower(cp[1]) == 'x') {
1370 cp = &cp[2];
1371 isHex = 1;
1372 for (cp = buf; *cp; cp++) {
1373 if (! isxdigit((int) *cp)) {
1374 break;
1375 }
1376 }
1377 } else {
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;
1385 break;
1386 }
1387 }
1388 }
1389 #endif
1390 }
1391 }
1392 }
1393
1394 switch (type) {
1395 case EJS_TYPE_OBJECT:
1396 case EJS_TYPE_UNDEFINED:
1397 case EJS_TYPE_NULL:
1398 case EJS_TYPE_PTR:
1399 default:
1400 break;
1401
1402 case EJS_TYPE_BOOL:
1403 return ejsCreateBoolVar(ep, ejsParseBoolean(buf));
1404
1405 case EJS_TYPE_INT:
1406 return ejsCreateIntegerVar(ep, ejsParseInteger(buf));
1407
1408 #if BLD_FEATURE_INT64
1409 case EJS_TYPE_INT64:
1410 return ejsCreateInteger64Var(ep, ejsParseInteger64(buf));
1411 #endif
1412
1413 case EJS_TYPE_STRING:
1414 if (strcmp(buf, "null") == 0) {
1415 return ejsCreateNullVar(ep);
1416
1417 } else if (strcmp(buf, "undefined") == 0) {
1418 return ejsCreateUndefinedVar(ep);
1419 }
1420
1421 return ejsCreateStringVar(ep, buf);
1422
1423 #if BLD_FEATURE_FLOATING_POINT
1424 case EJS_TYPE_FLOAT:
1425 return ejsCreateFloatVar(ep, atof(buf));
1426 #endif
1427
1428 }
1429 return ejsCreateUndefinedVar(ep);
1430 }
1431
1432 /******************************************************************************/
1433 /*
1434 * Convert the variable to a number type. Only works for primitive types.
1435 */
1436
ejsParseBoolean(const char * s)1437 bool ejsParseBoolean(const char *s)
1438 {
1439 if (s == 0 || *s == '\0') {
1440 return 0;
1441 }
1442 if (strcmp(s, "false") == 0 || strcmp(s, "FALSE") == 0) {
1443 return 0;
1444 }
1445 return 1;
1446 }
1447
1448 /******************************************************************************/
1449 /*
1450 * Convert the variable to a number type. Only works for primitive types.
1451 */
1452
ejsParseNumber(const char * s)1453 EjsNum ejsParseNumber(const char *s)
1454 {
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);
1459 #else
1460 return ejsParseInteger(s);
1461 #endif
1462 }
1463
1464 /******************************************************************************/
1465 #if BLD_FEATURE_INT64
1466 /*
1467 * Convert the string buffer to an Integer64.
1468 */
1469
ejsParseInteger64(const char * str)1470 int64 ejsParseInteger64(const char *str)
1471 {
1472 const char *cp;
1473 int64 num64;
1474 int radix, c, negative;
1475
1476 mprAssert(str);
1477
1478 cp = str;
1479 num64 = 0;
1480 negative = 0;
1481
1482 if (*cp == '-') {
1483 cp++;
1484 negative = 1;
1485 } else if (*cp == '+') {
1486 cp++;
1487 }
1488
1489 /*
1490 * Parse a number. Observe hex and octal prefixes (0x, 0)
1491 */
1492 if (*cp != '0') {
1493 /*
1494 * Normal numbers (Radix 10)
1495 */
1496 while (isdigit((int) *cp)) {
1497 num64 = (*cp - '0') + (num64 * 10);
1498 cp++;
1499 }
1500 } else {
1501 cp++;
1502 if (tolower(*cp) == 'x') {
1503 cp++;
1504 radix = 16;
1505 while (*cp) {
1506 c = tolower(*cp);
1507 if (isdigit(c)) {
1508 num64 = (c - '0') + (num64 * radix);
1509 } else if (c >= 'a' && c <= 'f') {
1510 num64 = (c - 'a' + 10) + (num64 * radix);
1511 } else {
1512 break;
1513 }
1514 cp++;
1515 }
1516
1517 } else{
1518 radix = 8;
1519 while (*cp) {
1520 c = tolower(*cp);
1521 if (isdigit(c) && c < '8') {
1522 num64 = (c - '0') + (num64 * radix);
1523 } else {
1524 break;
1525 }
1526 cp++;
1527 }
1528 }
1529 }
1530
1531 if (negative) {
1532 return 0 - num64;
1533 }
1534 return num64;
1535 }
1536
1537 #endif /* BLD_FEATURE_INT64 */
1538 /******************************************************************************/
1539 /*
1540 * Convert the string buffer to an Integer.
1541 */
1542
ejsParseInteger(const char * str)1543 int ejsParseInteger(const char *str)
1544 {
1545 const char *cp;
1546 int num;
1547 int radix, c, negative;
1548
1549 mprAssert(str);
1550
1551 cp = str;
1552 num = 0;
1553 negative = 0;
1554
1555 if (*cp == '-') {
1556 cp++;
1557 negative = 1;
1558 } else if (*cp == '+') {
1559 cp++;
1560 }
1561
1562 /*
1563 * Parse a number. Observe hex and octal prefixes (0x, 0)
1564 */
1565 if (*cp != '0') {
1566 /*
1567 * Normal numbers (Radix 10)
1568 */
1569 while (isdigit((int) *cp)) {
1570 num = (*cp - '0') + (num * 10);
1571 cp++;
1572 }
1573 } else {
1574 cp++;
1575 if (tolower(*cp) == 'x') {
1576 cp++;
1577 radix = 16;
1578 while (*cp) {
1579 c = tolower(*cp);
1580 if (isdigit(c)) {
1581 num = (c - '0') + (num * radix);
1582 } else if (c >= 'a' && c <= 'f') {
1583 num = (c - 'a' + 10) + (num * radix);
1584 } else {
1585 break;
1586 }
1587 cp++;
1588 }
1589
1590 } else{
1591 radix = 8;
1592 while (*cp) {
1593 c = tolower(*cp);
1594 if (isdigit(c) && c < '8') {
1595 num = (c - '0') + (num * radix);
1596 } else {
1597 break;
1598 }
1599 cp++;
1600 }
1601 }
1602 }
1603
1604 if (negative) {
1605 return 0 - num;
1606 }
1607 return num;
1608 }
1609
1610 /******************************************************************************/
1611 #if BLD_FEATURE_FLOATING_POINT
1612 /*
1613 * Convert the string buffer to an Floating.
1614 */
1615
ejsParseFloat(const char * str)1616 double ejsParseFloat(const char *str)
1617 {
1618 return atof(str);
1619 }
1620
1621 /******************************************************************************/
1622
ejsIsNan(double f)1623 int ejsIsNan(double f)
1624 {
1625 #if WIN
1626 return _isnan(f);
1627 #elif VXWORKS
1628 /* FUTURE */
1629 return (0);
1630 #else
1631 return (f == FP_NAN);
1632 #endif
1633 }
1634 /******************************************************************************/
1635
ejsIsInfinite(double f)1636 int ejsIsInfinite(double f)
1637 {
1638 #if WIN
1639 return !_finite(f);
1640 #elif VXWORKS
1641 /* FUTURE */
1642 return (0);
1643 #else
1644 return (f == FP_INFINITE);
1645 #endif
1646 }
1647
1648 #endif /* BLD_FEATURE_FLOATING_POINT */
1649
1650 /******************************************************************************/
1651 /*
1652 * Single point of control for all assignment to properties.
1653 *
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.
1657 */
1658
copyVar(EJS_LOC_DEC (ep,loc),EjsVar * dest,const EjsVar * src,EjsCopyDepth copyDepth)1659 static EjsVar *copyVar(EJS_LOC_DEC(ep, loc), EjsVar *dest, const EjsVar *src,
1660 EjsCopyDepth copyDepth)
1661 {
1662 Ejs *ejsContext;
1663 EjsObj *srcObj;
1664 EjsProperty *destp;
1665 const char **srcArgs;
1666 char *str;
1667 int i;
1668
1669 mprAssert(dest);
1670 mprAssert(src);
1671
1672 if (dest == src) {
1673 return dest;
1674 }
1675
1676 if (dest->type != EJS_TYPE_UNDEFINED) {
1677 ejsClearVar(ep, dest);
1678 }
1679
1680 dest->allocatedData = 0;
1681
1682 switch (src->type) {
1683 default:
1684 case EJS_TYPE_UNDEFINED:
1685 case EJS_TYPE_NULL:
1686 break;
1687
1688 case EJS_TYPE_BOOL:
1689 dest->boolean = src->boolean;
1690 break;
1691
1692 case EJS_TYPE_PTR:
1693 dest->ptr = src->ptr;
1694 if (dest->ptr.destructor) {
1695 dest->allocatedData = 1;
1696 }
1697 break;
1698
1699 case EJS_TYPE_STRING_CMETHOD:
1700 dest->cMethodWithStrings = src->cMethodWithStrings;
1701 break;
1702
1703 case EJS_TYPE_CMETHOD:
1704 dest->cMethod = src->cMethod;
1705 break;
1706
1707 #if BLD_FEATURE_FLOATING_POINT
1708 case EJS_TYPE_FLOAT:
1709 dest->floating = src->floating;
1710 break;
1711 #endif
1712
1713 case EJS_TYPE_INT:
1714 dest->integer = src->integer;
1715 break;
1716
1717 #if BLD_FEATURE_INT64
1718 case EJS_TYPE_INT64:
1719 dest->integer64 = src->integer64;
1720 break;
1721 #endif
1722
1723 case EJS_TYPE_OBJECT:
1724 if (copyDepth == EJS_SHALLOW_COPY) {
1725
1726 /*
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.
1731 */
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;
1737 break;
1738 }
1739 }
1740
1741 /*
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.
1745 *
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.
1750 */
1751 if (dest->isProperty) {
1752 destp = ejsGetPropertyPtr(dest);
1753 if (destp->parentObj == 0) {
1754 ejsContext = ep;
1755
1756 } else {
1757 mprAssert(destp->parentObj);
1758 ejsContext = destp->parentObj->ejs;
1759 mprAssert(ejsContext);
1760 }
1761
1762 } else {
1763 ejsContext = ep;
1764 }
1765
1766 dest->objectState = createObj(EJS_LOC_PASS(ejsContext, loc));
1767 if (dest->objectState == 0) {
1768 /* Memory Error */
1769 return 0;
1770 }
1771
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);
1777
1778 if (dest->objectState->objName == 0) {
1779 return 0;
1780 }
1781
1782 if (ejsCopyProperties(ep, dest, src, copyDepth) == 0) {
1783 return 0;
1784 }
1785 dest->allocatedData = 1;
1786 break;
1787
1788 case EJS_TYPE_METHOD:
1789 dest->method.args = mprCreateItemArray(ep, EJS_INC_ARGS,
1790 EJS_MAX_ARGS);
1791 if (dest->method.args == 0) {
1792 return 0;
1793 }
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,
1799 loc), srcArgs[i]);
1800 if (str == 0) {
1801 mprFree(dest->method.args);
1802 dest->method.args = 0;
1803 return 0;
1804 }
1805 if (mprAddItem(dest->method.args, str) < 0) {
1806 mprFree(dest->method.args);
1807 dest->method.args = 0;
1808 return 0;
1809 }
1810 }
1811 }
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;
1816 return 0;
1817 }
1818 dest->callsSuper = src->callsSuper;
1819 break;
1820
1821 case EJS_TYPE_STRING:
1822 dest->length = src->length;
1823 if (src->string) {
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) {
1828 return 0;
1829 }
1830 dest->allocatedData = 1;
1831
1832 } else {
1833 dest->string = src->string;
1834 dest->allocatedData = 0;
1835 }
1836 break;
1837 }
1838
1839 dest->type = src->type;
1840 dest->flags = src->flags;
1841 dest->isArray = src->isArray;
1842
1843 return dest;
1844 }
1845
1846 /******************************************************************************/
1847 /*
1848 * Copy all properies in an object. Must preserve property order
1849 */
1850
ejsCopyProperties(Ejs * ep,EjsVar * dest,const EjsVar * src,EjsCopyDepth copyDepth)1851 EjsVar *ejsCopyProperties(Ejs *ep, EjsVar *dest, const EjsVar *src,
1852 EjsCopyDepth copyDepth)
1853 {
1854 EjsProperty *srcProp, *destProp, *last, *next;
1855 int propertyIndex;
1856
1857 srcProp = ejsGetFirstProperty(src, EJS_ENUM_ALL);
1858 while (srcProp) {
1859 next = ejsGetNextProperty(srcProp, EJS_ENUM_ALL);
1860 if (srcProp->visited) {
1861 srcProp = next;
1862 continue;
1863 }
1864
1865 /*
1866 * This finds the last variable in the hash chain
1867 * FUTURE OPT. This is slow. If used double link, we could locate the
1868 * tail more easily.
1869 */
1870 destProp = hashLookup(dest->objectState, srcProp->name,
1871 &propertyIndex, &last);
1872 mprAssert(destProp == 0);
1873
1874 destProp = allocProperty(ep, dest, srcProp->name, propertyIndex, last);
1875 if (destProp == 0) {
1876 mprAssert(destProp);
1877 return 0;
1878 }
1879
1880 /*
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.
1884 */
1885 srcProp->visited = 1;
1886
1887 if (copyVar(EJS_LOC_ARGS(ep), ejsGetVarPtr(destProp),
1888 ejsGetVarPtr(srcProp),
1889 (copyDepth == EJS_DEEP_COPY) ? EJS_SHALLOW_COPY : copyDepth)
1890 == 0) {
1891 return 0;
1892 }
1893 srcProp->visited = 0;
1894
1895 srcProp = next;
1896 }
1897 return dest;
1898 }
1899
1900 /******************************************************************************/
1901 /********************************** Properties ********************************/
1902 /******************************************************************************/
1903 /*
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
1907 */
1908
hashLookup(EjsObj * obj,const char * property,int * propertyIndex,EjsProperty ** hashTail)1909 static EjsProperty *hashLookup(EjsObj *obj, const char *property,
1910 int *propertyIndex, EjsProperty **hashTail)
1911 {
1912 EjsProperty *prop, *last;
1913 int index;
1914
1915 mprAssert(obj);
1916 mprAssert(property);
1917
1918 if (obj == 0 || property == 0 || *property == '\0') {
1919 mprAssert(0);
1920 return 0;
1921 }
1922
1923 /*
1924 * Find the property in the hash chain if it exists
1925 */
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) {
1931 break;
1932 }
1933 }
1934 if (propertyIndex) {
1935 *propertyIndex = index;
1936 }
1937 if (hashTail) {
1938 *hashTail = last;
1939 }
1940
1941 return prop;
1942 }
1943
1944 /******************************************************************************/
1945 /*
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.
1950 */
1951
ejsCreateSimpleProperty(Ejs * ep,EjsVar * op,const char * property)1952 EjsProperty *ejsCreateSimpleProperty(Ejs *ep, EjsVar *op, const char *property)
1953 {
1954 EjsProperty *prop, *last;
1955 int propertyIndex;
1956
1957 if (op == 0 || op->type != EJS_TYPE_OBJECT || property == 0 ||
1958 *property == '\0') {
1959 mprAssert(0);
1960 return 0;
1961 }
1962
1963 /*
1964 * Find the property in the hash chain if it exists
1965 */
1966 prop = hashLookup(op->objectState, property, &propertyIndex, &last);
1967
1968 if (prop == 0) {
1969 /*
1970 * Create a new property
1971 */
1972 prop = allocProperty(ep, op, property, propertyIndex, last);
1973 if (prop == 0) {
1974 mprAssert(prop == 0);
1975 return 0;
1976 }
1977 }
1978 return prop;
1979 }
1980
1981 /******************************************************************************/
1982 /*
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
1986 */
1987
ejsCreateSimpleNonUniqueProperty(Ejs * ep,EjsVar * op,const char * property)1988 EjsProperty *ejsCreateSimpleNonUniqueProperty(Ejs *ep, EjsVar *op,
1989 const char *property)
1990 {
1991 EjsProperty *prop, *last;
1992 int propertyIndex;
1993
1994 if (op == 0 || op->type != EJS_TYPE_OBJECT || property == 0 ||
1995 *property == '\0') {
1996 mprAssert(0);
1997 return 0;
1998 }
1999
2000 /*
2001 * Find end of chain
2002 */
2003 propertyIndex = hash(property);
2004 prop = op->objectState->propertyHash[propertyIndex];
2005 for (last = 0; prop != 0; last = prop, prop = prop->hashNext) {
2006 ;
2007 }
2008
2009 return allocProperty(ep, op, property, propertyIndex, last);
2010 }
2011
2012 /******************************************************************************/
2013 /*
2014 * Find a property in an object and return a pointer to it.
2015 * This does NOT traverse base classes.
2016 */
2017
ejsGetSimpleProperty(Ejs * ep,EjsVar * op,const char * property)2018 EjsProperty *ejsGetSimpleProperty(Ejs *ep, EjsVar *op, const char *property)
2019 {
2020 mprAssert(op);
2021 mprAssert(op->type == EJS_TYPE_OBJECT);
2022 mprAssert(property && *property);
2023
2024 /*
2025 * This is an internal API. It has very little checking.
2026 */
2027 return hashLookup(op->objectState, property, 0, 0);
2028 }
2029
2030 /******************************************************************************/
2031
2032 /*
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
2037 * classes
2038 */
2039
2040 /******************************************************************************/
2041 /******************************* Property Access ******************************/
2042 /******************************************************************************/
2043 /*
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.
2047 */
2048
2049 /*
2050 * Find a property in an object and return a pointer to it.
2051 * This follows base classes.
2052 */
2053
ejsGetProperty(Ejs * ep,EjsVar * op,const char * property)2054 EjsProperty *ejsGetProperty(Ejs *ep, EjsVar *op, const char *property)
2055 {
2056 EjsVar *vp, *newOp;
2057 int maxBaseClasses = 50;
2058
2059 do {
2060 if (op->type != EJS_TYPE_OBJECT) {
2061 mprAssert(op->type == EJS_TYPE_OBJECT);
2062 return 0;
2063 }
2064 mprAssert(op->objectState);
2065
2066 vp = ejsGetPropertyMethod(ep, op, property);
2067 if (vp != 0) {
2068 /*
2069 * Found
2070 */
2071 break;
2072 }
2073
2074 newOp = op->objectState->baseClass;
2075 if (newOp == 0) {
2076 if (op->objectState != ep->objectClass->objectState) {
2077 newOp = ep->objectClass;
2078 }
2079 }
2080 op = newOp;
2081
2082 /*
2083 * A little bit of sanity checking
2084 */
2085 if (--maxBaseClasses <= 0) {
2086 mprAssert(maxBaseClasses > 0);
2087 break;
2088 }
2089
2090 } while (op);
2091
2092 return ejsGetPropertyPtr(vp);
2093 }
2094
2095 /******************************************************************************/
2096 /*
2097 * Get the property's variable. Optionally create if it does not exist.
2098 */
2099
ejsGetPropertyAsVar(Ejs * ep,EjsVar * vp,const char * property)2100 EjsVar *ejsGetPropertyAsVar(Ejs *ep, EjsVar *vp, const char *property)
2101 {
2102 return ejsGetVarPtr(ejsGetProperty(ep, vp, property));
2103 }
2104
2105 /******************************************************************************/
2106 /*
2107 * Get the property's value as a binary string.
2108 */
2109
ejsGetPropertyAsBinaryString(Ejs * ep,EjsVar * obj,const char * property,int * length)2110 const uchar *ejsGetPropertyAsBinaryString(Ejs *ep, EjsVar *obj,
2111 const char *property, int *length)
2112 {
2113 EjsVar *vp;
2114
2115 vp = ejsGetVarPtr(ejsGetProperty(ep, obj, property));
2116 if (vp == 0 || ejsVarIsUndefined(vp)) {
2117 return 0;
2118 }
2119
2120 if (vp->type == EJS_TYPE_STRING) {
2121 if (length) {
2122 *length = vp->length;
2123 }
2124 return vp->ustring;
2125 }
2126 return 0;
2127 }
2128
2129 /******************************************************************************/
2130 /*
2131 * Get the property's value as a string.
2132 */
2133
ejsGetPropertyAsString(Ejs * ep,EjsVar * obj,const char * property)2134 const char *ejsGetPropertyAsString(Ejs *ep, EjsVar *obj, const char *property)
2135 {
2136 EjsVar *vp;
2137
2138 vp = ejsGetVarPtr(ejsGetProperty(ep, obj, property));
2139 if (vp == 0 || ejsVarIsUndefined(vp)) {
2140 return 0;
2141 }
2142
2143 if (vp->type == EJS_TYPE_STRING) {
2144 return vp->string;
2145 }
2146 return 0;
2147 }
2148
2149 /******************************************************************************/
2150 /*
2151 * Get the property's value as a number.
2152 */
2153
ejsGetPropertyAsNumber(Ejs * ep,EjsVar * obj,const char * property)2154 BLD_FEATURE_NUM_TYPE ejsGetPropertyAsNumber(Ejs *ep, EjsVar *obj,
2155 const char *property)
2156 {
2157 EjsVar *vp;
2158
2159 vp = ejsGetVarPtr(ejsGetProperty(ep, obj, property));
2160 if (vp == 0 || ejsVarIsUndefined(vp)) {
2161 return 0;
2162 }
2163
2164 return ejsVarToNumber(vp);
2165 }
2166
2167 /******************************************************************************/
2168 /*
2169 * Get the property's value as a integer.
2170 */
2171
ejsGetPropertyAsInteger(Ejs * ep,EjsVar * obj,const char * property)2172 int ejsGetPropertyAsInteger(Ejs *ep, EjsVar *obj, const char *property)
2173 {
2174 EjsVar *vp;
2175
2176 vp = ejsGetVarPtr(ejsGetProperty(ep, obj, property));
2177 if (vp == 0 || ejsVarIsUndefined(vp)) {
2178 return 0;
2179 }
2180
2181 return ejsVarToInteger(vp);
2182 }
2183
2184 /******************************************************************************/
2185 /*
2186 * Get the property's value as a boolean.
2187 */
2188
ejsGetPropertyAsBoolean(Ejs * ep,EjsVar * obj,const char * property)2189 bool ejsGetPropertyAsBoolean(Ejs *ep, EjsVar *obj, const char *property)
2190 {
2191 EjsVar *vp;
2192
2193 vp = ejsGetVarPtr(ejsGetProperty(ep, obj, property));
2194 if (vp == 0 || ejsVarIsUndefined(vp)) {
2195 return 0;
2196 }
2197
2198 return ejsVarToBoolean(vp);
2199 }
2200
2201 /******************************************************************************/
2202 /*
2203 * Get the property's value as a pointer.
2204 */
2205
ejsGetPropertyAsPtr(Ejs * ep,EjsVar * obj,const char * property)2206 void *ejsGetPropertyAsPtr(Ejs *ep, EjsVar *obj, const char *property)
2207 {
2208 EjsVar *vp;
2209
2210 vp = ejsGetVarPtr(ejsGetProperty(ep, obj, property));
2211 if (vp == 0 || ejsVarIsUndefined(vp)) {
2212 return 0;
2213 }
2214 if (vp->type == EJS_TYPE_PTR) {
2215 return vp->ptr.userPtr;
2216 }
2217 return 0;
2218 }
2219
2220 /******************************************************************************/
2221 /*
2222 * Create a property in the object. This will override any base class
2223 * properties.
2224 *
2225 * MOB -- need to spell out the difference between ejsSetProperty and
2226 * ejsCreateProperty.
2227 */
2228
ejsCreateProperty(Ejs * ep,EjsVar * obj,const char * property)2229 EjsProperty *ejsCreateProperty(Ejs *ep, EjsVar *obj, const char *property)
2230 {
2231 EjsVar *vp;
2232
2233 vp = ejsCreatePropertyMethod(ep, obj, property);
2234 return ejsGetPropertyPtr(vp);
2235 }
2236
2237 /******************************************************************************/
2238 /*
2239 * Set a property's variable value. Create the property if it does not exist.
2240 * This routine DOES follow base classes.
2241 */
2242
ejsSetBaseProperty(Ejs * ep,EjsVar * op,const char * property,const EjsVar * value)2243 EjsProperty *ejsSetBaseProperty(Ejs *ep, EjsVar *op, const char *property,
2244 const EjsVar *value)
2245 {
2246 EjsVar *vp, *newOp;
2247 int maxBaseClasses = 50;
2248
2249 do {
2250 if (op->type != EJS_TYPE_OBJECT) {
2251 mprAssert(op->type == EJS_TYPE_OBJECT);
2252 return 0;
2253 }
2254 mprAssert(op->objectState);
2255
2256 vp = ejsGetPropertyMethod(ep, op, property);
2257 if (vp != 0) {
2258 /*
2259 * Found
2260 */
2261 vp = ejsSetPropertyMethod(ep, op, property, value);
2262 break;
2263 }
2264
2265 newOp = op->objectState->baseClass;
2266 if (newOp == 0) {
2267 if (op->objectState != ep->objectClass->objectState) {
2268 newOp = ep->objectClass;
2269 }
2270 }
2271 op = newOp;
2272
2273 /*
2274 * A little bit of sanity checking
2275 */
2276 if (--maxBaseClasses <= 0) {
2277 mprAssert(maxBaseClasses > 0);
2278 break;
2279 }
2280
2281 } while (op);
2282
2283 return ejsGetPropertyPtr(vp);
2284 }
2285
2286 /******************************************************************************/
2287 /*
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.
2292 */
2293
ejsSetProperty(Ejs * ep,EjsVar * obj,const char * property,const EjsVar * value)2294 EjsProperty *ejsSetProperty(Ejs *ep, EjsVar *obj, const char *property,
2295 const EjsVar *value)
2296 {
2297 EjsVar *vp;
2298
2299 vp = ejsSetPropertyMethod(ep, obj, property, value);
2300
2301 return ejsGetPropertyPtr(vp);
2302 }
2303
2304 /******************************************************************************/
2305 /*
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.
2308 */
2309
ejsSetPropertyAndFree(Ejs * ep,EjsVar * obj,const char * property,EjsVar * value)2310 EjsProperty *ejsSetPropertyAndFree(Ejs *ep, EjsVar *obj,
2311 const char *property, EjsVar *value)
2312 {
2313 EjsVar *vp;
2314
2315 vp = ejsSetPropertyMethod(ep, obj, property, value);
2316
2317 ejsFree(ep, value, EJS_SLAB_VAR);
2318
2319 return ejsGetPropertyPtr(vp);
2320 }
2321
2322 /******************************************************************************/
2323
ejsSetPropertyToCMethod(Ejs * ep,EjsVar * vp,const char * prop,EjsCMethod fn,void * userData,int flags)2324 EjsProperty *ejsSetPropertyToCMethod(Ejs *ep, EjsVar *vp, const char *prop,
2325 EjsCMethod fn, void *userData, int flags)
2326 {
2327 EjsVar v;
2328
2329 ejsInitVar(&v, EJS_TYPE_CMETHOD);
2330 v.cMethod.fn = fn;
2331 v.cMethod.userData = userData;
2332 v.flags = flags;
2333
2334 return ejsSetProperty(ep, vp, prop, &v);
2335 }
2336
2337 /******************************************************************************/
2338
ejsSetPropertyToBoolean(Ejs * ep,EjsVar * vp,const char * prop,int value)2339 EjsProperty *ejsSetPropertyToBoolean(Ejs *ep, EjsVar *vp, const char *prop,
2340 int value)
2341 {
2342 EjsVar v;
2343
2344 ejsInitVar(&v, EJS_TYPE_BOOL);
2345 v.boolean = value;
2346
2347 return ejsSetProperty(ep, vp, prop, &v);
2348 }
2349
2350 /******************************************************************************/
2351 #if BLD_FEATURE_FLOATING_POINT
2352
ejsSetPropertyToFloat(Ejs * ep,EjsVar * vp,const char * prop,double value)2353 EjsProperty *ejsSetPropertyToFloat(Ejs *ep, EjsVar *vp, const char *prop,
2354 double value)
2355 {
2356 EjsVar v;
2357
2358 ejsInitVar(&v, EJS_TYPE_FLOAT);
2359 v.floating = value;
2360
2361 return ejsSetProperty(ep, vp, prop, &v);
2362 }
2363
2364 #endif
2365 /******************************************************************************/
2366
ejsSetPropertyToInteger(Ejs * ep,EjsVar * vp,const char * prop,int value)2367 EjsProperty *ejsSetPropertyToInteger(Ejs *ep, EjsVar *vp, const char *prop,
2368 int value)
2369 {
2370 EjsVar v;
2371
2372 ejsInitVar(&v, EJS_TYPE_INT);
2373 v.integer = value;
2374
2375 return ejsSetProperty(ep, vp, prop, &v);
2376 }
2377
2378 /******************************************************************************/
2379 #if BLD_FEATURE_INT64
2380
ejsSetPropertyToInteger64(Ejs * ep,EjsVar * vp,const char * prop,int64 value)2381 EjsProperty *ejsSetPropertyToInteger64(Ejs *ep, EjsVar *vp, const char *prop,
2382 int64 value)
2383 {
2384 EjsVar v;
2385
2386 ejsInitVar(&v, EJS_TYPE_INT64);
2387 v.integer64 = value;
2388
2389 return ejsSetProperty(ep, vp, prop, &v);
2390 }
2391
2392 #endif
2393 /******************************************************************************/
2394
ejsSetPropertyToNull(Ejs * ep,EjsVar * vp,const char * prop)2395 EjsProperty *ejsSetPropertyToNull(Ejs *ep, EjsVar *vp, const char *prop)
2396 {
2397 EjsVar v;
2398
2399 ejsInitVar(&v, EJS_TYPE_NULL);
2400
2401 return ejsSetProperty(ep, vp, prop, &v);
2402 }
2403
2404 /******************************************************************************/
2405
ejsSetPropertyToMethod(Ejs * ep,EjsVar * vp,const char * prop,const char * body,MprArray * args,int flags)2406 EjsProperty *ejsSetPropertyToMethod(Ejs *ep, EjsVar *vp, const char *prop,
2407 const char *body, MprArray *args, int flags)
2408 {
2409 return ejsSetPropertyAndFree(ep, vp, prop,
2410 ejsCreateMethodVar(ep, body, args, flags));
2411 }
2412
2413 /******************************************************************************/
2414
ejsSetPropertyToNumber(Ejs * ep,EjsVar * vp,const char * prop,EjsNum value)2415 EjsProperty *ejsSetPropertyToNumber(Ejs *ep, EjsVar *vp, const char *prop,
2416 EjsNum value)
2417 {
2418 return ejsSetPropertyAndFree(ep, vp, prop, ejsCreateNumberVar(ep, value));
2419 }
2420
2421 /******************************************************************************/
2422
ejsSetPropertyToStringCMethod(Ejs * ep,EjsVar * vp,const char * prop,EjsStringCMethod fn,void * userData,int flags)2423 EjsProperty *ejsSetPropertyToStringCMethod(Ejs *ep, EjsVar *vp,
2424 const char *prop, EjsStringCMethod fn, void *userData, int flags)
2425 {
2426 EjsVar v;
2427
2428 ejsInitVar(&v, EJS_TYPE_STRING_CMETHOD);
2429 v.cMethodWithStrings.fn = fn;
2430 v.cMethodWithStrings.userData = userData;
2431 v.flags = flags;
2432
2433 return ejsSetProperty(ep, vp, prop, &v);
2434 }
2435
2436 /******************************************************************************/
2437
ejsSetPropertyToString(Ejs * ep,EjsVar * vp,const char * prop,const char * value)2438 EjsProperty *ejsSetPropertyToString(Ejs *ep, EjsVar *vp, const char *prop,
2439 const char *value)
2440 {
2441 EjsProperty *pp;
2442 EjsVar v;
2443
2444 ejsInitVar(&v, EJS_TYPE_STRING);
2445
2446 /* FUTURE OPT */
2447 v.string = mprStrdupInternal(EJS_LOC_ARGS(ep), value);
2448 if (v.string == 0) {
2449 return 0;
2450 }
2451 v.length = strlen(v.string);
2452 v.allocatedData = 1;
2453
2454 pp = ejsSetProperty(ep, vp, prop, &v);
2455
2456 mprFree(v.string);
2457
2458 return pp;
2459 }
2460
2461 /******************************************************************************/
2462
ejsSetPropertyToBinaryString(Ejs * ep,EjsVar * vp,const char * prop,const uchar * value,int len)2463 EjsProperty *ejsSetPropertyToBinaryString(Ejs *ep, EjsVar *vp,
2464 const char *prop, const uchar *value, int len)
2465 {
2466 EjsProperty *pp;
2467 EjsVar v;
2468
2469 ejsInitVar(&v, EJS_TYPE_STRING);
2470
2471 /* FUTURE OPT */
2472 v.length = dupString(MPR_LOC_ARGS(ep), &v.ustring, value, len);
2473 if (v.length < 0) {
2474 return 0;
2475 }
2476 v.allocatedData = 1;
2477
2478 pp = ejsSetProperty(ep, vp, prop, &v);
2479
2480 mprFree(v.ustring);
2481
2482 return pp;
2483 }
2484
2485 /******************************************************************************/
2486
ejsSetPropertyToUndefined(Ejs * ep,EjsVar * vp,const char * prop)2487 EjsProperty *ejsSetPropertyToUndefined(Ejs *ep, EjsVar *vp, const char *prop)
2488 {
2489 EjsVar v;
2490
2491 ejsInitVar(&v, EJS_TYPE_UNDEFINED);
2492
2493 return ejsSetProperty(ep, vp, prop, &v);
2494 }
2495
2496 /******************************************************************************/
2497
ejsSetPropertyToPtr(Ejs * ep,EjsVar * vp,const char * prop,void * ptr,EjsDestructor destructor)2498 EjsProperty *ejsSetPropertyToPtr(Ejs *ep, EjsVar *vp, const char *prop,
2499 void *ptr, EjsDestructor destructor)
2500 {
2501 EjsVar v;
2502
2503 ejsInitVar(&v, EJS_TYPE_PTR);
2504 v.ptr.userPtr = ptr;
2505 v.ptr.destructor = destructor;
2506 v.allocatedData = 1;
2507
2508 return ejsSetProperty(ep, vp, prop, &v);
2509 }
2510
2511 /******************************************************************************/
2512
ejsSetPropertyToNewObj(Ejs * ep,EjsVar * vp,const char * prop,const char * className,MprArray * args)2513 EjsProperty *ejsSetPropertyToNewObj(Ejs *ep, EjsVar *vp, const char *prop,
2514 const char *className, MprArray *args)
2515 {
2516 return ejsSetPropertyAndFree(ep, vp, prop,
2517 ejsCreateObjUsingArgv(ep, 0, className, args));
2518 }
2519
2520 /******************************************************************************/
2521
ejsSetPropertyToObj(Ejs * ep,EjsVar * op,const char * prop)2522 EjsProperty *ejsSetPropertyToObj(Ejs *ep, EjsVar *op, const char *prop)
2523 {
2524 return ejsSetPropertyAndFree(ep, op, prop, ejsCreateObjVar(ep));
2525 }
2526
2527 /******************************************************************************/
2528 /*
2529 * Convenience routines
2530 */
2531
ejsSetPropertyToObjAsVar(Ejs * ep,EjsVar * op,const char * prop)2532 EjsVar *ejsSetPropertyToObjAsVar(Ejs *ep, EjsVar *op, const char *prop)
2533 {
2534 return ejsGetVarPtr(ejsSetPropertyToObj(ep, op, prop));
2535 }
2536
2537 /******************************************************************************/
2538 /******************************************************************************/
2539 /******************************************************************************/
2540 /******************************************************************************/
2541 /*
2542 * Create a script method
2543 */
2544
ejsDefineMethod(Ejs * ep,EjsVar * vp,const char * prop,const char * body,MprArray * args)2545 EjsProperty *ejsDefineMethod(Ejs *ep, EjsVar *vp, const char *prop,
2546 const char *body, MprArray *args)
2547 {
2548 if (vp == 0) {
2549 vp = ejsGetGlobalObj(ep);
2550 }
2551 return ejsSetPropertyToMethod(ep, vp, prop, body, args, 0);
2552 }
2553
2554 /******************************************************************************/
2555 /*
2556 * Create a C language method
2557 */
2558
ejsDefineCMethod(Ejs * ep,EjsVar * vp,const char * prop,EjsCMethod fn,int flags)2559 EjsProperty *ejsDefineCMethod(Ejs *ep, EjsVar *vp, const char *prop,
2560 EjsCMethod fn, int flags)
2561 {
2562 if (vp == 0) {
2563 vp = ejsGetGlobalObj(ep);
2564 }
2565 return ejsSetPropertyToCMethod(ep, vp, prop, fn, 0, flags);
2566 }
2567
2568 /******************************************************************************/
2569 /*
2570 * Define accessors
2571 */
2572
ejsDefineAccessors(Ejs * ep,EjsVar * vp,const char * prop,const char * getBody,const char * setBody)2573 EjsProperty *ejsDefineAccessors(Ejs *ep, EjsVar *vp, const char *prop,
2574 const char *getBody, const char *setBody)
2575 {
2576 EjsProperty *pp;
2577 MprArray *args;
2578 char *propName;
2579
2580 if (vp == 0) {
2581 vp = ejsGetGlobalObj(ep);
2582 }
2583
2584 if (ejsSetPropertyToMethod(ep, vp, prop, getBody, 0, EJS_GET_ACCESSOR) < 0){
2585 ejsMemoryError(ep);
2586 return 0;
2587 }
2588
2589 /* MOB -- OPT to use SLAB */
2590 /* MOB -- need to encapsulate this logic */
2591
2592 if (mprAllocStrcat(MPR_LOC_ARGS(ep), &propName, EJS_MAX_ID+5, 0,
2593 "-set-", prop, 0) < 0) {
2594 ejsMemoryError(ep);
2595 return 0;
2596 }
2597
2598 args = mprCreateItemArray(ep, EJS_INC_ARGS, EJS_MAX_ARGS);
2599 mprAddItem(args, mprStrdup(args, "value"));
2600
2601 pp = ejsSetPropertyToMethod(ep, vp, propName, setBody, args,
2602 EJS_SET_ACCESSOR);
2603 mprFree(propName);
2604
2605 if (pp == 0) {
2606 ejsMemoryError(ep);
2607 return 0;
2608 }
2609
2610 return pp;
2611 }
2612
2613 /******************************************************************************/
2614 /*
2615 * Define C accessors
2616 */
2617
ejsDefineCAccessors(Ejs * ep,EjsVar * vp,const char * prop,EjsCMethod getFn,EjsCMethod setFn,int flags)2618 EjsProperty *ejsDefineCAccessors(Ejs *ep, EjsVar *vp, const char *prop,
2619 EjsCMethod getFn, EjsCMethod setFn, int flags)
2620 {
2621 EjsProperty *pp;
2622 char *propName;
2623
2624 if (vp == 0) {
2625 vp = ejsGetGlobalObj(ep);
2626 }
2627 pp = ejsSetPropertyToCMethod(ep, vp, prop, getFn, 0,
2628 flags | EJS_GET_ACCESSOR);
2629 if (pp == 0) {
2630 ejsMemoryError(ep);
2631 return 0;
2632 }
2633
2634 /* MOB -- OPT to use SLAB */
2635 if (mprAllocStrcat(MPR_LOC_ARGS(ep), &propName, EJS_MAX_ID + 5, 0,
2636 "-set-", prop, 0) < 0) {
2637 ejsMemoryError(ep);
2638 return 0;
2639 }
2640 pp = ejsSetPropertyToCMethod(ep, vp, propName, setFn, 0,
2641 flags | EJS_SET_ACCESSOR);
2642 mprFree(propName);
2643
2644 if (pp == 0) {
2645 ejsMemoryError(ep);
2646 return 0;
2647 }
2648 return pp;
2649 }
2650
2651 /******************************************************************************/
2652 /*
2653 * Create a C language method with string arguments
2654 */
2655
ejsDefineStringCMethod(Ejs * ep,EjsVar * vp,const char * prop,EjsStringCMethod fn,int flags)2656 EjsProperty *ejsDefineStringCMethod(Ejs *ep, EjsVar *vp, const char *prop,
2657 EjsStringCMethod fn, int flags)
2658 {
2659 if (vp == 0) {
2660 vp = ejsGetGlobalObj(ep);
2661 }
2662 return ejsSetPropertyToStringCMethod(ep, vp, prop, fn, 0, flags);
2663 }
2664
2665 /******************************************************************************/
2666
ejsSetCMethodUserData(EjsVar * obj,void * userData)2667 void ejsSetCMethodUserData(EjsVar *obj, void *userData)
2668 {
2669 /*
2670 * This is a little dirty. We rely on the userData being in the same
2671 * place in the var structure.
2672 */
2673 obj->cMethod.userData = userData;
2674 }
2675
2676 /******************************************************************************/
2677
ejsSetVarFlags(EjsVar * obj,int flags)2678 void ejsSetVarFlags(EjsVar *obj, int flags)
2679 {
2680 obj->flags = flags;
2681 }
2682
2683 /******************************************************************************/
2684
ejsGetCMethodUserData(EjsVar * obj)2685 void *ejsGetCMethodUserData(EjsVar *obj)
2686 {
2687 return obj->cMethod.userData;
2688 }
2689
2690 /******************************************************************************/
2691
ejsGetVarFlags(EjsVar * obj)2692 int ejsGetVarFlags(EjsVar *obj)
2693 {
2694 return obj->flags;
2695 }
2696
2697 /******************************************************************************/
2698
ejsSetObjDestructor(Ejs * ep,EjsVar * obj,EjsDestructor destructor)2699 void ejsSetObjDestructor(Ejs *ep, EjsVar *obj, EjsDestructor destructor)
2700 {
2701 obj->objectState->destructor = destructor;
2702 }
2703
2704 /******************************************************************************/
2705
ejsClearObjDestructor(Ejs * ep,EjsVar * obj)2706 void ejsClearObjDestructor(Ejs *ep, EjsVar *obj)
2707 {
2708 obj->objectState->destructor = 0;
2709 }
2710
2711 /******************************************************************************/
2712 /*
2713 * Create a new property
2714 */
2715
allocProperty(Ejs * ep,EjsVar * op,const char * property,int propertyIndex,EjsProperty * last)2716 static EjsProperty *allocProperty(Ejs *ep, EjsVar *op, const char *property,
2717 int propertyIndex, EjsProperty *last)
2718 {
2719 EjsProperty *prop;
2720 EjsObj *obj;
2721
2722 obj = op->objectState;
2723
2724 /*
2725 * Allocate the property using the memory context of the owning object
2726 */
2727 prop = ejsAllocProperty(EJS_LOC_ARGS(obj->ejs));
2728 if (prop == 0) {
2729 return 0;
2730 }
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);
2735 return 0;
2736 }
2737
2738 ejsSetVarName(ep, ejsGetVarPtr(prop), &prop->name[0]);
2739
2740 /*
2741 * Do hash linkage
2742 */
2743 if (last) {
2744 last->hashNext = prop;
2745 } else {
2746 obj->propertyHash[propertyIndex] = prop;
2747 }
2748
2749 #if BLD_DEBUG
2750 prop->link.propertyName = prop->name;
2751 prop->link.property = prop;
2752 prop->link.head = &obj->link;
2753 #endif
2754
2755 /*
2756 * Inserting before the dummy head will append to the end
2757 */
2758 linkPropertyBefore(obj, &obj->link, &prop->link);
2759
2760 obj->numProperties++;
2761 prop->parentObj = obj;
2762 mprAssert(obj->ejs);
2763
2764 return prop;
2765 }
2766
2767 /******************************************************************************/
2768 /*
2769 * Delete a property from this object
2770 */
2771
ejsDeleteProperty(Ejs * ep,EjsVar * vp,const char * property)2772 int ejsDeleteProperty(Ejs *ep, EjsVar *vp, const char *property)
2773 {
2774 EjsProperty *prop, *last;
2775 EjsObj *obj;
2776 int propertyIndex;
2777
2778 mprAssert(vp);
2779 mprAssert(property && *property);
2780 mprAssert(vp->type == EJS_TYPE_OBJECT);
2781
2782 if (vp->type != EJS_TYPE_OBJECT) {
2783 mprAssert(vp->type == EJS_TYPE_OBJECT);
2784 return MPR_ERR_BAD_ARGS;
2785 }
2786
2787 prop = hashLookup(vp->objectState, property, &propertyIndex, &last);
2788 if (prop == (EjsProperty*) 0) {
2789 return MPR_ERR_NOT_FOUND;
2790 }
2791 obj = vp->objectState;
2792
2793 #if FUTURE
2794 if (prop->readonly) {
2795 mprAssert(! prop->readonly);
2796 return MPR_ERR_READ_ONLY;
2797 }
2798 #endif
2799
2800 /*
2801 * If doing enumerations, then the object will mark preventDelete to
2802 * prevent any properties being deleted and thus disturbing the
2803 * traversal.
2804 */
2805 if (obj->preventDeleteProp) {
2806 obj->delayedDeleteProp = 1;
2807 prop->delayedDelete = 1;
2808 return 0;
2809 }
2810
2811 /*
2812 * Remove from hash
2813 */
2814 if (last) {
2815 last->hashNext = prop->hashNext;
2816 } else {
2817 obj->propertyHash[propertyIndex] = prop->hashNext;
2818 }
2819
2820 unlinkProperty(obj, &prop->link);
2821 obj->numProperties--;
2822
2823 /*
2824 * Free any property data and return to the slab
2825 */
2826 if (prop->var.type != EJS_TYPE_OBJECT) {
2827 ejsClearVar(ep, ejsGetVarPtr(prop));
2828 }
2829 ejsFree(ep, prop, EJS_SLAB_PROPERTY);
2830
2831 return 0;
2832 }
2833
2834 /******************************************************************************/
2835 /*
2836 * Remove a property's value from this object. The property is set to
2837 * undefined.
2838 */
2839
ejsClearProperty(Ejs * ep,EjsVar * vp,const char * property)2840 EjsVar *ejsClearProperty(Ejs *ep, EjsVar *vp, const char *property)
2841 {
2842 EjsProperty *prop;
2843
2844 mprAssert(vp);
2845 mprAssert(property && *property);
2846 mprAssert(vp->type == EJS_TYPE_OBJECT);
2847
2848 if (vp->type != EJS_TYPE_OBJECT) {
2849 mprAssert(vp->type == EJS_TYPE_OBJECT);
2850 return 0;
2851 }
2852
2853 prop = hashLookup(vp->objectState, property, 0, 0);
2854 if (prop == (EjsProperty*) 0) {
2855 return 0;
2856 }
2857 #if FUTURE
2858 if (prop->readonly) {
2859 mprAssert(! prop->readonly);
2860 return 0;
2861 }
2862 #endif
2863
2864 ejsClearVar(ep, &prop->var);
2865 return &prop->var;
2866 }
2867
2868 /******************************************************************************/
2869 /*
2870 * Unlink a property from the ordered list of properties
2871 */
2872
unlinkProperty(EjsObj * obj,EjsPropLink * propLink)2873 static void unlinkProperty(EjsObj *obj, EjsPropLink *propLink)
2874 {
2875 propLink->prev->next = propLink->next;
2876 propLink->next->prev = propLink->prev;
2877 }
2878
2879 /******************************************************************************/
2880 #if UNUSED && KEEP
2881 /*
2882 * Insert a link after a specified link.
2883 */
2884
linkPropertyAfter(EjsObj * obj,EjsPropLink * at,EjsPropLink * propLink)2885 static void linkPropertyAfter(EjsObj *obj, EjsPropLink *at,
2886 EjsPropLink *propLink)
2887 {
2888 propLink->next = at->next;
2889 propLink->prev = at;
2890
2891 at->next->prev = propLink;
2892 at->next = propLink;
2893 }
2894
2895 #endif
2896 /******************************************************************************/
2897 /*
2898 * Insert a link before a specified link.
2899 */
2900
linkPropertyBefore(EjsObj * obj,EjsPropLink * at,EjsPropLink * propLink)2901 static void linkPropertyBefore(EjsObj *obj, EjsPropLink *at,
2902 EjsPropLink *propLink)
2903 {
2904 propLink->prev = at->prev;
2905 propLink->next = at;
2906
2907 at->prev->next = propLink;
2908 at->prev = propLink;
2909 }
2910
2911 /******************************************************************************/
2912 /*
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.
2918 *
2919 * NOTE: arrays keep their original index values.
2920 */
2921
ejsSortProperties(Ejs * ep,EjsVar * op,EjsSortFn fn,const char * propertyName,int order)2922 void ejsSortProperties(Ejs *ep, EjsVar *op, EjsSortFn fn,
2923 const char *propertyName, int order)
2924 {
2925 EjsProperty *p1, *p2, *tmp;
2926 EjsPropLink *l1, *l2, *oldL1Spot;
2927 EjsObj *obj;
2928
2929 obj = op->objectState;
2930
2931 p1 = ejsGetFirstProperty(op, 0);
2932 while (p1) {
2933 if (p1->dontEnumerate) {
2934 p1 = ejsGetNextProperty(p1, 0);
2935 continue;
2936 }
2937
2938 p2 = ejsGetFirstProperty(op, 0);
2939 while (p2 && p2 != p1) {
2940
2941 if (p2->dontEnumerate) {
2942 p2 = ejsGetNextProperty(p2, 0);
2943 continue;
2944 }
2945
2946 if (fn == 0) {
2947 if (propertyName) {
2948 fn = sortByProperty;
2949 } else {
2950 fn = sortAllProperties;
2951 }
2952 }
2953
2954 if (fn(ep, p1, p2, propertyName, order) < 0) {
2955
2956 l1 = &p1->link;
2957 l2 = &p2->link;
2958
2959 /*
2960 * Swap the properties without disturbing the hash chains.
2961 * l1 is always after l2 in the list. Unlink l1 and remember
2962 * the one after l1.
2963 */
2964 oldL1Spot = l1->next;
2965 unlinkProperty(obj, l1);
2966
2967 /*
2968 * Manually reinsert l1 by replacing l2 with l1. l2 is out of
2969 * the chain.
2970 */
2971 l2->prev->next = l1;
2972 l2->next->prev = l1;
2973 l1->prev = l2->prev;
2974 l1->next = l2->next;
2975
2976 /*
2977 * Reinsert l2 before the spot where l1 was.
2978 */
2979 linkPropertyBefore(obj, oldL1Spot, l2);
2980
2981 /*
2982 * Swap the pointers so we continue to traverse correctly
2983 */
2984 tmp = p1;
2985 p1 = p2;
2986 p2 = tmp;
2987 }
2988 p2 = ejsGetNextProperty(p2, 0);
2989 }
2990 p1 = ejsGetNextProperty(p1, 0);
2991 }
2992 }
2993
2994 /******************************************************************************/
2995 /*
2996 * Sort properties. Strings are sorted in ascending ASCII collating sequence
2997 * Numbers are sorted in increasing numerical order.
2998 */
sortAllProperties(Ejs * ep,EjsProperty * p1,EjsProperty * p2,const char * propertyName,int order)2999 static int sortAllProperties(Ejs *ep, EjsProperty *p1, EjsProperty *p2,
3000 const char *propertyName, int order)
3001 {
3002 EjsVar *v1, *v2;
3003 char *buf1, *buf2;
3004 int rc, buf1Alloc;
3005
3006 v1 = ejsGetVarPtr(p1);
3007 v2 = ejsGetVarPtr(p2);
3008
3009 if (v1->type == v2->type) {
3010 /* MOB -- should support Numbers */
3011 if (v1->type == EJS_TYPE_INT) {
3012 if (v1->integer < v2->integer) {
3013 return - order;
3014
3015 } else if (v1->integer == v2->integer) {
3016 return 0;
3017 }
3018 return order;
3019
3020 #if BLD_FEATURE_FLOATING_POINT
3021 } else if (v1->type == EJS_TYPE_FLOAT) {
3022 if (v1->floating < v2->floating) {
3023 return - order;
3024
3025 } else if (v1->floating == v2->floating) {
3026 return 0;
3027 }
3028 return order;
3029
3030 #endif
3031 } else if (v1->type == EJS_TYPE_STRING) {
3032 /* MOB -- need binary support ? */
3033 return strcmp(v1->string, v2->string) * order;
3034
3035 } else {
3036
3037 buf1 = ejsVarToStringEx(ep, v1, &buf1Alloc);
3038 buf2 = ejsVarToString(ep, v2);
3039
3040 rc = strcmp(buf1, buf2);
3041
3042 if (buf1Alloc) {
3043 mprFree(buf1);
3044 }
3045
3046 return rc * order;
3047 }
3048
3049 } else {
3050 /* Type mismatch in array */
3051 return 0;
3052 }
3053 return 0;
3054 }
3055
3056 /******************************************************************************/
3057 /*
3058 * Sort an object by a given property.
3059 */
sortByProperty(Ejs * ep,EjsProperty * p1,EjsProperty * p2,const char * propertyName,int order)3060 static int sortByProperty(Ejs *ep, EjsProperty *p1, EjsProperty *p2,
3061 const char *propertyName, int order)
3062 {
3063 EjsVar *o1, *o2, *v1, *v2;
3064 char *buf1, *buf2;
3065 int rc, buf1Alloc;
3066
3067 o1 = ejsGetVarPtr(p1);
3068 o2 = ejsGetVarPtr(p2);
3069
3070 if (!ejsVarIsObject(o1) || !ejsVarIsObject(o2)) {
3071 mprAssert(ejsVarIsObject(o1));
3072 mprAssert(ejsVarIsObject(o2));
3073 return 0;
3074 }
3075
3076 v1 = ejsGetPropertyAsVar(ep, o1, propertyName);
3077 v2 = ejsGetPropertyAsVar(ep, o2, propertyName);
3078
3079 if (v1 == 0 || v2 == 0) {
3080 /* Property name not found */
3081 return 0;
3082 }
3083
3084 if (v1->type != v2->type) {
3085 mprAssert(v1->type == v2->type);
3086 return 0;
3087 }
3088
3089 if (v1->type == v2->type) {
3090 /* MOB -- should support Numbers */
3091 if (v1->type == EJS_TYPE_INT) {
3092 if (v1->integer < v2->integer) {
3093 return -order;
3094
3095 } else if (v1->integer == v2->integer) {
3096 return 0;
3097 }
3098 return order;
3099
3100 #if BLD_FEATURE_FLOATING_POINT
3101 } else if (v1->type == EJS_TYPE_FLOAT) {
3102 if (v1->floating < v2->floating) {
3103 return -order;
3104
3105 } else if (v1->floating == v2->floating) {
3106 return 0;
3107 }
3108 return order;
3109
3110 #endif
3111 } else if (v1->type == EJS_TYPE_STRING) {
3112 /* MOB -- need binary support ? */
3113 return strcmp(v1->string, v2->string) * order;
3114
3115 } else {
3116 buf1 = ejsVarToStringEx(ep, v1, &buf1Alloc);
3117
3118 buf2 = ejsVarToString(ep, v2);
3119
3120 rc = strcmp(buf1, buf2);
3121
3122 if (buf1Alloc) {
3123 mprFree(buf1);
3124 }
3125
3126 return rc * order;
3127 }
3128
3129 } else {
3130 /* Type mismatch in array */
3131 return 0;
3132 }
3133 return 0;
3134 }
3135
3136 /******************************************************************************/
3137 /*
3138 * Set a property's name
3139 */
3140
ejsSetPropertyName(EjsProperty * pp,const char * property)3141 void ejsSetPropertyName(EjsProperty *pp, const char *property)
3142 {
3143 mprStrcpy(pp->name, sizeof(pp->name), property);
3144 }
3145
3146 /******************************************************************************/
3147
ejsMakePropertyEnumerable(EjsProperty * prop,int enumerate)3148 int ejsMakePropertyEnumerable(EjsProperty *prop, int enumerate)
3149 {
3150 int oldValue;
3151
3152 oldValue = prop->dontEnumerate;
3153 prop->dontEnumerate = !enumerate;
3154 return oldValue;
3155 }
3156
3157 /******************************************************************************/
3158
ejsMakePropertyPrivate(EjsProperty * prop,int isPrivate)3159 void ejsMakePropertyPrivate(EjsProperty *prop, int isPrivate)
3160 {
3161 prop->isPrivate = isPrivate;
3162 }
3163
3164 /******************************************************************************/
3165 /*
3166 * Make a variable read only. Can still be deleted.
3167 */
3168
ejsMakePropertyReadOnly(EjsProperty * prop,int readonly)3169 void ejsMakePropertyReadOnly(EjsProperty *prop, int readonly)
3170 {
3171 prop->readonly = readonly;
3172 }
3173
3174 /******************************************************************************/
3175
ejsMakeObjPermanent(EjsVar * vp,int permanent)3176 int ejsMakeObjPermanent(EjsVar *vp, int permanent)
3177 {
3178 int oldValue;
3179
3180 if (vp && vp->type == EJS_TYPE_OBJECT) {
3181 oldValue = vp->objectState->permanent;
3182 vp->objectState->permanent = permanent;
3183 } else {
3184 oldValue = 0;
3185 }
3186 return oldValue;
3187 }
3188
3189 /******************************************************************************/
3190
ejsMakeObjLive(EjsVar * vp,bool alive)3191 int ejsMakeObjLive(EjsVar *vp, bool alive)
3192 {
3193 int oldValue;
3194
3195 oldValue = 0;
3196 if (vp && vp->type == EJS_TYPE_OBJECT) {
3197 oldValue = vp->objectState->alive;
3198 vp->objectState->alive = alive;
3199 } else {
3200 oldValue = 0;
3201 }
3202 return oldValue;
3203 }
3204
3205 /******************************************************************************/
3206
ejsMakeClassNoConstructor(EjsVar * vp)3207 void ejsMakeClassNoConstructor(EjsVar *vp)
3208 {
3209 mprAssert(vp->type == EJS_TYPE_OBJECT);
3210
3211 if (vp->type == EJS_TYPE_OBJECT) {
3212 vp->objectState->noConstructor = 1;
3213 }
3214 }
3215
3216 /******************************************************************************/
3217 /*
3218 * Get the count of properties.
3219 */
3220
ejsGetPropertyCount(EjsVar * vp)3221 int ejsGetPropertyCount(EjsVar *vp)
3222 {
3223 EjsProperty *pp;
3224 EjsPropLink *lp, *head;
3225 int count;
3226
3227 mprAssert(vp);
3228
3229 if (vp->type != EJS_TYPE_OBJECT) {
3230 return 0;
3231 }
3232
3233 count = 0;
3234
3235 head = &vp->objectState->link;
3236 for (lp = head->next; lp != head; lp = lp->next) {
3237 pp = ejsGetPropertyFromLink(lp);
3238 if (! pp->dontEnumerate) {
3239 count++;
3240 }
3241 }
3242 return count;
3243 }
3244
3245 /******************************************************************************/
3246 /*
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
3249 * classes.
3250 */
3251
ejsGetFirstProperty(const EjsVar * op,int flags)3252 EjsProperty *ejsGetFirstProperty(const EjsVar *op, int flags)
3253 {
3254 EjsProperty *pp;
3255 EjsObj *obj;
3256 EjsPropLink *head, *lp;
3257
3258 mprAssert(op);
3259 mprAssert(op->type == EJS_TYPE_OBJECT);
3260
3261 if (op->type != EJS_TYPE_OBJECT) {
3262 mprAssert(op->type == EJS_TYPE_OBJECT);
3263 return 0;
3264 }
3265 pp = 0;
3266
3267 do {
3268 obj = op->objectState;
3269
3270 head = &obj->link;
3271 lp = head->next;
3272
3273 while (lp != head) {
3274 pp = ejsGetPropertyFromLink(lp);
3275 if (! pp->dontEnumerate || (flags & EJS_ENUM_HIDDEN)) {
3276 break;
3277 }
3278 lp = lp->next;
3279 }
3280 if (lp != head || op->type != EJS_TYPE_OBJECT ||
3281 !(flags & EJS_ENUM_CLASSES)) {
3282 break;
3283 }
3284
3285 op = obj->baseClass;
3286
3287 } while (lp == 0 && op);
3288
3289 return pp;
3290 }
3291
3292 /******************************************************************************/
3293 /*
3294 * Get the next property in sequence. This will only enumerate properties in
3295 * this class and not in base classes.
3296 */
3297
ejsGetNextProperty(EjsProperty * last,int flags)3298 EjsProperty *ejsGetNextProperty(EjsProperty *last, int flags)
3299 {
3300 EjsProperty *pp;
3301 EjsObj *obj;
3302 EjsPropLink *lp, *head;
3303
3304 obj = last->parentObj;
3305
3306 lp = last->link.next;
3307 head = &obj->link;
3308 pp = 0;
3309
3310 while (obj) {
3311 while (lp != head) {
3312 pp = ejsGetPropertyFromLink(lp);
3313 if (! pp->dontEnumerate || (flags & EJS_ENUM_HIDDEN)) {
3314 break;
3315 }
3316 lp = lp->next;
3317 }
3318 if (lp != head || !(flags & EJS_ENUM_CLASSES)) {
3319 break;
3320 }
3321
3322 /*
3323 * Now iterate over properties in base classes (down the chain)
3324 */
3325 if (obj->baseClass == 0) {
3326 break;
3327 }
3328
3329 obj = obj->baseClass->objectState;
3330 if (obj == 0) {
3331 break;
3332 }
3333 }
3334 return pp;
3335 }
3336
3337 /******************************************************************************/
3338 /*
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
3344 */
3345
ejsFindProperty(Ejs * ep,EjsVar ** obj,char ** property,EjsVar * global,EjsVar * local,const char * fullName,int create)3346 EjsVar *ejsFindProperty(Ejs *ep, EjsVar **obj, char **property, EjsVar *global,
3347 EjsVar *local, const char *fullName, int create)
3348 {
3349 EjsProperty *currentProp;
3350 EjsVar *currentObj;
3351 /* MOB -- WARNING BIG */
3352 char tokBuf[EJS_MAX_ID], propertyName[EJS_MAX_ID];
3353 char *token, *next, *cp, *endp;
3354
3355 mprAssert(fullName && *fullName);
3356
3357 currentProp = 0;
3358 currentObj = 0;
3359
3360 if (global == 0) {
3361 global = ep->global;
3362 }
3363
3364 if (obj) {
3365 *obj = 0;
3366 }
3367 if (property) {
3368 *property = 0;
3369 }
3370
3371 if (fullName == 0) {
3372 return 0;
3373 }
3374
3375 next = (char*) fullName;
3376 token = getNextVarToken(&next, tokBuf, sizeof(tokBuf));
3377 mprStrcpy(propertyName, sizeof(propertyName), token);
3378
3379 if (local) {
3380 currentProp = ejsGetProperty(ep, local, token);
3381 currentObj = local;
3382 }
3383 if (currentProp == 0) {
3384 currentProp = ejsGetProperty(ep, global, token);
3385 currentObj = global;
3386 }
3387
3388 token = getNextVarToken(&next, tokBuf, sizeof(tokBuf));
3389
3390 while (currentObj != 0 && token != 0 && *token) {
3391
3392 if (currentProp == 0) {
3393 return 0;
3394 }
3395 currentObj = ¤tProp->var;
3396 currentProp = 0;
3397
3398 if (*token == '[') {
3399 token = getNextVarToken(&next, tokBuf, sizeof(tokBuf));
3400
3401 mprStrcpy(propertyName, sizeof(propertyName), token);
3402 cp = propertyName;
3403 if (*cp == '\"') {
3404 cp++;
3405 if ((endp = strchr(cp, '\"')) != 0) {
3406 *endp = '\0';
3407 }
3408 } else if (*cp == '\'') {
3409 cp++;
3410 if ((endp = strchr(cp, '\'')) != 0) {
3411 *endp = '\0';
3412 }
3413 }
3414
3415 currentProp = ejsGetProperty(ep, currentObj, propertyName);
3416
3417 token = getNextVarToken(&next, tokBuf, sizeof(tokBuf));
3418 if (*token != ']') {
3419 return 0;
3420 }
3421
3422 } else if (*token == '.') {
3423 token = getNextVarToken(&next, tokBuf, sizeof(tokBuf));
3424 if (!isalpha((int) token[0]) &&
3425 token[0] != '_' && token[0] != '$') {
3426 return 0;
3427 }
3428
3429 mprStrcpy(propertyName, sizeof(propertyName), token);
3430 currentProp = ejsGetProperty(ep, currentObj, token);
3431
3432 } else {
3433 currentProp = ejsGetProperty(ep, currentObj, token);
3434 }
3435
3436 if (next == 0 || *next == '\0') {
3437 break;
3438 }
3439 token = getNextVarToken(&next, tokBuf, sizeof(tokBuf));
3440 }
3441
3442 if (obj) {
3443 *obj = currentObj;
3444 }
3445
3446
3447 if (currentProp == 0 && currentObj >= 0 && create) {
3448 currentProp = ejsCreateSimpleProperty(ep, currentObj, propertyName);
3449 }
3450
3451 if (property) {
3452 *property = currentProp->name;
3453 }
3454 return ejsGetVarPtr(currentProp);
3455 }
3456
3457 /******************************************************************************/
3458 /*
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.
3462 */
3463
getNextVarToken(char ** next,char * tokBuf,int tokBufLen)3464 static char *getNextVarToken(char **next, char *tokBuf, int tokBufLen)
3465 {
3466 char *start, *cp;
3467 int len;
3468
3469 start = *next;
3470 while (isspace((int) *start) || *start == '\n' || *start == '\r') {
3471 start++;
3472 }
3473 cp = start;
3474
3475 if (*cp == '.' || *cp == '[' || *cp == ']') {
3476 cp++;
3477 } else {
3478 while (*cp && *cp != '.' && *cp != '[' && *cp != ']' &&
3479 !isspace((int) *cp) && *cp != '\n' && *cp != '\r') {
3480 cp++;
3481 }
3482 }
3483 len = mprMemcpy(tokBuf, tokBufLen - 1, start, cp - start);
3484 tokBuf[len] = '\0';
3485
3486 *next = cp;
3487 return tokBuf;
3488 }
3489
3490 /******************************************************************************/
3491
ejsGetGlobalClass(Ejs * ep)3492 EjsVar *ejsGetGlobalClass(Ejs *ep)
3493 {
3494 return ep->global;
3495 }
3496
3497 /******************************************************************************/
3498 /*************************** Property Access Methods **************************/
3499 /******************************************************************************/
3500 /*
3501 * Create an undefined property. This routine calls the object method hooks.
3502 */
3503
3504 /* MOB -- better suffix than "Method" */
ejsCreatePropertyMethod(Ejs * ep,EjsVar * op,const char * property)3505 EjsVar *ejsCreatePropertyMethod(Ejs *ep, EjsVar *op, const char *property)
3506 {
3507 EjsVar *vp;
3508
3509 mprAssert(ep);
3510 mprAssert(op);
3511 mprAssert(property && *property);
3512
3513 if (op == 0) {
3514 return 0;
3515 }
3516
3517 mprAssert(op->type == EJS_TYPE_OBJECT);
3518 mprAssert(op->objectState);
3519
3520 if (op->objectState == 0) {
3521 return 0;
3522 }
3523
3524 if (op->objectState->methods == 0) {
3525 vp = ejsGetVarPtr(ejsCreateSimpleProperty(ep, op, property));
3526 } else {
3527 vp = (op->objectState->methods->createProperty)(ep, op, property);
3528 }
3529
3530 if (vp == 0) {
3531 mprAssert(vp);
3532 op->objectState->hasErrors = 1;
3533 return 0;
3534 }
3535
3536 /*
3537 * FUTURE - find a better way.
3538 */
3539 if (op->isArray) {
3540 ejsSetArrayLength(ep, op, property, 0, 0);
3541 }
3542 return vp;
3543 }
3544
3545 /******************************************************************************/
3546
ejsDeletePropertyMethod(Ejs * ep,EjsVar * op,const char * property)3547 int ejsDeletePropertyMethod(Ejs *ep, EjsVar *op, const char *property)
3548 {
3549 int rc;
3550
3551 mprAssert(ep);
3552 mprAssert(op);
3553 mprAssert(property && *property);
3554
3555 if (op == 0) {
3556 return -1;
3557 }
3558
3559 mprAssert(op->type == EJS_TYPE_OBJECT);
3560 mprAssert(op->objectState);
3561
3562 if (op->objectState == 0) {
3563 return -1;
3564 }
3565
3566 if (op->objectState->methods == 0) {
3567 rc = ejsDeleteProperty(ep, op, property);
3568 } else {
3569 rc = (op->objectState->methods->deleteProperty)(ep, op, property);
3570 }
3571
3572 if (rc < 0) {
3573 op->objectState->hasErrors = 1;
3574 }
3575
3576 op->objectState->dirty = 1;
3577
3578 return rc;
3579 }
3580
3581 /******************************************************************************/
3582 /*
3583 * Set the value of a property. Create if it does not exist
3584 * If the object has property accessor methods defined, use those.
3585 */
3586
ejsSetPropertyMethod(Ejs * ep,EjsVar * op,const char * property,const EjsVar * value)3587 EjsVar *ejsSetPropertyMethod(Ejs *ep, EjsVar *op, const char *property,
3588 const EjsVar *value)
3589 {
3590 EjsVar *vp;
3591
3592 mprAssert(ep);
3593 mprAssert(op);
3594 mprAssert(property && *property);
3595 mprAssert(value);
3596
3597 if (op == 0) {
3598 return 0;
3599 }
3600
3601 mprAssert(op->type == EJS_TYPE_OBJECT);
3602 mprAssert(op->objectState);
3603
3604 if (op->objectState == 0) {
3605 return 0;
3606 }
3607
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) {
3611 mprAssert(0);
3612 op->objectState->hasErrors = 1;
3613 return 0;
3614 }
3615
3616 } else {
3617 vp = (op->objectState->methods->setProperty)(ep, op, property, value);
3618 }
3619
3620 if (vp == 0) {
3621 mprAssert(vp);
3622 op->objectState->hasErrors = 1;
3623 return 0;
3624 }
3625
3626 if (vp->type == EJS_TYPE_OBJECT) {
3627 /*
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().
3632 */
3633 if (op->objectState != vp->objectState) {
3634 vp->objectState->alive = 1;
3635 }
3636 #if BLD_DEBUG
3637 {
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]);
3642 }
3643 }
3644 #endif
3645 }
3646
3647 /*
3648 * Trap assignments to array.length. MOB - find a better way.
3649 */
3650 if (vp->isArrayLength) {
3651 ejsSetArrayLength(ep, op, 0, 0, value);
3652 }
3653
3654 op->objectState->dirty = 1;
3655
3656 return vp;
3657 }
3658
3659 /******************************************************************************/
3660
ejsGetPropertyMethod(Ejs * ep,EjsVar * op,const char * property)3661 EjsVar *ejsGetPropertyMethod(Ejs *ep, EjsVar *op, const char *property)
3662 {
3663 mprAssert(ep);
3664 mprAssert(op);
3665 mprAssert(property && *property);
3666
3667 if (op == 0) {
3668 return 0;
3669 }
3670
3671 mprAssert(op->type == EJS_TYPE_OBJECT);
3672 mprAssert(op->objectState);
3673
3674 if (op->objectState == 0) {
3675 return 0;
3676 }
3677
3678 if (op->objectState->methods == 0) {
3679 return ejsGetVarPtr(ejsGetSimpleProperty(ep, op, property));
3680 } else {
3681 return (op->objectState->methods->getProperty)(ep, op, property);
3682 }
3683 }
3684
3685 /******************************************************************************/
3686 /*************************** Advisory Locking Support *************************/
3687 /******************************************************************************/
3688 #if BLD_FEATURE_MULTITHREAD
3689
ejsLockObj(EjsVar * vp)3690 void ejsLockObj(EjsVar *vp)
3691 {
3692 mprAssert(vp);
3693 mprAssert(vp->type == EJS_TYPE_OBJECT);
3694 mprAssert(vp->objectState);
3695
3696 if (vp->objectState->mutex == 0) {
3697 vp->objectState->mutex = mprCreateLock(vp->objectState->ejs);
3698 }
3699 mprLock(vp->objectState->mutex);
3700 }
3701
3702 /******************************************************************************/
3703
ejsUnlockObj(EjsVar * vp)3704 void ejsUnlockObj(EjsVar *vp)
3705 {
3706 mprAssert(vp);
3707 mprAssert(vp->type == EJS_TYPE_OBJECT);
3708 mprAssert(vp->objectState);
3709
3710 if (vp->objectState->mutex) {
3711 mprUnlock(vp->objectState->mutex);
3712 }
3713 }
3714
3715 #endif
3716 /******************************************************************************/
3717 /************************** Internal Support Routines *************************/
3718 /******************************************************************************/
3719 /*
3720 * Create an object.
3721 */
3722
createObj(EJS_LOC_DEC (ep,loc))3723 static EjsObj *createObj(EJS_LOC_DEC(ep, loc))
3724 {
3725 EjsObj *op;
3726 EjsPropLink *lp;
3727
3728 op = (EjsObj*) ejsAllocObj(EJS_LOC_PASS(ep, loc));
3729 if (op == NULL) {
3730 return 0;
3731 }
3732
3733 /*
3734 * The objectState holds the dummy head for the ordered list of properties
3735 */
3736 lp = &op->link;
3737 lp->next = lp->prev = lp;
3738
3739 #if BLD_DEBUG
3740 /*
3741 * This makes it much easier to debug the list
3742 */
3743 lp->head = lp;
3744 lp->propertyName = "dummyHead";
3745 #endif
3746
3747 return op;
3748 }
3749
3750 /******************************************************************************/
3751 /*
3752 * Destroy an object. Called by the garbage collector if there are no more
3753 * references to an object.
3754 */
3755
ejsDestroyObj(Ejs * ep,EjsObj * obj)3756 int ejsDestroyObj(Ejs *ep, EjsObj *obj)
3757 {
3758 EjsProperty *pp;
3759 EjsPropLink *lp, *head, *nextLink;
3760
3761 mprAssert(obj);
3762
3763 if (obj->destructor) {
3764 EjsVar v;
3765 memset(&v, 0, sizeof(v));
3766 v.type = EJS_TYPE_OBJECT;
3767 v.objectState = obj;
3768 ejsSetVarName(ep, &v, "destructor");
3769
3770 #if BLD_FEATURE_ALLOC_LEAK_TRACK
3771 v.gc.allocatedBy = "static";
3772 #endif
3773
3774 if ((obj->destructor)(ep, &v) < 0) {
3775 return -1;
3776 }
3777 }
3778 mprFree(obj->objName);
3779 obj->objName = 0;
3780
3781 /*
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
3784 * won't be reset.
3785 */
3786 obj->gcMarked = 0;
3787 obj->visited = 0;
3788
3789 head = &obj->link;
3790 for (lp = head->next; lp != head; lp = nextLink) {
3791
3792 pp = ejsGetPropertyFromLink(lp);
3793 nextLink = lp->next;
3794
3795 /*
3796 * We don't unlink as we are destroying all properties.
3797 * If an object, we don't need to clear either.
3798 */
3799 if (pp->var.type != EJS_TYPE_OBJECT) {
3800 ejsClearVar(ep, ejsGetVarPtr(pp));
3801 }
3802 ejsFree(ep, pp, EJS_SLAB_PROPERTY);
3803 }
3804
3805 #if BLD_FEATURE_MULTITHREAD
3806 if (obj->mutex) {
3807 mprDestroyLock(obj->mutex);
3808 }
3809 #endif
3810
3811 ejsFree(ep, obj, EJS_SLAB_OBJ);
3812 return 0;
3813 }
3814
3815 /******************************************************************************/
3816 /*
3817 * Fast hash. The history of this algorithm is part of lost computer science
3818 * folk lore.
3819 */
3820
hash(const char * property)3821 static int hash(const char *property)
3822 {
3823 uint sum;
3824
3825 mprAssert(property);
3826
3827 sum = 0;
3828 while (*property) {
3829 sum += (sum * 33) + *property++;
3830 }
3831
3832 return sum % EJS_OBJ_HASH_SIZE;
3833 }
3834
3835 /******************************************************************************/
3836 /*
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).
3842 */
3843
ejsSetArrayLength(Ejs * ep,EjsVar * obj,const char * create,const char * delete,const EjsVar * setLength)3844 void ejsSetArrayLength(Ejs *ep, EjsVar *obj, const char *create,
3845 const char *delete, const EjsVar *setLength)
3846 {
3847 EjsVar *vp;
3848 char idx[16];
3849 int oldSize, newSize, i;
3850
3851 vp = ejsGetPropertyAsVar(ep, obj, "length");
3852 oldSize = vp->integer;
3853 newSize = oldSize;
3854
3855 if (create) {
3856 if (isdigit(*create)) {
3857 i = atoi(create);
3858 newSize = max(i + 1, oldSize);
3859 }
3860 } else if (delete) {
3861 if (isdigit(*delete)) {
3862 i = atoi(delete);
3863 newSize = (i == (oldSize - 1) ? oldSize - 1 : oldSize);
3864 }
3865 } else {
3866 newSize = setLength->integer;
3867 }
3868
3869 for (i = newSize; i < oldSize; i++) {
3870 mprItoa(idx, sizeof(idx), i);
3871 ejsDeleteProperty(ep, obj, idx);
3872 }
3873
3874 if (ejsWriteVarAsInteger(ep, vp, newSize) == 0) {
3875 mprAssert(0);
3876 }
3877 }
3878
3879 /******************************************************************************/
3880
ejsClearObjErrors(EjsVar * vp)3881 void ejsClearObjErrors(EjsVar *vp)
3882 {
3883 if (vp == 0 || vp->type != EJS_TYPE_OBJECT || vp->objectState == 0) {
3884 mprAssert(0);
3885 return;
3886 }
3887 vp->objectState->hasErrors = 0;
3888 }
3889
3890 /******************************************************************************/
3891
ejsObjHasErrors(EjsVar * vp)3892 int ejsObjHasErrors(EjsVar *vp)
3893 {
3894 if (vp == 0 || vp->type != EJS_TYPE_OBJECT || vp->objectState == 0) {
3895 mprAssert(0);
3896 return -1;
3897 }
3898 return vp->objectState->hasErrors;
3899 }
3900
3901 /******************************************************************************/
3902
ejsIsObjDirty(EjsVar * vp)3903 bool ejsIsObjDirty(EjsVar *vp)
3904 {
3905 mprAssert(vp->type == EJS_TYPE_OBJECT && vp->objectState);
3906
3907 if (vp->type == EJS_TYPE_OBJECT && vp->objectState) {
3908 return vp->objectState->dirty;
3909 }
3910 return 0;
3911 }
3912
3913 /******************************************************************************/
3914
ejsResetObjDirtyBit(EjsVar * vp)3915 void ejsResetObjDirtyBit(EjsVar *vp)
3916 {
3917 mprAssert(vp->type == EJS_TYPE_OBJECT && vp->objectState);
3918
3919 if (vp->type == EJS_TYPE_OBJECT && vp->objectState) {
3920 vp->objectState->dirty = 0;
3921 }
3922 }
3923
3924 /******************************************************************************/
3925 /*
3926 * Copy a string. Always null terminate.
3927 */
3928
dupString(MPR_LOC_DEC (ctx,loc),uchar ** dest,const void * src,int nbytes)3929 static int dupString(MPR_LOC_DEC(ctx, loc), uchar **dest, const void *src,
3930 int nbytes)
3931 {
3932 mprAssert(dest);
3933 mprAssert(src);
3934
3935 if (nbytes > 0) {
3936 *dest = mprMemdupInternal(MPR_LOC_PASS(ctx, loc), src, nbytes + 1);
3937 if (*dest == 0) {
3938 return MPR_ERR_MEMORY;
3939 }
3940
3941 } else {
3942 *dest = (uchar*) mprAlloc(ctx, 1);
3943 nbytes = 0;
3944 }
3945
3946 (*dest)[nbytes] = '\0';
3947
3948 return nbytes;
3949 }
3950
3951 /******************************************************************************/
3952
ejsGetVarTypeAsString(EjsVar * vp)3953 const char *ejsGetVarTypeAsString(EjsVar *vp)
3954 {
3955 switch (vp->type) {
3956 default:
3957 case EJS_TYPE_UNDEFINED:
3958 return "undefined";
3959 case EJS_TYPE_NULL:
3960 return "null";
3961 case EJS_TYPE_BOOL:
3962 return "bool";
3963 case EJS_TYPE_CMETHOD:
3964 return "cmethod";
3965 case EJS_TYPE_FLOAT:
3966 return "float";
3967 case EJS_TYPE_INT:
3968 return "int";
3969 case EJS_TYPE_INT64:
3970 return "int64";
3971 case EJS_TYPE_OBJECT:
3972 return "object";
3973 case EJS_TYPE_METHOD:
3974 return "method";
3975 case EJS_TYPE_STRING:
3976 return "string";
3977 case EJS_TYPE_STRING_CMETHOD:
3978 return "string method";
3979 case EJS_TYPE_PTR:
3980 return "ptr";
3981 }
3982 }
3983
3984 /******************************************************************************/
3985
ejsGetVarUserPtr(EjsVar * vp)3986 void *ejsGetVarUserPtr(EjsVar *vp)
3987 {
3988 mprAssert(vp);
3989 mprAssert(vp->type == EJS_TYPE_PTR);
3990
3991 if (!ejsVarIsPtr(vp)) {
3992 return 0;
3993 }
3994 return vp->ptr.userPtr;
3995 }
3996
3997 /******************************************************************************/
3998
ejsSetVarUserPtr(EjsVar * vp,void * data)3999 void ejsSetVarUserPtr(EjsVar *vp, void *data)
4000 {
4001 mprAssert(vp);
4002 mprAssert(vp->type == EJS_TYPE_PTR);
4003
4004 vp->ptr.userPtr = data;
4005 }
4006
4007 /******************************************************************************/
4008 /*
4009 * Return TRUE if target is a subclass (or the same class) as baseClass.
4010 */
4011
ejsIsSubClass(EjsVar * target,EjsVar * baseClass)4012 bool ejsIsSubClass(EjsVar *target, EjsVar *baseClass)
4013 {
4014 do {
4015 if (target->objectState == baseClass->objectState) {
4016 return 1;
4017 }
4018 target = target->objectState->baseClass;
4019 } while (target);
4020
4021 return 0;
4022 }
4023
4024 /******************************************************************************/
4025 /*
4026 * Local variables:
4027 * tab-width: 4
4028 * c-basic-offset: 4
4029 * End:
4030 * vim:tw=78
4031 * vim600: sw=4 ts=4 fdm=marker
4032 * vim<600: sw=4 ts=4
4033 */
4034