Let winbind depend directly on libnet rather than through the ejs bindings.
[samba.git] / source4 / lib / appweb / ejs-2.0 / ejs / ejsVar.c
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
80 EjsType ejsGetVarType(EjsVar *vp)
81 {
82         mprAssert(vp);
83
84         return vp->type;
85 }
86
87 /******************************************************************************/
88
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
1616 double ejsParseFloat(const char *str)
1617 {
1618         return atof(str);
1619 }
1620
1621 /******************************************************************************/
1622
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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, NULL) < 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
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, NULL) < 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
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
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
2678 void ejsSetVarFlags(EjsVar *obj, int flags)
2679 {
2680         obj->flags = flags;
2681 }
2682
2683 /******************************************************************************/
2684
2685 void *ejsGetCMethodUserData(EjsVar *obj)
2686 {
2687         return obj->cMethod.userData;
2688 }
2689
2690 /******************************************************************************/
2691
2692 int ejsGetVarFlags(EjsVar *obj)
2693 {
2694         return obj->flags;
2695 }
2696
2697 /******************************************************************************/
2698
2699 void ejsSetObjDestructor(Ejs *ep, EjsVar *obj, EjsDestructor destructor)
2700 {
2701         obj->objectState->destructor = destructor;
2702 }
2703
2704 /******************************************************************************/
2705
2706 void ejsClearObjDestructor(Ejs *ep, EjsVar *obj)
2707 {
2708         obj->objectState->destructor = 0;
2709 }
2710
2711 /******************************************************************************/
2712 /*
2713  *      Create a new property
2714  */
2715
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
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
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
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
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
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         
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  */
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  */
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
3141 void ejsSetPropertyName(EjsProperty *pp, const char *property)
3142 {
3143         mprStrcpy(pp->name, sizeof(pp->name), property);
3144 }
3145
3146 /******************************************************************************/
3147
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
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
3169 void ejsMakePropertyReadOnly(EjsProperty *prop, int readonly)
3170 {
3171         prop->readonly = readonly;
3172 }
3173
3174 /******************************************************************************/
3175
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
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
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
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
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
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
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 = &currentProp->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
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
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" */
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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  */