1
0
mirror of https://github.com/samba-team/samba.git synced 2025-12-13 16:23:50 +03:00
Files
samba-mirror/source/lib/appweb/ejs-2.0/ejs/ejsVar.c
Andrew Bartlett 72ca8e3b2a r23961: Allow SWAT to operate on x86_64 machines.
On machines with a 4 byte int, and a 8 byte pointer, the ESP could would fail.

The problem is that 0 != NULL.  0 is an int (4 bytes) and NULL is a
pointer (8), and this matters critically to varargs functions.

If a 0 was passed as the 'terminating' argument, then only 4 bytes
would be written to the stack, but va_arg(ap, char *) would try and
pull 8, reading uninitalised memory.

Andrew Bartlett
2007-10-10 15:01:11 -05:00

4034 lines
85 KiB
C

/**
* @file ejsVar.c
* @brief Mbedthis Portable Runtime Universal Variable Type
*/
/*
* @copy default
*
* Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved.
* Copyright (c) Michael O'Brien, 1994-1995. All Rights Reserved.
*
* This software is distributed under commercial and open source licenses.
* You may use the GPL open source license described below or you may acquire
* a commercial license from Mbedthis Software. You agree to be fully bound
* by the terms of either license. Consult the LICENSE.TXT distributed with
* this software for full details.
*
* This software is open source; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See the GNU General Public License for more
* details at: http://www.mbedthis.com/downloads/gplLicense.html
*
* This program is distributed WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* This GPL license does NOT permit incorporating this software into
* proprietary programs. If you are unable to comply with the GPL, you must
* acquire a commercial license to use this software. Commercial licenses
* for this software and support services are available from Mbedthis
* Software at http://www.mbedthis.com
*
* @end
*/
/******************************* Documentation ********************************/
/*
* This module is NOT multithreaded.
*
* Properties are variables that are stored in an object type variable.
* Properties can be primitive data types, other objects or methods.
* Properties are indexed by a character name.
*/
/********************************** Includes **********************************/
#include "ejs.h"
/***************************** Forward Declarations ***************************/
static EjsProperty *allocProperty(Ejs *ep, EjsVar *op, const char *property,
int propertyIndex, EjsProperty *last);
static EjsVar *copyVar(EJS_LOC_DEC(ep, loc), EjsVar *dest,
const EjsVar *src, EjsCopyDepth copyDepth);
static EjsObj *createObj(EJS_LOC_DEC(ep, loc));
static char *getNextVarToken(char **next, char *tokBuf, int tokBufLen);
static int hash(const char *property);
static void unlinkProperty(EjsObj *obj, EjsPropLink *propLink);
static void linkPropertyBefore(EjsObj *obj, EjsPropLink *at,
EjsPropLink *propLink);
static int sortAllProperties(Ejs *ep, EjsProperty *p1,
EjsProperty *p2, const char *propertyName, int order);
static int sortByProperty(Ejs *ep, EjsProperty *p1, EjsProperty *p2,
const char *propertyName, int order);
static int dupString(MPR_LOC_DEC(ctx, loc), uchar **dest,
const void *src, int nbytes);
#if UNUSED && KEEP
static void linkPropertyAfter(EjsObj *obj, EjsPropLink *at,
EjsPropLink *propLink);
#endif
static EjsProperty *hashLookup(EjsObj *obj, const char *property,
int *propertyIndex, EjsProperty **hashTail);
/******************************************************************************/
/********************************** Var Routines ******************************/
/******************************************************************************/
EjsType ejsGetVarType(EjsVar *vp)
{
mprAssert(vp);
return vp->type;
}
/******************************************************************************/
void ejsFreeVar(Ejs *ep, EjsVar *vp)
{
if (vp) {
ejsClearVar(ep, vp);
ejsFree(ep, vp, EJS_SLAB_VAR);
}
}
/******************************************************************************/
#if UNUSED
/*
* Clear the value by freeing any allocated data. This will release objects
* so that later garbage collection can reclaim storage if there are no other
* object references.
*/
void ejsZeroVar(Ejs *ep, EjsVar *vp)
{
vp->type = EJS_TYPE_UNDEFINED;
vp->objectState = 0;
vp->method.body = 0;
vp->method.args = 0;
vp->callsSuper = 0;
vp->ptr.destructor = 0;
vp->allocatedData = 0;
}
#endif
/******************************************************************************/
/*
* Clear the value by freeing any allocated data. This will release objects
* so that later garbage collection can reclaim storage if there are no other
* object references.
*/
void ejsClearVar(Ejs *ep, EjsVar *vp)
{
MprArray *argList;
int i;
mprAssert(vp);
mprAssert(ep);
if (! vp->allocatedData) {
vp->type = EJS_TYPE_UNDEFINED;
return;
}
if (vp->type == EJS_TYPE_UNDEFINED) {
return;
}
switch (vp->type) {
default:
break;
case EJS_TYPE_STRING:
mprFree(vp->string);
vp->string = 0;
break;
case EJS_TYPE_OBJECT:
/*
* Set the "alive" bit so that the GC will cleanup if no
* other references.
*/
if (vp->objectState) {
vp->objectState->alive = 1;
}
vp->objectState = 0;
break;
case EJS_TYPE_METHOD:
argList = vp->method.args;
/*
* MOB OPT -- should be able to do just one mprFree(vp->method.args)
*/
mprFree(vp->method.body);
if (argList) {
for (i = 0; i < argList->length; i++) {
mprFree(argList->items[i]);
}
mprFree(vp->method.args);
}
vp->method.args = 0;
vp->method.body = 0;
vp->callsSuper = 0;
break;
case EJS_TYPE_PTR:
if (vp->ptr.destructor) {
(vp->ptr.destructor)(ep, vp);
}
break;
}
vp->type = EJS_TYPE_UNDEFINED;
vp->allocatedData = 0;
}
/******************************************************************************/
/*
* Initialize an undefined value.
*/
EjsVar *ejsCreateUndefinedVar(Ejs *ep)
{
EjsVar *vp;
mprAssert(ep);
vp = ejsAllocVar(EJS_LOC_ARGS(ep));
if (vp) {
vp->type = EJS_TYPE_UNDEFINED;
}
return vp;
}
/******************************************************************************/
/*
* Initialize an null value.
*/
EjsVar *ejsCreateNullVar(Ejs *ep)
{
EjsVar *vp;
mprAssert(ep);
vp = ejsAllocVar(EJS_LOC_ARGS(ep));
if (vp) {
vp->type = EJS_TYPE_NULL;
}
return vp;
}
/******************************************************************************/
EjsVar *ejsCreateBoolVar(Ejs *ep, int value)
{
EjsVar *vp;
mprAssert(ep);
vp = ejsAllocVar(EJS_LOC_ARGS(ep));
if (vp) {
vp->type = EJS_TYPE_BOOL;
vp->boolean = value;
}
return vp;
}
/******************************************************************************/
/*
* Initialize a C method.
*/
EjsVar *ejsCreateCMethodVar(Ejs *ep, EjsCMethod fn, void *userData, int flags)
{
EjsVar *vp;
mprAssert(ep);
vp = ejsAllocVar(EJS_LOC_ARGS(ep));
if (vp) {
vp->type = EJS_TYPE_CMETHOD;
vp->cMethod.fn = fn;
vp->cMethod.userData = userData;
vp->flags = flags;
}
return vp;
}
/******************************************************************************/
/*
* Initialize a C method.
*/
EjsVar *ejsCreateStringCMethodVar(Ejs *ep, EjsStringCMethod fn,
void *userData, int flags)
{
EjsVar *vp;
mprAssert(ep);
mprAssert(fn);
vp = ejsAllocVar(EJS_LOC_ARGS(ep));
if (vp) {
vp->type = EJS_TYPE_STRING_CMETHOD;
vp->cMethodWithStrings.fn = fn;
vp->cMethodWithStrings.userData = userData;
vp->flags = flags;
}
return vp;
}
/******************************************************************************/
/*
* Initialize an opaque pointer.
*/
EjsVar *ejsCreatePtrVar(Ejs *ep, void *ptr, EjsDestructor destructor)
{
EjsVar *vp;
mprAssert(ep);
mprAssert(ptr);
vp = ejsAllocVar(EJS_LOC_ARGS(ep));
if (vp) {
vp->type = EJS_TYPE_PTR;
vp->ptr.userPtr = ptr;
vp->ptr.destructor = destructor;
vp->allocatedData = 1;
}
return vp;
}
/******************************************************************************/
#if BLD_FEATURE_FLOATING_POINT
/*
* Initialize a floating value.
*/
EjsVar *ejsCreateFloatVar(Ejs *ep, double value)
{
EjsVar *vp;
mprAssert(ep);
vp = ejsAllocVar(EJS_LOC_ARGS(ep));
if (vp) {
vp->type = EJS_TYPE_FLOAT;
vp->floating = value;
}
return vp;
}
#endif
/******************************************************************************/
/*
* Initialize an integer value.
*/
EjsVar *ejsCreateIntegerVar(Ejs *ep, int value)
{
EjsVar *vp;
mprAssert(ep);
vp = ejsAllocVar(EJS_LOC_ARGS(ep));
if (vp) {
vp->type = EJS_TYPE_INT;
vp->integer = value;
}
return vp;
}
/******************************************************************************/
#if BLD_FEATURE_INT64
/*
* Initialize a 64-bit integer value.
*/
EjsVar *ejsCreateInteger64Var(Ejs *ep, int64 value)
{
EjsVar *vp;
mprAssert(ep);
vp = ejsAllocVar(EJS_LOC_ARGS(ep));
if (vp) {
vp->type = EJS_TYPE_INT64;
vp->integer64 = value;
}
return vp;
}
#endif /* BLD_FEATURE_INT64 */
/******************************************************************************/
/*
* Initialize an number variable. Type is defined by configure.
*/
EjsVar *ejsCreateNumberVar(Ejs *ep, EjsNum value)
{
EjsVar *vp;
mprAssert(ep);
vp = ejsAllocVar(EJS_LOC_ARGS(ep));
mprAssert(vp);
if (vp) {
vp->type = BLD_FEATURE_NUM_TYPE_ID;
#if BLD_FEATURE_NUM_TYPE_ID == EJS_TYPE_INT64
vp->integer64 = value;
#elif BLD_FEATURE_NUM_TYPE_ID == EJS_TYPE_FLOAT
vp->float = value;
#else
vp->integer = value;
#endif
}
return vp;
}
/******************************************************************************/
/*
* Initialize a (bare) JavaScript method. args and body can be null.
*/
EjsVar *ejsCreateMethodVar(Ejs *ep, const char *body, MprArray *args, int flags)
{
EjsVar *vp;
int i;
mprAssert(ep);
vp = ejsAllocVar(EJS_LOC_ARGS(ep));
mprAssert(vp);
if (vp == 0) {
return 0;
}
vp->type = EJS_TYPE_METHOD;
vp->allocatedData = 1;
vp->method.args = mprCreateItemArray(ep, EJS_INC_ARGS, EJS_MAX_ARGS);
if (vp->method.args == 0) {
mprAssert(vp->method.args);
ejsFreeVar(ep, vp);
return 0;
}
if (args) {
for (i = 0; i < args->length; i++) {
mprAddItem(vp->method.args,
mprStrdup(vp->method.args, mprGetItem(args, i)));
}
}
vp->method.body = mprStrdup(vp->method.args, body);
if (vp->method.body == 0) {
ejsFreeVar(ep, vp);
return 0;
}
vp->flags = flags;
return vp;
}
/******************************************************************************/
/*
* Initialize an object variable.
*/
EjsVar *ejsCreateObjVarInternal(EJS_LOC_DEC(ep, loc))
{
EjsVar *vp;
mprAssert(ep);
vp = ejsAllocVar(EJS_LOC_PASS(ep, loc));
mprAssert(vp);
if (vp) {
vp->type = EJS_TYPE_OBJECT;
vp->objectState = createObj(EJS_LOC_PASS(ep, loc));
if (vp->objectState == 0) {
ejsFreeVar(ep, vp);
return 0;
}
vp->allocatedData = 1;
}
return vp;
}
/******************************************************************************/
/*
* Initialize a string value.
*/
EjsVar *ejsCreateStringVarInternal(EJS_LOC_DEC(ep, loc), const char *value)
{
EjsVar *vp;
mprAssert(ep);
vp = ejsAllocVar(EJS_LOC_PASS(ep, loc));
mprAssert(vp);
if (vp) {
vp->type = EJS_TYPE_STRING;
vp->string = mprStrdupInternal(EJS_LOC_PASS(ep, loc), value);
if (vp->string == 0) {
ejsFreeVar(ep, vp);
return 0;
}
vp->length = strlen(vp->string);
vp->allocatedData = 1;
}
return vp;
}
/******************************************************************************/
/*
* Initialize a binary string value.
*/
EjsVar *ejsCreateBinaryStringVar(Ejs *ep, const uchar *value, int len)
{
EjsVar *vp;
mprAssert(ep);
vp = ejsAllocVar(EJS_LOC_ARGS(ep));
if (vp) {
vp->type = EJS_TYPE_STRING;
vp->length = dupString(MPR_LOC_ARGS(ep), &vp->ustring, value, len);
if (vp->length < 0) {
ejsFreeVar(ep, vp);
return 0;
}
vp->allocatedData = 1;
}
return vp;
}
/******************************************************************************/
void ejsSetClassName(Ejs *ep, EjsVar *vp, const char *name)
{
EjsObj *obj;
if (vp == 0 || !ejsVarIsObject(vp) || vp->objectState == 0) {
mprAssert(0);
return;
}
obj = vp->objectState;
if (obj->className) {
mprFree(obj->className);
}
obj->className = mprStrdup(ep, name);
}
/******************************************************************************/
EjsVar *ejsDupVarInternal(EJS_LOC_DEC(ep, loc), EjsVar *src,
EjsCopyDepth copyDepth)
{
EjsVar *vp;
vp = ejsAllocVar(EJS_LOC_PASS(ep, loc));
if (vp == 0) {
return 0;
}
vp->type = EJS_TYPE_UNDEFINED;
return copyVar(EJS_LOC_PASS(ep, loc), vp, src, copyDepth);
}
/******************************************************************************/
/*
* Set a var to a new value
*/
EjsVar *ejsWriteVarInternal(EJS_LOC_DEC(ep, loc), EjsVar *dest,
const EjsVar *src, EjsCopyDepth copyDepth)
{
mprAssert(dest);
mprAssert(src);
return copyVar(EJS_LOC_PASS(ep, loc), dest, src, copyDepth);
}
/******************************************************************************/
/*
* Set a var using a new bool value
*/
EjsVar *ejsWriteVarAsBoolean(Ejs *ep, EjsVar *dest, int value)
{
mprAssert(dest);
if (dest->type != EJS_TYPE_UNDEFINED) {
ejsClearVar(ep, dest);
}
dest->type = EJS_TYPE_BOOL;
dest->boolean = value;
dest->allocatedData = 0;
dest->flags = 0;
return dest;
}
/******************************************************************************/
/*
* Set a var using a new C Method
*/
EjsVar *ejsWriteVarAsCMethod(Ejs *ep, EjsVar *dest, EjsCMethod fn,
void *userData, int flags)
{
mprAssert(dest);
if (dest->type != EJS_TYPE_UNDEFINED) {
ejsClearVar(ep, dest);
}
dest->type = EJS_TYPE_CMETHOD;
dest->cMethod.fn = fn;
dest->cMethod.userData = userData;
dest->flags = flags;
dest->allocatedData = 0;
return dest;
}
/******************************************************************************/
#if BLD_FEATURE_FLOATING_POINT
/*
* Set a var using a new float value
*/
EjsVar *ejsWriteVarAsFloat(Ejs *ep, EjsVar *dest, double value)
{
mprAssert(dest);
if (dest->type != EJS_TYPE_UNDEFINED) {
ejsClearVar(ep, dest);
}
dest->type = EJS_TYPE_FLOAT;
dest->floating = value;
dest->allocatedData = 0;
dest->flags = 0;
return dest;
}
#endif
/******************************************************************************/
/*
* Set a var using a new integer value
*/
EjsVar *ejsWriteVarAsInteger(Ejs *ep, EjsVar *dest, int value)
{
mprAssert(dest);
if (dest->type != EJS_TYPE_UNDEFINED) {
ejsClearVar(ep, dest);
}
dest->type = EJS_TYPE_INT;
dest->integer = value;
dest->allocatedData = 0;
dest->flags = 0;
return dest;
}
/******************************************************************************/
#if BLD_FEATURE_INT64
/*
* Set a var using a new integer value
*/
EjsVar *ejsWriteVarAsInteger64(Ejs *ep, EjsVar *dest, int64 value)
{
mprAssert(dest);
if (dest->type != EJS_TYPE_UNDEFINED) {
ejsClearVar(ep, dest);
}
dest->type = EJS_TYPE_INT64;
dest->integer64 = value;
dest->allocatedData = 0;
dest->flags = 0;
return dest;
}
#endif
/******************************************************************************/
/*
* Set a var using a new Method
*/
EjsVar *ejsWriteVarAsMethod(Ejs *ep, EjsVar *dest, const char *body,
MprArray *args)
{
EjsVar **srcArgs, *arg;
int i;
mprAssert(ep);
mprAssert(dest);
mprAssert(body);
if (dest->type != EJS_TYPE_UNDEFINED) {
ejsClearVar(ep, dest);
}
dest->method.args = mprCreateItemArray(ep, EJS_INC_ARGS, EJS_MAX_ARGS);
if (dest->method.args == 0) {
return 0;
}
dest->type = EJS_TYPE_METHOD;
if (args) {
srcArgs = (EjsVar**) args->items;
for (i = 0; i < args->length; i++) {
arg = ejsDupVar(ep, srcArgs[i], EJS_SHALLOW_COPY);
if (arg == 0) {
return 0;
}
if (mprAddItem(dest->method.args, arg) < 0) {
return 0;
}
}
}
dest->method.body = mprStrdup(dest->method.args, body);
if (dest->method.body == 0) {
return 0;
}
dest->allocatedData = 1;
dest->flags = 0;
return dest;
}
/******************************************************************************/
/*
* Set a var to null
*/
EjsVar *ejsWriteVarAsNull(Ejs *ep, EjsVar *dest)
{
mprAssert(dest);
if (dest->type != EJS_TYPE_UNDEFINED) {
ejsClearVar(ep, dest);
}
dest->type = EJS_TYPE_NULL;
dest->allocatedData = 0;
dest->flags = 0;
return dest;
}
/******************************************************************************/
/*
* Set a var using a new number value
*/
EjsVar *ejsWriteVarAsNumber(Ejs *ep, EjsVar *dest, EjsNum value)
{
mprAssert(dest);
if (dest->type != EJS_TYPE_UNDEFINED) {
ejsClearVar(ep, dest);
}
dest->type = EJS_NUM_VAR;
dest->ejsNumber = value;
dest->allocatedData = 0;
dest->flags = 0;
return dest;
}
/******************************************************************************/
/*
* Set a var using a new C Method
*/
EjsVar *ejsWriteVarAsStringCMethod(Ejs *ep, EjsVar *dest, EjsStringCMethod fn,
void *userData, int flags)
{
mprAssert(dest);
if (dest->type != EJS_TYPE_UNDEFINED) {
ejsClearVar(ep, dest);
}
dest->type = EJS_TYPE_CMETHOD;
dest->cMethodWithStrings.fn = fn;
dest->cMethodWithStrings.userData = userData;
dest->flags = flags;
dest->allocatedData = 0;
return dest;
}
/******************************************************************************/
/*
* Set a var using a new string value
*/
EjsVar *ejsWriteVarAsStringInternal(EJS_LOC_DEC(ep, loc), EjsVar *dest,
const char *value)
{
mprAssert(dest);
mprAssert(value);
if (dest->type != EJS_TYPE_UNDEFINED) {
ejsClearVar(ep, dest);
}
dest->string = mprStrdupInternal(EJS_LOC_PASS(ep, loc), value);
if (dest->string == 0) {
return 0;
}
dest->length = strlen(dest->string);
dest->type = EJS_TYPE_STRING;
dest->allocatedData = 1;
dest->flags = 0;
return dest;
}
/******************************************************************************/
/*
* Set a var using a new string value
*/
EjsVar *ejsWriteVarAsBinaryString(Ejs *ep, EjsVar *dest, const uchar *value,
int len)
{
mprAssert(dest);
mprAssert(value);
ejsClearVar(ep, dest);
if (dest->type != EJS_TYPE_UNDEFINED) {
ejsClearVar(ep, dest);
}
dest->length = dupString(MPR_LOC_ARGS(ep), &dest->ustring, value, len);
if (dest->length < 0) {
return 0;
}
dest->type = EJS_TYPE_STRING;
dest->allocatedData = 1;
dest->flags = 0;
return dest;
}
/******************************************************************************/
/*
* Set a var to undefined
*/
EjsVar *ejsWriteVarAsUndefined(Ejs *ep, EjsVar *dest)
{
mprAssert(dest);
if (dest->type != EJS_TYPE_UNDEFINED) {
ejsClearVar(ep, dest);
}
dest->type = EJS_TYPE_UNDEFINED;
dest->allocatedData = 0;
dest->flags = 0;
return dest;
}
/******************************************************************************/
/*
* Convert a value to a text based representation of its value
* If you provide a format, you MUST ensure you know the type.
* Caller must free the result.
*/
char *ejsFormatVar(Ejs *ep, const char *fmt, EjsVar *vp)
{
char *buf, *src, *value, *allocValue;
uchar *ubuf;
int len;
buf = 0;
allocValue = 0;
value = 0;
switch (vp->type) {
case EJS_TYPE_UNDEFINED:
value = "undefined";
break;
case EJS_TYPE_NULL:
value = "null";
break;
case EJS_TYPE_PTR:
if (fmt == NULL || *fmt == '\0') {
len = mprAllocSprintf(MPR_LOC_ARGS(ep), &buf, 0,
"[Opaque Pointer %p]", vp->ptr.userPtr);
} else {
len = mprAllocSprintf(MPR_LOC_ARGS(ep), &buf, 0, fmt, vp->ptr);
}
goto done;
case EJS_TYPE_BOOL:
value = (vp->boolean) ? "true" : "false";
break;
#if BLD_FEATURE_FLOATING_POINT
case EJS_TYPE_FLOAT:
if (fmt == NULL || *fmt == '\0') {
fmt = "%f";
}
len = mprAllocSprintf(MPR_LOC_ARGS(ep), &buf, 0, fmt, vp->floating);
goto done;
#endif
case EJS_TYPE_INT:
if (fmt == NULL || *fmt == '\0') {
fmt = "%d";
}
mprAllocSprintf(MPR_LOC_ARGS(ep), &buf, 0, fmt, vp->integer);
goto done;
#if BLD_FEATURE_INT64
case EJS_TYPE_INT64:
if (fmt == NULL || *fmt == '\0') {
fmt = "%Ld";
}
mprAllocSprintf(MPR_LOC_ARGS(ep), &buf, 0, fmt, vp->integer64);
goto done;
#endif
case EJS_TYPE_CMETHOD:
value = "[C Method]";
break;
case EJS_TYPE_STRING_CMETHOD:
value = "[C StringMethod]";
break;
case EJS_TYPE_METHOD:
value = ejsVarToString(ep, vp);
break;
case EJS_TYPE_OBJECT:
value = ejsVarToString(ep, vp);
break;
case EJS_TYPE_STRING:
src = vp->string;
mprAssert(src);
if (fmt && *fmt && src) {
mprAllocSprintf(MPR_LOC_ARGS(ep), &buf, 0, fmt, src);
} else if (src == NULL) {
buf = mprStrdup(ep, "null");
} else {
ubuf = (uchar*) buf;
if (dupString(MPR_LOC_ARGS(ep), &ubuf, src, vp->length) < 0) {
return mprStrdup(ep, "");
}
buf = (char*) ubuf;
}
break;
default:
mprAssert(0);
}
if (fmt == NULL || *fmt == '\0') {
len = mprAllocSprintf(MPR_LOC_ARGS(ep), &buf, 0, "%s", value);
} else {
len = mprAllocSprintf(MPR_LOC_ARGS(ep), &buf, 0, fmt, value);
}
done:
if (allocValue) {
mprFree(allocValue);
}
return buf;
}
/******************************************************************************/
/*
* Convert the variable to a boolean. Only for primitive types.
*/
int ejsVarToBoolean(EjsVar *vp)
{
mprAssert(vp);
switch (vp->type) {
case EJS_TYPE_UNDEFINED:
case EJS_TYPE_NULL:
case EJS_TYPE_STRING_CMETHOD:
case EJS_TYPE_CMETHOD:
case EJS_TYPE_METHOD:
return 0;
case EJS_TYPE_OBJECT:
return (vp->objectState != NULL);
case EJS_TYPE_PTR:
return (vp->ptr.userPtr != NULL);
case EJS_TYPE_BOOL:
return vp->boolean;
#if BLD_FEATURE_FLOATING_POINT
case EJS_TYPE_FLOAT:
return (vp->floating != 0 && !ejsIsNan(vp->floating));
#endif
case EJS_TYPE_INT:
return (vp->integer != 0);
#if BLD_FEATURE_INT64
case EJS_TYPE_INT64:
return (vp->integer64 != 0);
#endif
case EJS_TYPE_STRING:
return (vp->length > 0);
#if UNUSED
if (strcmp(vp->string, "true") == 0 ||
strcmp(vp->string, "TRUE") == 0) {
return 1;
} else if (strcmp(vp->string, "false") == 0 ||
strcmp(vp->string, "FALSE") == 0) {
return 0;
} else {
return atoi(vp->string);
}
#endif
}
/* Not reached */
return 0;
}
/******************************************************************************/
#if BLD_FEATURE_FLOATING_POINT
/*
* Convert the variable to a floating point number. Only for primitive types.
*/
double ejsVarToFloat(EjsVar *vp)
{
mprAssert(vp);
switch (vp->type) {
case EJS_TYPE_UNDEFINED:
case EJS_TYPE_NULL:
case EJS_TYPE_STRING_CMETHOD:
case EJS_TYPE_CMETHOD:
case EJS_TYPE_METHOD:
case EJS_TYPE_OBJECT:
case EJS_TYPE_PTR:
return 0;
case EJS_TYPE_BOOL:
return (vp->boolean) ? 1.0 : 0.0;
case EJS_TYPE_FLOAT:
return vp->floating;
case EJS_TYPE_INT:
return (double) vp->integer;
#if BLD_FEATURE_INT64
case EJS_TYPE_INT64:
return (double) vp->integer64;
#endif
case EJS_TYPE_STRING:
if (vp->length == 0) {
return 0.0;
} else {
return atof(vp->string);
}
}
/* Not reached */
return 0;
}
#endif
/******************************************************************************/
/*
* Convert the variable to an Integer type. Only works for primitive types.
*/
int ejsVarToInteger(EjsVar *vp)
{
mprAssert(vp);
switch (vp->type) {
case EJS_TYPE_UNDEFINED:
case EJS_TYPE_NULL:
case EJS_TYPE_STRING_CMETHOD:
case EJS_TYPE_CMETHOD:
case EJS_TYPE_METHOD:
case EJS_TYPE_OBJECT:
return 0;
case EJS_TYPE_BOOL:
return (vp->boolean) ? 1 : 0;
#if BLD_FEATURE_FLOATING_POINT
case EJS_TYPE_FLOAT:
if (ejsIsNan(vp->floating)) {
return 0;
}
return (int) vp->floating;
#endif
case EJS_TYPE_INT:
return vp->integer;
#if BLD_FEATURE_INT64
case EJS_TYPE_INT64:
return (int) vp->integer64;
#endif
case EJS_TYPE_STRING:
if (vp->length == 0) {
return 0;
} else {
return ejsParseInteger(vp->string);
}
}
/* Not reached */
return 0;
}
/******************************************************************************/
#if BLD_FEATURE_INT64
/*
* Convert the variable to an Integer64 type. Only works for primitive types.
*/
int64 ejsVarToInteger64(EjsVar *vp)
{
mprAssert(vp);
switch (vp->type) {
case EJS_TYPE_UNDEFINED:
case EJS_TYPE_NULL:
case EJS_TYPE_STRING_CMETHOD:
case EJS_TYPE_CMETHOD:
case EJS_TYPE_METHOD:
case EJS_TYPE_OBJECT:
case EJS_TYPE_PTR:
return 0;
case EJS_TYPE_BOOL:
return (vp->boolean) ? 1 : 0;
#if BLD_FEATURE_FLOATING_POINT
case EJS_TYPE_FLOAT:
if (ejsIsNan(vp->floating)) {
return 0;
}
return (int64) vp->floating;
#endif
case EJS_TYPE_INT:
return vp->integer;
case EJS_TYPE_INT64:
return vp->integer64;
case EJS_TYPE_STRING:
if (vp->length == 0) {
return 0;
} else {
return ejsParseInteger64(vp->string);
}
}
/* Not reached */
return 0;
}
#endif /* BLD_FEATURE_INT64 */
/******************************************************************************/
/*
* Convert the variable to a number type. Only works for primitive types.
*/
EjsNum ejsVarToNumber(EjsVar *vp)
{
#if BLD_FEATURE_NUM_TYPE_ID == EJS_TYPE_INT64
return ejsVarToInteger64(vp);
#elif BLD_FEATURE_NUM_TYPE_ID == EJS_TYPE_FLOAT
return ejsVarToFloat(vp);
#else
return ejsVarToInteger(vp);
#endif
}
/******************************************************************************/
/*
* Convert a var to a string. Store the result in ep->castTemp. If allocated
* set ep->castAlloc to TRUE. Caller must NOT free the result.
*/
char *ejsVarToString(Ejs *ep, EjsVar *vp)
{
MprBuf *bp;
char numBuf[16];
int len, i;
if (ep->castAlloc) {
mprFree(ep->castTemp);
}
ep->castTemp = 0;
ep->castAlloc = 0;
switch (vp->type) {
case EJS_TYPE_UNDEFINED:
ep->castTemp = "undefined";
break;
case EJS_TYPE_NULL:
ep->castTemp = "null";
break;
case EJS_TYPE_PTR:
len = mprAllocSprintf(MPR_LOC_ARGS(ep), &ep->castTemp, 0,
"[Opaque Pointer %p]", vp->ptr.userPtr);
ep->castAlloc = 1;
break;
case EJS_TYPE_BOOL:
if (vp->boolean) {
ep->castTemp = "true";
} else {
ep->castTemp = "false";
}
break;
#if BLD_FEATURE_FLOATING_POINT
case EJS_TYPE_FLOAT:
len = mprAllocSprintf(MPR_LOC_ARGS(ep), &ep->castTemp, 0,
"%f", vp->floating);
ep->castAlloc = 1;
break;
#endif
case EJS_TYPE_INT:
mprItoa(numBuf, sizeof(numBuf), vp->integer);
ep->castTemp = mprStrdup(ep, numBuf);
ep->castAlloc = 1;
break;
#if BLD_FEATURE_INT64
case EJS_TYPE_INT64:
mprAllocSprintf(MPR_LOC_ARGS(ep), &ep->castTemp, 0,
"%Ld", vp->integer64);
ep->castAlloc = 1;
break;
#endif
case EJS_TYPE_CMETHOD:
ep->castTemp = "[C Method]";
break;
case EJS_TYPE_STRING_CMETHOD:
ep->castTemp = "[C StringMethod]";
break;
case EJS_TYPE_METHOD:
bp = mprCreateBuf(ep, 0, 0);
mprPutStringToBuf(bp, "function (");
for (i = 0; i < vp->method.args->length; i++) {
mprPutStringToBuf(bp, vp->method.args->items[i]);
if ((i + 1) < vp->method.args->length) {
mprPutStringToBuf(bp, ", ");
}
}
mprPutStringToBuf(bp, ") {");
mprPutStringToBuf(bp, vp->method.body);
mprPutStringToBuf(bp, "}");
mprAddNullToBuf(bp);
ep->castTemp = mprStealBuf(ep, bp);
ep->castAlloc = 1;
mprFree(bp);
break;
case EJS_TYPE_OBJECT:
if (ejsRunMethod(ep, vp, "toString", 0) < 0) {
return mprStrdup(ep, "[object Object]");
}
ep->castTemp = mprStrdup(ep, ep->result->string);
ep->castAlloc = 1;
break;
case EJS_TYPE_STRING:
if (vp->string == 0) {
ep->castTemp = "null";
} else {
ep->castTemp = vp->string;
}
break;
default:
mprAssert(0);
}
mprAssert(ep->castTemp);
return ep->castTemp;
}
/******************************************************************************/
char *ejsVarToStringEx(Ejs *ep, EjsVar *vp, bool *alloc)
{
char *str;
mprAssert(alloc);
str = ejsVarToString(ep, vp);
*alloc = ep->castAlloc;
ep->castAlloc = 0;
ep->castTemp = 0;
return str;
}
/******************************************************************************/
/*
* Parse a string based on formatting instructions and intelligently
* create a variable.
*
* Float format: [+|-]DIGITS][DIGITS][(e|E)[+|-]DIGITS]
*/
EjsVar *ejsParseVar(Ejs *ep, const char *buf, EjsType preferredType)
{
EjsType type;
const char *cp;
int isHex;
mprAssert(buf);
type = preferredType;
if (preferredType == EJS_TYPE_UNDEFINED) {
isHex = 0;
if (*buf == '-' || *buf == '+') {
type = EJS_NUM_VAR;
} else if (!isdigit((int) *buf)) {
if (strcmp(buf, "true") == 0 || strcmp(buf, "false") == 0) {
type = EJS_TYPE_BOOL;
} else {
type = EJS_TYPE_STRING;
}
} else if (isdigit((int) *buf)) {
type = EJS_NUM_VAR;
cp = buf;
if (*cp && tolower(cp[1]) == 'x') {
cp = &cp[2];
isHex = 1;
for (cp = buf; *cp; cp++) {
if (! isxdigit((int) *cp)) {
break;
}
}
} else {
#if BLD_FEATURE_FLOATING_POINT
/* Could be integer or float */
for (cp = buf; *cp; cp++) {
if (! isdigit((int) *cp)) {
int c = tolower(*cp);
if (c == '.' || c == 'e' || c == 'f') {
type = EJS_TYPE_FLOAT;
break;
}
}
}
#endif
}
}
}
switch (type) {
case EJS_TYPE_OBJECT:
case EJS_TYPE_UNDEFINED:
case EJS_TYPE_NULL:
case EJS_TYPE_PTR:
default:
break;
case EJS_TYPE_BOOL:
return ejsCreateBoolVar(ep, ejsParseBoolean(buf));
case EJS_TYPE_INT:
return ejsCreateIntegerVar(ep, ejsParseInteger(buf));
#if BLD_FEATURE_INT64
case EJS_TYPE_INT64:
return ejsCreateInteger64Var(ep, ejsParseInteger64(buf));
#endif
case EJS_TYPE_STRING:
if (strcmp(buf, "null") == 0) {
return ejsCreateNullVar(ep);
} else if (strcmp(buf, "undefined") == 0) {
return ejsCreateUndefinedVar(ep);
}
return ejsCreateStringVar(ep, buf);
#if BLD_FEATURE_FLOATING_POINT
case EJS_TYPE_FLOAT:
return ejsCreateFloatVar(ep, atof(buf));
#endif
}
return ejsCreateUndefinedVar(ep);
}
/******************************************************************************/
/*
* Convert the variable to a number type. Only works for primitive types.
*/
bool ejsParseBoolean(const char *s)
{
if (s == 0 || *s == '\0') {
return 0;
}
if (strcmp(s, "false") == 0 || strcmp(s, "FALSE") == 0) {
return 0;
}
return 1;
}
/******************************************************************************/
/*
* Convert the variable to a number type. Only works for primitive types.
*/
EjsNum ejsParseNumber(const char *s)
{
#if BLD_FEATURE_NUM_TYPE_ID == EJS_TYPE_INT64
return ejsParseInteger64(s);
#elif BLD_FEATURE_NUM_TYPE_ID == EJS_TYPE_FLOAT
return ejsParseFloat(s);
#else
return ejsParseInteger(s);
#endif
}
/******************************************************************************/
#if BLD_FEATURE_INT64
/*
* Convert the string buffer to an Integer64.
*/
int64 ejsParseInteger64(const char *str)
{
const char *cp;
int64 num64;
int radix, c, negative;
mprAssert(str);
cp = str;
num64 = 0;
negative = 0;
if (*cp == '-') {
cp++;
negative = 1;
} else if (*cp == '+') {
cp++;
}
/*
* Parse a number. Observe hex and octal prefixes (0x, 0)
*/
if (*cp != '0') {
/*
* Normal numbers (Radix 10)
*/
while (isdigit((int) *cp)) {
num64 = (*cp - '0') + (num64 * 10);
cp++;
}
} else {
cp++;
if (tolower(*cp) == 'x') {
cp++;
radix = 16;
while (*cp) {
c = tolower(*cp);
if (isdigit(c)) {
num64 = (c - '0') + (num64 * radix);
} else if (c >= 'a' && c <= 'f') {
num64 = (c - 'a' + 10) + (num64 * radix);
} else {
break;
}
cp++;
}
} else{
radix = 8;
while (*cp) {
c = tolower(*cp);
if (isdigit(c) && c < '8') {
num64 = (c - '0') + (num64 * radix);
} else {
break;
}
cp++;
}
}
}
if (negative) {
return 0 - num64;
}
return num64;
}
#endif /* BLD_FEATURE_INT64 */
/******************************************************************************/
/*
* Convert the string buffer to an Integer.
*/
int ejsParseInteger(const char *str)
{
const char *cp;
int num;
int radix, c, negative;
mprAssert(str);
cp = str;
num = 0;
negative = 0;
if (*cp == '-') {
cp++;
negative = 1;
} else if (*cp == '+') {
cp++;
}
/*
* Parse a number. Observe hex and octal prefixes (0x, 0)
*/
if (*cp != '0') {
/*
* Normal numbers (Radix 10)
*/
while (isdigit((int) *cp)) {
num = (*cp - '0') + (num * 10);
cp++;
}
} else {
cp++;
if (tolower(*cp) == 'x') {
cp++;
radix = 16;
while (*cp) {
c = tolower(*cp);
if (isdigit(c)) {
num = (c - '0') + (num * radix);
} else if (c >= 'a' && c <= 'f') {
num = (c - 'a' + 10) + (num * radix);
} else {
break;
}
cp++;
}
} else{
radix = 8;
while (*cp) {
c = tolower(*cp);
if (isdigit(c) && c < '8') {
num = (c - '0') + (num * radix);
} else {
break;
}
cp++;
}
}
}
if (negative) {
return 0 - num;
}
return num;
}
/******************************************************************************/
#if BLD_FEATURE_FLOATING_POINT
/*
* Convert the string buffer to an Floating.
*/
double ejsParseFloat(const char *str)
{
return atof(str);
}
/******************************************************************************/
int ejsIsNan(double f)
{
#if WIN
return _isnan(f);
#elif VXWORKS
/* FUTURE */
return (0);
#else
return (f == FP_NAN);
#endif
}
/******************************************************************************/
int ejsIsInfinite(double f)
{
#if WIN
return !_finite(f);
#elif VXWORKS
/* FUTURE */
return (0);
#else
return (f == FP_INFINITE);
#endif
}
#endif /* BLD_FEATURE_FLOATING_POINT */
/******************************************************************************/
/*
* Single point of control for all assignment to properties.
*
* Copy an objects core value (only). This preserves the destination object's
* name. This implements copy by reference for objects and copy by value for
* strings and other types. Caller must free dest prior to calling.
*/
static EjsVar *copyVar(EJS_LOC_DEC(ep, loc), EjsVar *dest, const EjsVar *src,
EjsCopyDepth copyDepth)
{
Ejs *ejsContext;
EjsObj *srcObj;
EjsProperty *destp;
const char **srcArgs;
char *str;
int i;
mprAssert(dest);
mprAssert(src);
if (dest == src) {
return dest;
}
if (dest->type != EJS_TYPE_UNDEFINED) {
ejsClearVar(ep, dest);
}
dest->allocatedData = 0;
switch (src->type) {
default:
case EJS_TYPE_UNDEFINED:
case EJS_TYPE_NULL:
break;
case EJS_TYPE_BOOL:
dest->boolean = src->boolean;
break;
case EJS_TYPE_PTR:
dest->ptr = src->ptr;
if (dest->ptr.destructor) {
dest->allocatedData = 1;
}
break;
case EJS_TYPE_STRING_CMETHOD:
dest->cMethodWithStrings = src->cMethodWithStrings;
break;
case EJS_TYPE_CMETHOD:
dest->cMethod = src->cMethod;
break;
#if BLD_FEATURE_FLOATING_POINT
case EJS_TYPE_FLOAT:
dest->floating = src->floating;
break;
#endif
case EJS_TYPE_INT:
dest->integer = src->integer;
break;
#if BLD_FEATURE_INT64
case EJS_TYPE_INT64:
dest->integer64 = src->integer64;
break;
#endif
case EJS_TYPE_OBJECT:
if (copyDepth == EJS_SHALLOW_COPY) {
/*
* If doing a shallow copy and the src object is from the same
* interpreter, or we are copying from the master interpreter, or
* we are using a shared slab, then we can do a shallow copy.
* Otherwise, we must do a deep copy.
*/
srcObj = src->objectState;
if (srcObj->ejs == ep || srcObj->ejs == ep->service->master ||
(ep->flags & EJS_FLAGS_SHARED_SLAB)) {
dest->objectState = src->objectState;
dest->allocatedData = 1;
break;
}
}
/*
* Doing a deep or recursive deep. Can get here if doing a shallow
* copy and the object is from another non-master interpeter and not
* using a shared slab.
*
* We must make sure the data is allocated using the right memory
* context. It must be the same as the destination parent object.
* Otherwise, when we free the property memory, the parent may
* have a dangling pointer.
*/
if (dest->isProperty) {
destp = ejsGetPropertyPtr(dest);
if (destp->parentObj == 0) {
ejsContext = ep;
} else {
mprAssert(destp->parentObj);
ejsContext = destp->parentObj->ejs;
mprAssert(ejsContext);
}
} else {
ejsContext = ep;
}
dest->objectState = createObj(EJS_LOC_PASS(ejsContext, loc));
if (dest->objectState == 0) {
/* Memory Error */
return 0;
}
dest->objectState->baseClass = src->objectState->baseClass;
dest->objectState->methods = src->objectState->methods;
dest->objectState->noConstructor = src->objectState->noConstructor;
dest->objectState->objName =
mprStrdup(ejsContext, src->objectState->objName);
if (dest->objectState->objName == 0) {
return 0;
}
if (ejsCopyProperties(ep, dest, src, copyDepth) == 0) {
return 0;
}
dest->allocatedData = 1;
break;
case EJS_TYPE_METHOD:
dest->method.args = mprCreateItemArray(ep, EJS_INC_ARGS,
EJS_MAX_ARGS);
if (dest->method.args == 0) {
return 0;
}
dest->allocatedData = 1;
if (src->method.args) {
srcArgs = (const char**) src->method.args->items;
for (i = 0; i < src->method.args->length; i++) {
str = mprStrdupInternal(EJS_LOC_PASS(dest->method.args,
loc), srcArgs[i]);
if (str == 0) {
mprFree(dest->method.args);
dest->method.args = 0;
return 0;
}
if (mprAddItem(dest->method.args, str) < 0) {
mprFree(dest->method.args);
dest->method.args = 0;
return 0;
}
}
}
dest->method.body = mprStrdup(dest->method.args, src->method.body);
if (dest->method.body == 0) {
mprFree(dest->method.args);
dest->method.args = 0;
return 0;
}
dest->callsSuper = src->callsSuper;
break;
case EJS_TYPE_STRING:
dest->length = src->length;
if (src->string) {
/* Shallow, deep or recursive deep */
dest->length = dupString(MPR_LOC_PASS(ep, loc), &dest->ustring,
src->ustring, src->length);
if (dest->length < 0) {
return 0;
}
dest->allocatedData = 1;
} else {
dest->string = src->string;
dest->allocatedData = 0;
}
break;
}
dest->type = src->type;
dest->flags = src->flags;
dest->isArray = src->isArray;
return dest;
}
/******************************************************************************/
/*
* Copy all properies in an object. Must preserve property order
*/
EjsVar *ejsCopyProperties(Ejs *ep, EjsVar *dest, const EjsVar *src,
EjsCopyDepth copyDepth)
{
EjsProperty *srcProp, *destProp, *last, *next;
int propertyIndex;
srcProp = ejsGetFirstProperty(src, EJS_ENUM_ALL);
while (srcProp) {
next = ejsGetNextProperty(srcProp, EJS_ENUM_ALL);
if (srcProp->visited) {
srcProp = next;
continue;
}
/*
* This finds the last variable in the hash chain
* FUTURE OPT. This is slow. If used double link, we could locate the
* tail more easily.
*/
destProp = hashLookup(dest->objectState, srcProp->name,
&propertyIndex, &last);
mprAssert(destProp == 0);
destProp = allocProperty(ep, dest, srcProp->name, propertyIndex, last);
if (destProp == 0) {
mprAssert(destProp);
return 0;
}
/*
* Recursively copy the object. If DEEP_COPY, then we
* will do a shallow copy of the object contents. If
* RECURSIVE_DEEP, then we do a deep copy at all levels.
*/
srcProp->visited = 1;
if (copyVar(EJS_LOC_ARGS(ep), ejsGetVarPtr(destProp),
ejsGetVarPtr(srcProp),
(copyDepth == EJS_DEEP_COPY) ? EJS_SHALLOW_COPY : copyDepth)
== 0) {
return 0;
}
srcProp->visited = 0;
srcProp = next;
}
return dest;
}
/******************************************************************************/
/********************************** Properties ********************************/
/******************************************************************************/
/*
* Create a property in an object and return a pointer to it. If the property
* already exists then just return a pointer to it (no error).
* To test for existance of a property, use GetProperty
*/
static EjsProperty *hashLookup(EjsObj *obj, const char *property,
int *propertyIndex, EjsProperty **hashTail)
{
EjsProperty *prop, *last;
int index;
mprAssert(obj);
mprAssert(property);
if (obj == 0 || property == 0 || *property == '\0') {
mprAssert(0);
return 0;
}
/*
* Find the property in the hash chain if it exists
*/
index = hash(property);
prop = obj->propertyHash[index];
for (last = 0; prop != 0; last = prop, prop = prop->hashNext) {
if (prop->name[0] == property[0] &&
strcmp(prop->name, property) == 0) {
break;
}
}
if (propertyIndex) {
*propertyIndex = index;
}
if (hashTail) {
*hashTail = last;
}
return prop;
}
/******************************************************************************/
/*
* Create a property in an object and return a pointer to it. If the property
* already exists then just return a pointer to it (no error). If the property
* does not exist, create an undefined variable. To test for existance of a
* property, use GetProperty.
*/
EjsProperty *ejsCreateSimpleProperty(Ejs *ep, EjsVar *op, const char *property)
{
EjsProperty *prop, *last;
int propertyIndex;
if (op == 0 || op->type != EJS_TYPE_OBJECT || property == 0 ||
*property == '\0') {
mprAssert(0);
return 0;
}
/*
* Find the property in the hash chain if it exists
*/
prop = hashLookup(op->objectState, property, &propertyIndex, &last);
if (prop == 0) {
/*
* Create a new property
*/
prop = allocProperty(ep, op, property, propertyIndex, last);
if (prop == 0) {
mprAssert(prop == 0);
return 0;
}
}
return prop;
}
/******************************************************************************/
/*
* Create a property in an object and return a pointer to it. If the property
* already exists then just return a pointer to it (no error).
* To test for existance of a property, use GetProperty
*/
EjsProperty *ejsCreateSimpleNonUniqueProperty(Ejs *ep, EjsVar *op,
const char *property)
{
EjsProperty *prop, *last;
int propertyIndex;
if (op == 0 || op->type != EJS_TYPE_OBJECT || property == 0 ||
*property == '\0') {
mprAssert(0);
return 0;
}
/*
* Find end of chain
*/
propertyIndex = hash(property);
prop = op->objectState->propertyHash[propertyIndex];
for (last = 0; prop != 0; last = prop, prop = prop->hashNext) {
;
}
return allocProperty(ep, op, property, propertyIndex, last);
}
/******************************************************************************/
/*
* Find a property in an object and return a pointer to it.
* This does NOT traverse base classes.
*/
EjsProperty *ejsGetSimpleProperty(Ejs *ep, EjsVar *op, const char *property)
{
mprAssert(op);
mprAssert(op->type == EJS_TYPE_OBJECT);
mprAssert(property && *property);
/*
* This is an internal API. It has very little checking.
*/
return hashLookup(op->objectState, property, 0, 0);
}
/******************************************************************************/
/*
* NOTE: There is no ejsSetSimpleProperty as all the ejsSetProperty routines
* operate only on the instance and don't follow base classes. ie. there is
* no simple version required. However, there is a ejsSetBaseProperty routine
* that will follow base classes and is used to set static properties in base
* classes
*/
/******************************************************************************/
/******************************* Property Access ******************************/
/******************************************************************************/
/*
* The property get routines follow base classes and utilize the propery
* method access routines. The property set routines do not follow base
* classes. The property ejsSetBase... routines do follow base classes.
*/
/*
* Find a property in an object and return a pointer to it.
* This follows base classes.
*/
EjsProperty *ejsGetProperty(Ejs *ep, EjsVar *op, const char *property)
{
EjsVar *vp, *newOp;
int maxBaseClasses = 50;
do {
if (op->type != EJS_TYPE_OBJECT) {
mprAssert(op->type == EJS_TYPE_OBJECT);
return 0;
}
mprAssert(op->objectState);
vp = ejsGetPropertyMethod(ep, op, property);
if (vp != 0) {
/*
* Found
*/
break;
}
newOp = op->objectState->baseClass;
if (newOp == 0) {
if (op->objectState != ep->objectClass->objectState) {
newOp = ep->objectClass;
}
}
op = newOp;
/*
* A little bit of sanity checking
*/
if (--maxBaseClasses <= 0) {
mprAssert(maxBaseClasses > 0);
break;
}
} while (op);
return ejsGetPropertyPtr(vp);
}
/******************************************************************************/
/*
* Get the property's variable. Optionally create if it does not exist.
*/
EjsVar *ejsGetPropertyAsVar(Ejs *ep, EjsVar *vp, const char *property)
{
return ejsGetVarPtr(ejsGetProperty(ep, vp, property));
}
/******************************************************************************/
/*
* Get the property's value as a binary string.
*/
const uchar *ejsGetPropertyAsBinaryString(Ejs *ep, EjsVar *obj,
const char *property, int *length)
{
EjsVar *vp;
vp = ejsGetVarPtr(ejsGetProperty(ep, obj, property));
if (vp == 0 || ejsVarIsUndefined(vp)) {
return 0;
}
if (vp->type == EJS_TYPE_STRING) {
if (length) {
*length = vp->length;
}
return vp->ustring;
}
return 0;
}
/******************************************************************************/
/*
* Get the property's value as a string.
*/
const char *ejsGetPropertyAsString(Ejs *ep, EjsVar *obj, const char *property)
{
EjsVar *vp;
vp = ejsGetVarPtr(ejsGetProperty(ep, obj, property));
if (vp == 0 || ejsVarIsUndefined(vp)) {
return 0;
}
if (vp->type == EJS_TYPE_STRING) {
return vp->string;
}
return 0;
}
/******************************************************************************/
/*
* Get the property's value as a number.
*/
BLD_FEATURE_NUM_TYPE ejsGetPropertyAsNumber(Ejs *ep, EjsVar *obj,
const char *property)
{
EjsVar *vp;
vp = ejsGetVarPtr(ejsGetProperty(ep, obj, property));
if (vp == 0 || ejsVarIsUndefined(vp)) {
return 0;
}
return ejsVarToNumber(vp);
}
/******************************************************************************/
/*
* Get the property's value as a integer.
*/
int ejsGetPropertyAsInteger(Ejs *ep, EjsVar *obj, const char *property)
{
EjsVar *vp;
vp = ejsGetVarPtr(ejsGetProperty(ep, obj, property));
if (vp == 0 || ejsVarIsUndefined(vp)) {
return 0;
}
return ejsVarToInteger(vp);
}
/******************************************************************************/
/*
* Get the property's value as a boolean.
*/
bool ejsGetPropertyAsBoolean(Ejs *ep, EjsVar *obj, const char *property)
{
EjsVar *vp;
vp = ejsGetVarPtr(ejsGetProperty(ep, obj, property));
if (vp == 0 || ejsVarIsUndefined(vp)) {
return 0;
}
return ejsVarToBoolean(vp);
}
/******************************************************************************/
/*
* Get the property's value as a pointer.
*/
void *ejsGetPropertyAsPtr(Ejs *ep, EjsVar *obj, const char *property)
{
EjsVar *vp;
vp = ejsGetVarPtr(ejsGetProperty(ep, obj, property));
if (vp == 0 || ejsVarIsUndefined(vp)) {
return 0;
}
if (vp->type == EJS_TYPE_PTR) {
return vp->ptr.userPtr;
}
return 0;
}
/******************************************************************************/
/*
* Create a property in the object. This will override any base class
* properties.
*
* MOB -- need to spell out the difference between ejsSetProperty and
* ejsCreateProperty.
*/
EjsProperty *ejsCreateProperty(Ejs *ep, EjsVar *obj, const char *property)
{
EjsVar *vp;
vp = ejsCreatePropertyMethod(ep, obj, property);
return ejsGetPropertyPtr(vp);
}
/******************************************************************************/
/*
* Set a property's variable value. Create the property if it does not exist.
* This routine DOES follow base classes.
*/
EjsProperty *ejsSetBaseProperty(Ejs *ep, EjsVar *op, const char *property,
const EjsVar *value)
{
EjsVar *vp, *newOp;
int maxBaseClasses = 50;
do {
if (op->type != EJS_TYPE_OBJECT) {
mprAssert(op->type == EJS_TYPE_OBJECT);
return 0;
}
mprAssert(op->objectState);
vp = ejsGetPropertyMethod(ep, op, property);
if (vp != 0) {
/*
* Found
*/
vp = ejsSetPropertyMethod(ep, op, property, value);
break;
}
newOp = op->objectState->baseClass;
if (newOp == 0) {
if (op->objectState != ep->objectClass->objectState) {
newOp = ep->objectClass;
}
}
op = newOp;
/*
* A little bit of sanity checking
*/
if (--maxBaseClasses <= 0) {
mprAssert(maxBaseClasses > 0);
break;
}
} while (op);
return ejsGetPropertyPtr(vp);
}
/******************************************************************************/
/*
* Set a property's variable value. Create the property if it does not exist.
* This does NOT follow base classes. Okay when updating instance properties,
* but not for class (static) properties. This does a shallow copy which
* will copy references.
*/
EjsProperty *ejsSetProperty(Ejs *ep, EjsVar *obj, const char *property,
const EjsVar *value)
{
EjsVar *vp;
vp = ejsSetPropertyMethod(ep, obj, property, value);
return ejsGetPropertyPtr(vp);
}
/******************************************************************************/
/*
* Set a property's variable value by assigning the given value. The caller
* must NOT free value as it is assigned directly into the property's value.
*/
EjsProperty *ejsSetPropertyAndFree(Ejs *ep, EjsVar *obj,
const char *property, EjsVar *value)
{
EjsVar *vp;
vp = ejsSetPropertyMethod(ep, obj, property, value);
ejsFree(ep, value, EJS_SLAB_VAR);
return ejsGetPropertyPtr(vp);
}
/******************************************************************************/
EjsProperty *ejsSetPropertyToCMethod(Ejs *ep, EjsVar *vp, const char *prop,
EjsCMethod fn, void *userData, int flags)
{
EjsVar v;
ejsInitVar(&v, EJS_TYPE_CMETHOD);
v.cMethod.fn = fn;
v.cMethod.userData = userData;
v.flags = flags;
return ejsSetProperty(ep, vp, prop, &v);
}
/******************************************************************************/
EjsProperty *ejsSetPropertyToBoolean(Ejs *ep, EjsVar *vp, const char *prop,
int value)
{
EjsVar v;
ejsInitVar(&v, EJS_TYPE_BOOL);
v.boolean = value;
return ejsSetProperty(ep, vp, prop, &v);
}
/******************************************************************************/
#if BLD_FEATURE_FLOATING_POINT
EjsProperty *ejsSetPropertyToFloat(Ejs *ep, EjsVar *vp, const char *prop,
double value)
{
EjsVar v;
ejsInitVar(&v, EJS_TYPE_FLOAT);
v.floating = value;
return ejsSetProperty(ep, vp, prop, &v);
}
#endif
/******************************************************************************/
EjsProperty *ejsSetPropertyToInteger(Ejs *ep, EjsVar *vp, const char *prop,
int value)
{
EjsVar v;
ejsInitVar(&v, EJS_TYPE_INT);
v.integer = value;
return ejsSetProperty(ep, vp, prop, &v);
}
/******************************************************************************/
#if BLD_FEATURE_INT64
EjsProperty *ejsSetPropertyToInteger64(Ejs *ep, EjsVar *vp, const char *prop,
int64 value)
{
EjsVar v;
ejsInitVar(&v, EJS_TYPE_INT64);
v.integer64 = value;
return ejsSetProperty(ep, vp, prop, &v);
}
#endif
/******************************************************************************/
EjsProperty *ejsSetPropertyToNull(Ejs *ep, EjsVar *vp, const char *prop)
{
EjsVar v;
ejsInitVar(&v, EJS_TYPE_NULL);
return ejsSetProperty(ep, vp, prop, &v);
}
/******************************************************************************/
EjsProperty *ejsSetPropertyToMethod(Ejs *ep, EjsVar *vp, const char *prop,
const char *body, MprArray *args, int flags)
{
return ejsSetPropertyAndFree(ep, vp, prop,
ejsCreateMethodVar(ep, body, args, flags));
}
/******************************************************************************/
EjsProperty *ejsSetPropertyToNumber(Ejs *ep, EjsVar *vp, const char *prop,
EjsNum value)
{
return ejsSetPropertyAndFree(ep, vp, prop, ejsCreateNumberVar(ep, value));
}
/******************************************************************************/
EjsProperty *ejsSetPropertyToStringCMethod(Ejs *ep, EjsVar *vp,
const char *prop, EjsStringCMethod fn, void *userData, int flags)
{
EjsVar v;
ejsInitVar(&v, EJS_TYPE_STRING_CMETHOD);
v.cMethodWithStrings.fn = fn;
v.cMethodWithStrings.userData = userData;
v.flags = flags;
return ejsSetProperty(ep, vp, prop, &v);
}
/******************************************************************************/
EjsProperty *ejsSetPropertyToString(Ejs *ep, EjsVar *vp, const char *prop,
const char *value)
{
EjsProperty *pp;
EjsVar v;
ejsInitVar(&v, EJS_TYPE_STRING);
/* FUTURE OPT */
v.string = mprStrdupInternal(EJS_LOC_ARGS(ep), value);
if (v.string == 0) {
return 0;
}
v.length = strlen(v.string);
v.allocatedData = 1;
pp = ejsSetProperty(ep, vp, prop, &v);
mprFree(v.string);
return pp;
}
/******************************************************************************/
EjsProperty *ejsSetPropertyToBinaryString(Ejs *ep, EjsVar *vp,
const char *prop, const uchar *value, int len)
{
EjsProperty *pp;
EjsVar v;
ejsInitVar(&v, EJS_TYPE_STRING);
/* FUTURE OPT */
v.length = dupString(MPR_LOC_ARGS(ep), &v.ustring, value, len);
if (v.length < 0) {
return 0;
}
v.allocatedData = 1;
pp = ejsSetProperty(ep, vp, prop, &v);
mprFree(v.ustring);
return pp;
}
/******************************************************************************/
EjsProperty *ejsSetPropertyToUndefined(Ejs *ep, EjsVar *vp, const char *prop)
{
EjsVar v;
ejsInitVar(&v, EJS_TYPE_UNDEFINED);
return ejsSetProperty(ep, vp, prop, &v);
}
/******************************************************************************/
EjsProperty *ejsSetPropertyToPtr(Ejs *ep, EjsVar *vp, const char *prop,
void *ptr, EjsDestructor destructor)
{
EjsVar v;
ejsInitVar(&v, EJS_TYPE_PTR);
v.ptr.userPtr = ptr;
v.ptr.destructor = destructor;
v.allocatedData = 1;
return ejsSetProperty(ep, vp, prop, &v);
}
/******************************************************************************/
EjsProperty *ejsSetPropertyToNewObj(Ejs *ep, EjsVar *vp, const char *prop,
const char *className, MprArray *args)
{
return ejsSetPropertyAndFree(ep, vp, prop,
ejsCreateObjUsingArgv(ep, 0, className, args));
}
/******************************************************************************/
EjsProperty *ejsSetPropertyToObj(Ejs *ep, EjsVar *op, const char *prop)
{
return ejsSetPropertyAndFree(ep, op, prop, ejsCreateObjVar(ep));
}
/******************************************************************************/
/*
* Convenience routines
*/
EjsVar *ejsSetPropertyToObjAsVar(Ejs *ep, EjsVar *op, const char *prop)
{
return ejsGetVarPtr(ejsSetPropertyToObj(ep, op, prop));
}
/******************************************************************************/
/******************************************************************************/
/******************************************************************************/
/******************************************************************************/
/*
* Create a script method
*/
EjsProperty *ejsDefineMethod(Ejs *ep, EjsVar *vp, const char *prop,
const char *body, MprArray *args)
{
if (vp == 0) {
vp = ejsGetGlobalObj(ep);
}
return ejsSetPropertyToMethod(ep, vp, prop, body, args, 0);
}
/******************************************************************************/
/*
* Create a C language method
*/
EjsProperty *ejsDefineCMethod(Ejs *ep, EjsVar *vp, const char *prop,
EjsCMethod fn, int flags)
{
if (vp == 0) {
vp = ejsGetGlobalObj(ep);
}
return ejsSetPropertyToCMethod(ep, vp, prop, fn, 0, flags);
}
/******************************************************************************/
/*
* Define accessors
*/
EjsProperty *ejsDefineAccessors(Ejs *ep, EjsVar *vp, const char *prop,
const char *getBody, const char *setBody)
{
EjsProperty *pp;
MprArray *args;
char *propName;
if (vp == 0) {
vp = ejsGetGlobalObj(ep);
}
if (ejsSetPropertyToMethod(ep, vp, prop, getBody, 0, EJS_GET_ACCESSOR) < 0){
ejsMemoryError(ep);
return 0;
}
/* MOB -- OPT to use SLAB */
/* MOB -- need to encapsulate this logic */
if (mprAllocStrcat(MPR_LOC_ARGS(ep), &propName, EJS_MAX_ID+5, 0,
"-set-", prop, NULL) < 0) {
ejsMemoryError(ep);
return 0;
}
args = mprCreateItemArray(ep, EJS_INC_ARGS, EJS_MAX_ARGS);
mprAddItem(args, mprStrdup(args, "value"));
pp = ejsSetPropertyToMethod(ep, vp, propName, setBody, args,
EJS_SET_ACCESSOR);
mprFree(propName);
if (pp == 0) {
ejsMemoryError(ep);
return 0;
}
return pp;
}
/******************************************************************************/
/*
* Define C accessors
*/
EjsProperty *ejsDefineCAccessors(Ejs *ep, EjsVar *vp, const char *prop,
EjsCMethod getFn, EjsCMethod setFn, int flags)
{
EjsProperty *pp;
char *propName;
if (vp == 0) {
vp = ejsGetGlobalObj(ep);
}
pp = ejsSetPropertyToCMethod(ep, vp, prop, getFn, 0,
flags | EJS_GET_ACCESSOR);
if (pp == 0) {
ejsMemoryError(ep);
return 0;
}
/* MOB -- OPT to use SLAB */
if (mprAllocStrcat(MPR_LOC_ARGS(ep), &propName, EJS_MAX_ID + 5, 0,
"-set-", prop, NULL) < 0) {
ejsMemoryError(ep);
return 0;
}
pp = ejsSetPropertyToCMethod(ep, vp, propName, setFn, 0,
flags | EJS_SET_ACCESSOR);
mprFree(propName);
if (pp == 0) {
ejsMemoryError(ep);
return 0;
}
return pp;
}
/******************************************************************************/
/*
* Create a C language method with string arguments
*/
EjsProperty *ejsDefineStringCMethod(Ejs *ep, EjsVar *vp, const char *prop,
EjsStringCMethod fn, int flags)
{
if (vp == 0) {
vp = ejsGetGlobalObj(ep);
}
return ejsSetPropertyToStringCMethod(ep, vp, prop, fn, 0, flags);
}
/******************************************************************************/
void ejsSetCMethodUserData(EjsVar *obj, void *userData)
{
/*
* This is a little dirty. We rely on the userData being in the same
* place in the var structure.
*/
obj->cMethod.userData = userData;
}
/******************************************************************************/
void ejsSetVarFlags(EjsVar *obj, int flags)
{
obj->flags = flags;
}
/******************************************************************************/
void *ejsGetCMethodUserData(EjsVar *obj)
{
return obj->cMethod.userData;
}
/******************************************************************************/
int ejsGetVarFlags(EjsVar *obj)
{
return obj->flags;
}
/******************************************************************************/
void ejsSetObjDestructor(Ejs *ep, EjsVar *obj, EjsDestructor destructor)
{
obj->objectState->destructor = destructor;
}
/******************************************************************************/
void ejsClearObjDestructor(Ejs *ep, EjsVar *obj)
{
obj->objectState->destructor = 0;
}
/******************************************************************************/
/*
* Create a new property
*/
static EjsProperty *allocProperty(Ejs *ep, EjsVar *op, const char *property,
int propertyIndex, EjsProperty *last)
{
EjsProperty *prop;
EjsObj *obj;
obj = op->objectState;
/*
* Allocate the property using the memory context of the owning object
*/
prop = ejsAllocProperty(EJS_LOC_ARGS(obj->ejs));
if (prop == 0) {
return 0;
}
if (mprStrcpy(prop->name, sizeof(prop->name), property) < 0) {
ejsError(ep, EJS_REFERENCE_ERROR,
"Property name %s is too long. Max is %d letters.",
prop->name, EJS_MAX_ID);
return 0;
}
ejsSetVarName(ep, ejsGetVarPtr(prop), &prop->name[0]);
/*
* Do hash linkage
*/
if (last) {
last->hashNext = prop;
} else {
obj->propertyHash[propertyIndex] = prop;
}
#if BLD_DEBUG
prop->link.propertyName = prop->name;
prop->link.property = prop;
prop->link.head = &obj->link;
#endif
/*
* Inserting before the dummy head will append to the end
*/
linkPropertyBefore(obj, &obj->link, &prop->link);
obj->numProperties++;
prop->parentObj = obj;
mprAssert(obj->ejs);
return prop;
}
/******************************************************************************/
/*
* Delete a property from this object
*/
int ejsDeleteProperty(Ejs *ep, EjsVar *vp, const char *property)
{
EjsProperty *prop, *last;
EjsObj *obj;
int propertyIndex;
mprAssert(vp);
mprAssert(property && *property);
mprAssert(vp->type == EJS_TYPE_OBJECT);
if (vp->type != EJS_TYPE_OBJECT) {
mprAssert(vp->type == EJS_TYPE_OBJECT);
return MPR_ERR_BAD_ARGS;
}
prop = hashLookup(vp->objectState, property, &propertyIndex, &last);
if (prop == (EjsProperty*) 0) {
return MPR_ERR_NOT_FOUND;
}
obj = vp->objectState;
#if FUTURE
if (prop->readonly) {
mprAssert(! prop->readonly);
return MPR_ERR_READ_ONLY;
}
#endif
/*
* If doing enumerations, then the object will mark preventDelete to
* prevent any properties being deleted and thus disturbing the
* traversal.
*/
if (obj->preventDeleteProp) {
obj->delayedDeleteProp = 1;
prop->delayedDelete = 1;
return 0;
}
/*
* Remove from hash
*/
if (last) {
last->hashNext = prop->hashNext;
} else {
obj->propertyHash[propertyIndex] = prop->hashNext;
}
unlinkProperty(obj, &prop->link);
obj->numProperties--;
/*
* Free any property data and return to the slab
*/
if (prop->var.type != EJS_TYPE_OBJECT) {
ejsClearVar(ep, ejsGetVarPtr(prop));
}
ejsFree(ep, prop, EJS_SLAB_PROPERTY);
return 0;
}
/******************************************************************************/
/*
* Remove a property's value from this object. The property is set to
* undefined.
*/
EjsVar *ejsClearProperty(Ejs *ep, EjsVar *vp, const char *property)
{
EjsProperty *prop;
mprAssert(vp);
mprAssert(property && *property);
mprAssert(vp->type == EJS_TYPE_OBJECT);
if (vp->type != EJS_TYPE_OBJECT) {
mprAssert(vp->type == EJS_TYPE_OBJECT);
return 0;
}
prop = hashLookup(vp->objectState, property, 0, 0);
if (prop == (EjsProperty*) 0) {
return 0;
}
#if FUTURE
if (prop->readonly) {
mprAssert(! prop->readonly);
return 0;
}
#endif
ejsClearVar(ep, &prop->var);
return &prop->var;
}
/******************************************************************************/
/*
* Unlink a property from the ordered list of properties
*/
static void unlinkProperty(EjsObj *obj, EjsPropLink *propLink)
{
propLink->prev->next = propLink->next;
propLink->next->prev = propLink->prev;
}
/******************************************************************************/
#if UNUSED && KEEP
/*
* Insert a link after a specified link.
*/
static void linkPropertyAfter(EjsObj *obj, EjsPropLink *at,
EjsPropLink *propLink)
{
propLink->next = at->next;
propLink->prev = at;
at->next->prev = propLink;
at->next = propLink;
}
#endif
/******************************************************************************/
/*
* Insert a link before a specified link.
*/
static void linkPropertyBefore(EjsObj *obj, EjsPropLink *at,
EjsPropLink *propLink)
{
propLink->prev = at->prev;
propLink->next = at;
at->prev->next = propLink;
at->prev = propLink;
}
/******************************************************************************/
/*
* This routine will sort properties in an object. If propertyName is not
* null, then the properties in op must be objects with a property of the
* name propertyName. If propertyName is null, then the properties of op
* are directly sorted. If order is 1, they are sorted in ascending order.
* If -1, they are sorted in descending order.
*
* NOTE: arrays keep their original index values.
*/
void ejsSortProperties(Ejs *ep, EjsVar *op, EjsSortFn fn,
const char *propertyName, int order)
{
EjsProperty *p1, *p2, *tmp;
EjsPropLink *l1, *l2, *oldL1Spot;
EjsObj *obj;
obj = op->objectState;
p1 = ejsGetFirstProperty(op, 0);
while (p1) {
if (p1->dontEnumerate) {
p1 = ejsGetNextProperty(p1, 0);
continue;
}
p2 = ejsGetFirstProperty(op, 0);
while (p2 && p2 != p1) {
if (p2->dontEnumerate) {
p2 = ejsGetNextProperty(p2, 0);
continue;
}
if (fn == 0) {
if (propertyName) {
fn = sortByProperty;
} else {
fn = sortAllProperties;
}
}
if (fn(ep, p1, p2, propertyName, order) < 0) {
l1 = &p1->link;
l2 = &p2->link;
/*
* Swap the properties without disturbing the hash chains.
* l1 is always after l2 in the list. Unlink l1 and remember
* the one after l1.
*/
oldL1Spot = l1->next;
unlinkProperty(obj, l1);
/*
* Manually reinsert l1 by replacing l2 with l1. l2 is out of
* the chain.
*/
l2->prev->next = l1;
l2->next->prev = l1;
l1->prev = l2->prev;
l1->next = l2->next;
/*
* Reinsert l2 before the spot where l1 was.
*/
linkPropertyBefore(obj, oldL1Spot, l2);
/*
* Swap the pointers so we continue to traverse correctly
*/
tmp = p1;
p1 = p2;
p2 = tmp;
}
p2 = ejsGetNextProperty(p2, 0);
}
p1 = ejsGetNextProperty(p1, 0);
}
}
/******************************************************************************/
/*
* Sort properties. Strings are sorted in ascending ASCII collating sequence
* Numbers are sorted in increasing numerical order.
*/
static int sortAllProperties(Ejs *ep, EjsProperty *p1, EjsProperty *p2,
const char *propertyName, int order)
{
EjsVar *v1, *v2;
char *buf1, *buf2;
int rc, buf1Alloc;
v1 = ejsGetVarPtr(p1);
v2 = ejsGetVarPtr(p2);
if (v1->type == v2->type) {
/* MOB -- should support Numbers */
if (v1->type == EJS_TYPE_INT) {
if (v1->integer < v2->integer) {
return - order;
} else if (v1->integer == v2->integer) {
return 0;
}
return order;
#if BLD_FEATURE_FLOATING_POINT
} else if (v1->type == EJS_TYPE_FLOAT) {
if (v1->floating < v2->floating) {
return - order;
} else if (v1->floating == v2->floating) {
return 0;
}
return order;
#endif
} else if (v1->type == EJS_TYPE_STRING) {
/* MOB -- need binary support ? */
return strcmp(v1->string, v2->string) * order;
} else {
buf1 = ejsVarToStringEx(ep, v1, &buf1Alloc);
buf2 = ejsVarToString(ep, v2);
rc = strcmp(buf1, buf2);
if (buf1Alloc) {
mprFree(buf1);
}
return rc * order;
}
} else {
/* Type mismatch in array */
return 0;
}
return 0;
}
/******************************************************************************/
/*
* Sort an object by a given property.
*/
static int sortByProperty(Ejs *ep, EjsProperty *p1, EjsProperty *p2,
const char *propertyName, int order)
{
EjsVar *o1, *o2, *v1, *v2;
char *buf1, *buf2;
int rc, buf1Alloc;
o1 = ejsGetVarPtr(p1);
o2 = ejsGetVarPtr(p2);
if (!ejsVarIsObject(o1) || !ejsVarIsObject(o2)) {
mprAssert(ejsVarIsObject(o1));
mprAssert(ejsVarIsObject(o2));
return 0;
}
v1 = ejsGetPropertyAsVar(ep, o1, propertyName);
v2 = ejsGetPropertyAsVar(ep, o2, propertyName);
if (v1 == 0 || v2 == 0) {
/* Property name not found */
return 0;
}
if (v1->type != v2->type) {
mprAssert(v1->type == v2->type);
return 0;
}
if (v1->type == v2->type) {
/* MOB -- should support Numbers */
if (v1->type == EJS_TYPE_INT) {
if (v1->integer < v2->integer) {
return -order;
} else if (v1->integer == v2->integer) {
return 0;
}
return order;
#if BLD_FEATURE_FLOATING_POINT
} else if (v1->type == EJS_TYPE_FLOAT) {
if (v1->floating < v2->floating) {
return -order;
} else if (v1->floating == v2->floating) {
return 0;
}
return order;
#endif
} else if (v1->type == EJS_TYPE_STRING) {
/* MOB -- need binary support ? */
return strcmp(v1->string, v2->string) * order;
} else {
buf1 = ejsVarToStringEx(ep, v1, &buf1Alloc);
buf2 = ejsVarToString(ep, v2);
rc = strcmp(buf1, buf2);
if (buf1Alloc) {
mprFree(buf1);
}
return rc * order;
}
} else {
/* Type mismatch in array */
return 0;
}
return 0;
}
/******************************************************************************/
/*
* Set a property's name
*/
void ejsSetPropertyName(EjsProperty *pp, const char *property)
{
mprStrcpy(pp->name, sizeof(pp->name), property);
}
/******************************************************************************/
int ejsMakePropertyEnumerable(EjsProperty *prop, int enumerate)
{
int oldValue;
oldValue = prop->dontEnumerate;
prop->dontEnumerate = !enumerate;
return oldValue;
}
/******************************************************************************/
void ejsMakePropertyPrivate(EjsProperty *prop, int isPrivate)
{
prop->isPrivate = isPrivate;
}
/******************************************************************************/
/*
* Make a variable read only. Can still be deleted.
*/
void ejsMakePropertyReadOnly(EjsProperty *prop, int readonly)
{
prop->readonly = readonly;
}
/******************************************************************************/
int ejsMakeObjPermanent(EjsVar *vp, int permanent)
{
int oldValue;
if (vp && vp->type == EJS_TYPE_OBJECT) {
oldValue = vp->objectState->permanent;
vp->objectState->permanent = permanent;
} else {
oldValue = 0;
}
return oldValue;
}
/******************************************************************************/
int ejsMakeObjLive(EjsVar *vp, bool alive)
{
int oldValue;
oldValue = 0;
if (vp && vp->type == EJS_TYPE_OBJECT) {
oldValue = vp->objectState->alive;
vp->objectState->alive = alive;
} else {
oldValue = 0;
}
return oldValue;
}
/******************************************************************************/
void ejsMakeClassNoConstructor(EjsVar *vp)
{
mprAssert(vp->type == EJS_TYPE_OBJECT);
if (vp->type == EJS_TYPE_OBJECT) {
vp->objectState->noConstructor = 1;
}
}
/******************************************************************************/
/*
* Get the count of properties.
*/
int ejsGetPropertyCount(EjsVar *vp)
{
EjsProperty *pp;
EjsPropLink *lp, *head;
int count;
mprAssert(vp);
if (vp->type != EJS_TYPE_OBJECT) {
return 0;
}
count = 0;
head = &vp->objectState->link;
for (lp = head->next; lp != head; lp = lp->next) {
pp = ejsGetPropertyFromLink(lp);
if (! pp->dontEnumerate) {
count++;
}
}
return count;
}
/******************************************************************************/
/*
* Get the first property in an object. Used for walking all properties in an
* object. This will only enumerate properties in this class and not in base
* classes.
*/
EjsProperty *ejsGetFirstProperty(const EjsVar *op, int flags)
{
EjsProperty *pp;
EjsObj *obj;
EjsPropLink *head, *lp;
mprAssert(op);
mprAssert(op->type == EJS_TYPE_OBJECT);
if (op->type != EJS_TYPE_OBJECT) {
mprAssert(op->type == EJS_TYPE_OBJECT);
return 0;
}
pp = 0;
do {
obj = op->objectState;
head = &obj->link;
lp = head->next;
while (lp != head) {
pp = ejsGetPropertyFromLink(lp);
if (! pp->dontEnumerate || (flags & EJS_ENUM_HIDDEN)) {
break;
}
lp = lp->next;
}
if (lp != head || op->type != EJS_TYPE_OBJECT ||
!(flags & EJS_ENUM_CLASSES)) {
break;
}
op = obj->baseClass;
} while (lp == 0 && op);
return pp;
}
/******************************************************************************/
/*
* Get the next property in sequence. This will only enumerate properties in
* this class and not in base classes.
*/
EjsProperty *ejsGetNextProperty(EjsProperty *last, int flags)
{
EjsProperty *pp;
EjsObj *obj;
EjsPropLink *lp, *head;
obj = last->parentObj;
lp = last->link.next;
head = &obj->link;
pp = 0;
while (obj) {
while (lp != head) {
pp = ejsGetPropertyFromLink(lp);
if (! pp->dontEnumerate || (flags & EJS_ENUM_HIDDEN)) {
break;
}
lp = lp->next;
}
if (lp != head || !(flags & EJS_ENUM_CLASSES)) {
break;
}
/*
* Now iterate over properties in base classes (down the chain)
*/
if (obj->baseClass == 0) {
break;
}
obj = obj->baseClass->objectState;
if (obj == 0) {
break;
}
}
return pp;
}
/******************************************************************************/
/*
* Find a variable given a variable name and return the parent object and
* the variable itself. This routine supports literal variable and property
* names that may be objects or arrays but may NOT have expressions.
* Returns -1 on errors or if the variable is not found.
* FUTURE -- Needs OPT
*/
EjsVar *ejsFindProperty(Ejs *ep, EjsVar **obj, char **property, EjsVar *global,
EjsVar *local, const char *fullName, int create)
{
EjsProperty *currentProp;
EjsVar *currentObj;
/* MOB -- WARNING BIG */
char tokBuf[EJS_MAX_ID], propertyName[EJS_MAX_ID];
char *token, *next, *cp, *endp;
mprAssert(fullName && *fullName);
currentProp = 0;
currentObj = 0;
if (global == 0) {
global = ep->global;
}
if (obj) {
*obj = 0;
}
if (property) {
*property = 0;
}
if (fullName == 0) {
return 0;
}
next = (char*) fullName;
token = getNextVarToken(&next, tokBuf, sizeof(tokBuf));
mprStrcpy(propertyName, sizeof(propertyName), token);
if (local) {
currentProp = ejsGetProperty(ep, local, token);
currentObj = local;
}
if (currentProp == 0) {
currentProp = ejsGetProperty(ep, global, token);
currentObj = global;
}
token = getNextVarToken(&next, tokBuf, sizeof(tokBuf));
while (currentObj != 0 && token != 0 && *token) {
if (currentProp == 0) {
return 0;
}
currentObj = &currentProp->var;
currentProp = 0;
if (*token == '[') {
token = getNextVarToken(&next, tokBuf, sizeof(tokBuf));
mprStrcpy(propertyName, sizeof(propertyName), token);
cp = propertyName;
if (*cp == '\"') {
cp++;
if ((endp = strchr(cp, '\"')) != 0) {
*endp = '\0';
}
} else if (*cp == '\'') {
cp++;
if ((endp = strchr(cp, '\'')) != 0) {
*endp = '\0';
}
}
currentProp = ejsGetProperty(ep, currentObj, propertyName);
token = getNextVarToken(&next, tokBuf, sizeof(tokBuf));
if (*token != ']') {
return 0;
}
} else if (*token == '.') {
token = getNextVarToken(&next, tokBuf, sizeof(tokBuf));
if (!isalpha((int) token[0]) &&
token[0] != '_' && token[0] != '$') {
return 0;
}
mprStrcpy(propertyName, sizeof(propertyName), token);
currentProp = ejsGetProperty(ep, currentObj, token);
} else {
currentProp = ejsGetProperty(ep, currentObj, token);
}
if (next == 0 || *next == '\0') {
break;
}
token = getNextVarToken(&next, tokBuf, sizeof(tokBuf));
}
if (obj) {
*obj = currentObj;
}
if (currentProp == 0 && currentObj >= 0 && create) {
currentProp = ejsCreateSimpleProperty(ep, currentObj, propertyName);
}
if (property) {
*property = currentProp->name;
}
return ejsGetVarPtr(currentProp);
}
/******************************************************************************/
/*
* Get the next token as part of a variable specification. This will return
* a pointer to the next token and will return a pointer to the next token
* (after this one) in "next". The tokBuf holds the parsed token.
*/
static char *getNextVarToken(char **next, char *tokBuf, int tokBufLen)
{
char *start, *cp;
int len;
start = *next;
while (isspace((int) *start) || *start == '\n' || *start == '\r') {
start++;
}
cp = start;
if (*cp == '.' || *cp == '[' || *cp == ']') {
cp++;
} else {
while (*cp && *cp != '.' && *cp != '[' && *cp != ']' &&
!isspace((int) *cp) && *cp != '\n' && *cp != '\r') {
cp++;
}
}
len = mprMemcpy(tokBuf, tokBufLen - 1, start, cp - start);
tokBuf[len] = '\0';
*next = cp;
return tokBuf;
}
/******************************************************************************/
EjsVar *ejsGetGlobalClass(Ejs *ep)
{
return ep->global;
}
/******************************************************************************/
/*************************** Property Access Methods **************************/
/******************************************************************************/
/*
* Create an undefined property. This routine calls the object method hooks.
*/
/* MOB -- better suffix than "Method" */
EjsVar *ejsCreatePropertyMethod(Ejs *ep, EjsVar *op, const char *property)
{
EjsVar *vp;
mprAssert(ep);
mprAssert(op);
mprAssert(property && *property);
if (op == 0) {
return 0;
}
mprAssert(op->type == EJS_TYPE_OBJECT);
mprAssert(op->objectState);
if (op->objectState == 0) {
return 0;
}
if (op->objectState->methods == 0) {
vp = ejsGetVarPtr(ejsCreateSimpleProperty(ep, op, property));
} else {
vp = (op->objectState->methods->createProperty)(ep, op, property);
}
if (vp == 0) {
mprAssert(vp);
op->objectState->hasErrors = 1;
return 0;
}
/*
* FUTURE - find a better way.
*/
if (op->isArray) {
ejsSetArrayLength(ep, op, property, 0, 0);
}
return vp;
}
/******************************************************************************/
int ejsDeletePropertyMethod(Ejs *ep, EjsVar *op, const char *property)
{
int rc;
mprAssert(ep);
mprAssert(op);
mprAssert(property && *property);
if (op == 0) {
return -1;
}
mprAssert(op->type == EJS_TYPE_OBJECT);
mprAssert(op->objectState);
if (op->objectState == 0) {
return -1;
}
if (op->objectState->methods == 0) {
rc = ejsDeleteProperty(ep, op, property);
} else {
rc = (op->objectState->methods->deleteProperty)(ep, op, property);
}
if (rc < 0) {
op->objectState->hasErrors = 1;
}
op->objectState->dirty = 1;
return rc;
}
/******************************************************************************/
/*
* Set the value of a property. Create if it does not exist
* If the object has property accessor methods defined, use those.
*/
EjsVar *ejsSetPropertyMethod(Ejs *ep, EjsVar *op, const char *property,
const EjsVar *value)
{
EjsVar *vp;
mprAssert(ep);
mprAssert(op);
mprAssert(property && *property);
mprAssert(value);
if (op == 0) {
return 0;
}
mprAssert(op->type == EJS_TYPE_OBJECT);
mprAssert(op->objectState);
if (op->objectState == 0) {
return 0;
}
if (op->objectState->methods == 0) {
vp = ejsGetVarPtr(ejsCreateSimpleProperty(ep, op, property));
if (vp && ejsWriteVar(ep, vp, (EjsVar*) value, EJS_SHALLOW_COPY) < 0) {
mprAssert(0);
op->objectState->hasErrors = 1;
return 0;
}
} else {
vp = (op->objectState->methods->setProperty)(ep, op, property, value);
}
if (vp == 0) {
mprAssert(vp);
op->objectState->hasErrors = 1;
return 0;
}
if (vp->type == EJS_TYPE_OBJECT) {
/*
* We make an object alive (and subject to garbage collection) when
* it is referenced in some other object. If this is undesirable, the
* caller should make the object permanent while calling this routine
* and then afterward clear the alive bit by calling ejsMakeObjLive().
*/
if (op->objectState != vp->objectState) {
vp->objectState->alive = 1;
}
#if BLD_DEBUG
{
EjsProperty *pp = ejsGetPropertyPtr(vp);
ejsSetVarName(ep, vp, &pp->name[0]);
if (value->propertyName == 0) {
ejsSetVarName(ep, (EjsVar*) value, &pp->name[0]);
}
}
#endif
}
/*
* Trap assignments to array.length. MOB - find a better way.
*/
if (vp->isArrayLength) {
ejsSetArrayLength(ep, op, 0, 0, value);
}
op->objectState->dirty = 1;
return vp;
}
/******************************************************************************/
EjsVar *ejsGetPropertyMethod(Ejs *ep, EjsVar *op, const char *property)
{
mprAssert(ep);
mprAssert(op);
mprAssert(property && *property);
if (op == 0) {
return 0;
}
mprAssert(op->type == EJS_TYPE_OBJECT);
mprAssert(op->objectState);
if (op->objectState == 0) {
return 0;
}
if (op->objectState->methods == 0) {
return ejsGetVarPtr(ejsGetSimpleProperty(ep, op, property));
} else {
return (op->objectState->methods->getProperty)(ep, op, property);
}
}
/******************************************************************************/
/*************************** Advisory Locking Support *************************/
/******************************************************************************/
#if BLD_FEATURE_MULTITHREAD
void ejsLockObj(EjsVar *vp)
{
mprAssert(vp);
mprAssert(vp->type == EJS_TYPE_OBJECT);
mprAssert(vp->objectState);
if (vp->objectState->mutex == 0) {
vp->objectState->mutex = mprCreateLock(vp->objectState->ejs);
}
mprLock(vp->objectState->mutex);
}
/******************************************************************************/
void ejsUnlockObj(EjsVar *vp)
{
mprAssert(vp);
mprAssert(vp->type == EJS_TYPE_OBJECT);
mprAssert(vp->objectState);
if (vp->objectState->mutex) {
mprUnlock(vp->objectState->mutex);
}
}
#endif
/******************************************************************************/
/************************** Internal Support Routines *************************/
/******************************************************************************/
/*
* Create an object.
*/
static EjsObj *createObj(EJS_LOC_DEC(ep, loc))
{
EjsObj *op;
EjsPropLink *lp;
op = (EjsObj*) ejsAllocObj(EJS_LOC_PASS(ep, loc));
if (op == NULL) {
return 0;
}
/*
* The objectState holds the dummy head for the ordered list of properties
*/
lp = &op->link;
lp->next = lp->prev = lp;
#if BLD_DEBUG
/*
* This makes it much easier to debug the list
*/
lp->head = lp;
lp->propertyName = "dummyHead";
#endif
return op;
}
/******************************************************************************/
/*
* Destroy an object. Called by the garbage collector if there are no more
* references to an object.
*/
int ejsDestroyObj(Ejs *ep, EjsObj *obj)
{
EjsProperty *pp;
EjsPropLink *lp, *head, *nextLink;
mprAssert(obj);
if (obj->destructor) {
EjsVar v;
memset(&v, 0, sizeof(v));
v.type = EJS_TYPE_OBJECT;
v.objectState = obj;
ejsSetVarName(ep, &v, "destructor");
#if BLD_FEATURE_ALLOC_LEAK_TRACK
v.gc.allocatedBy = "static";
#endif
if ((obj->destructor)(ep, &v) < 0) {
return -1;
}
}
mprFree(obj->objName);
obj->objName = 0;
/*
* Just for safety. An object may be marked by a GC on the default
* interpreter. After destroying, it won't be on the free list and so
* won't be reset.
*/
obj->gcMarked = 0;
obj->visited = 0;
head = &obj->link;
for (lp = head->next; lp != head; lp = nextLink) {
pp = ejsGetPropertyFromLink(lp);
nextLink = lp->next;
/*
* We don't unlink as we are destroying all properties.
* If an object, we don't need to clear either.
*/
if (pp->var.type != EJS_TYPE_OBJECT) {
ejsClearVar(ep, ejsGetVarPtr(pp));
}
ejsFree(ep, pp, EJS_SLAB_PROPERTY);
}
#if BLD_FEATURE_MULTITHREAD
if (obj->mutex) {
mprDestroyLock(obj->mutex);
}
#endif
ejsFree(ep, obj, EJS_SLAB_OBJ);
return 0;
}
/******************************************************************************/
/*
* Fast hash. The history of this algorithm is part of lost computer science
* folk lore.
*/
static int hash(const char *property)
{
uint sum;
mprAssert(property);
sum = 0;
while (*property) {
sum += (sum * 33) + *property++;
}
return sum % EJS_OBJ_HASH_SIZE;
}
/******************************************************************************/
/*
* Set a new length for an array. If create is non-null, then it is the name
* of a new array index. If delete is set, it is the name of an index being
* deleted. If setLength is set to a variable, it counts the new length for the
* array. Note that create and delete are ignored if they are non-integer
* array indexes (eg. normal properties).
*/
void ejsSetArrayLength(Ejs *ep, EjsVar *obj, const char *create,
const char *delete, const EjsVar *setLength)
{
EjsVar *vp;
char idx[16];
int oldSize, newSize, i;
vp = ejsGetPropertyAsVar(ep, obj, "length");
oldSize = vp->integer;
newSize = oldSize;
if (create) {
if (isdigit(*create)) {
i = atoi(create);
newSize = max(i + 1, oldSize);
}
} else if (delete) {
if (isdigit(*delete)) {
i = atoi(delete);
newSize = (i == (oldSize - 1) ? oldSize - 1 : oldSize);
}
} else {
newSize = setLength->integer;
}
for (i = newSize; i < oldSize; i++) {
mprItoa(idx, sizeof(idx), i);
ejsDeleteProperty(ep, obj, idx);
}
if (ejsWriteVarAsInteger(ep, vp, newSize) == 0) {
mprAssert(0);
}
}
/******************************************************************************/
void ejsClearObjErrors(EjsVar *vp)
{
if (vp == 0 || vp->type != EJS_TYPE_OBJECT || vp->objectState == 0) {
mprAssert(0);
return;
}
vp->objectState->hasErrors = 0;
}
/******************************************************************************/
int ejsObjHasErrors(EjsVar *vp)
{
if (vp == 0 || vp->type != EJS_TYPE_OBJECT || vp->objectState == 0) {
mprAssert(0);
return -1;
}
return vp->objectState->hasErrors;
}
/******************************************************************************/
bool ejsIsObjDirty(EjsVar *vp)
{
mprAssert(vp->type == EJS_TYPE_OBJECT && vp->objectState);
if (vp->type == EJS_TYPE_OBJECT && vp->objectState) {
return vp->objectState->dirty;
}
return 0;
}
/******************************************************************************/
void ejsResetObjDirtyBit(EjsVar *vp)
{
mprAssert(vp->type == EJS_TYPE_OBJECT && vp->objectState);
if (vp->type == EJS_TYPE_OBJECT && vp->objectState) {
vp->objectState->dirty = 0;
}
}
/******************************************************************************/
/*
* Copy a string. Always null terminate.
*/
static int dupString(MPR_LOC_DEC(ctx, loc), uchar **dest, const void *src,
int nbytes)
{
mprAssert(dest);
mprAssert(src);
if (nbytes > 0) {
*dest = mprMemdupInternal(MPR_LOC_PASS(ctx, loc), src, nbytes + 1);
if (*dest == 0) {
return MPR_ERR_MEMORY;
}
} else {
*dest = (uchar*) mprAlloc(ctx, 1);
nbytes = 0;
}
(*dest)[nbytes] = '\0';
return nbytes;
}
/******************************************************************************/
const char *ejsGetVarTypeAsString(EjsVar *vp)
{
switch (vp->type) {
default:
case EJS_TYPE_UNDEFINED:
return "undefined";
case EJS_TYPE_NULL:
return "null";
case EJS_TYPE_BOOL:
return "bool";
case EJS_TYPE_CMETHOD:
return "cmethod";
case EJS_TYPE_FLOAT:
return "float";
case EJS_TYPE_INT:
return "int";
case EJS_TYPE_INT64:
return "int64";
case EJS_TYPE_OBJECT:
return "object";
case EJS_TYPE_METHOD:
return "method";
case EJS_TYPE_STRING:
return "string";
case EJS_TYPE_STRING_CMETHOD:
return "string method";
case EJS_TYPE_PTR:
return "ptr";
}
}
/******************************************************************************/
void *ejsGetVarUserPtr(EjsVar *vp)
{
mprAssert(vp);
mprAssert(vp->type == EJS_TYPE_PTR);
if (!ejsVarIsPtr(vp)) {
return 0;
}
return vp->ptr.userPtr;
}
/******************************************************************************/
void ejsSetVarUserPtr(EjsVar *vp, void *data)
{
mprAssert(vp);
mprAssert(vp->type == EJS_TYPE_PTR);
vp->ptr.userPtr = data;
}
/******************************************************************************/
/*
* Return TRUE if target is a subclass (or the same class) as baseClass.
*/
bool ejsIsSubClass(EjsVar *target, EjsVar *baseClass)
{
do {
if (target->objectState == baseClass->objectState) {
return 1;
}
target = target->objectState->baseClass;
} while (target);
return 0;
}
/******************************************************************************/
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim:tw=78
* vim600: sw=4 ts=4 fdm=marker
* vim<600: sw=4 ts=4
*/