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