mirror of
https://github.com/samba-team/samba.git
synced 2025-12-13 16:23:50 +03:00
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
4034 lines
85 KiB
C
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 = ¤tProp->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
|
|
*/
|