diff options
Diffstat (limited to 'source4/lib/appweb/ejs-2.0/ejs/classes/ejsObject.c')
-rw-r--r-- | source4/lib/appweb/ejs-2.0/ejs/classes/ejsObject.c | 588 |
1 files changed, 588 insertions, 0 deletions
diff --git a/source4/lib/appweb/ejs-2.0/ejs/classes/ejsObject.c b/source4/lib/appweb/ejs-2.0/ejs/classes/ejsObject.c new file mode 100644 index 0000000000..4f2e23beb2 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/classes/ejsObject.c @@ -0,0 +1,588 @@ +/* + * @file ejsObject.c + * @brief Object class + */ +/********************************* Copyright **********************************/ +/* + * @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 + */ +/********************************** Includes **********************************/ + +#include "ejs.h" + +#if BLD_FEATURE_EJS + +/****************************** Forward Declarations **************************/ +/* + * Support routines + */ + +static void formatVar(Ejs *ep, MprBuf *bp, EjsVar *vp); + +/******************************************************************************/ +/* + * Routine to create an object of the desired class. Class name may + * contain "." + * + * The created object will be a stand-alone class NOT entered into the + * properties of any other object. Callers must do this if required. ClassName + * may contain "." and is interpreted relative to "obj" if supplied. + * + * Note: this does not call the constructors for the various objects and base + * classes. + */ + +EjsVar *ejsCreateSimpleObjInternal(EJS_LOC_DEC(ep, loc), const char *className) +{ + EjsVar *baseClass; + + if (className && *className) { + baseClass = ejsGetClass(ep, 0, className); + if (baseClass == 0) { + mprError(ep, MPR_LOC, "Can't find base class %s", className); + return 0; + } + } else { + baseClass = 0; + } + + return ejsCreateSimpleObjUsingClassInt(EJS_LOC_PASS(ep, loc), + baseClass); +} + +/******************************************************************************/ +/* + * Create an object based upon the specified base class object. It will be a + * stand-alone class not entered into the properties of any other object. + * Callers must do this if required. + * + * Note: this does not call the constructors for the various objects and base + * classes. + */ + +EjsVar *ejsCreateSimpleObjUsingClassInt(EJS_LOC_DEC(ep, loc), + EjsVar *baseClass) +{ + EjsVar *vp; + + mprAssert(baseClass); + + if (baseClass == 0) { + mprError(ep, MPR_LOC, "Missing base class\n"); + return 0; + } + + vp = ejsCreateObjVarInternal(EJS_LOC_PASS(ep, loc)); + if (vp == 0) { + return vp; + } + + ejsSetBaseClass(vp, baseClass); + + /* + * This makes all internal method accesses faster + * NOTE: this code is duplicated in ejsCreateSimpleClass + */ + mprAssert(vp->objectState); + vp->objectState->methods = baseClass->objectState->methods; + + return vp; +} + +/******************************************************************************/ + +void ejsSetMethods(Ejs *ep, EjsVar *op) +{ + op->objectState->methods = ep->global->objectState->methods; +} + +/******************************************************************************/ +/******************************** Internal Methods ****************************/ +/******************************************************************************/ + +static EjsVar *createObjProperty(Ejs *ep, EjsVar *obj, const char *property) +{ + return ejsGetVarPtr(ejsCreateSimpleProperty(ep, obj, property)); +} + +/******************************************************************************/ + +static int deleteObjProperty(Ejs *ep, EjsVar *obj, const char *property) +{ + return ejsDeleteProperty(ep, obj, property); +} + +/******************************************************************************/ + +static EjsVar *getObjProperty(Ejs *ep, EjsVar *obj, const char *property) +{ + return ejsGetVarPtr(ejsGetSimpleProperty(ep, obj, property)); +} + +/******************************************************************************/ +/* + * Set the value of a property. Create if it does not exist + */ + +static EjsVar *setObjProperty(Ejs *ep, EjsVar *obj, const char *property, + const EjsVar *value) +{ + EjsProperty *pp; + EjsVar *vp; + + pp = ejsCreateSimpleProperty(ep, obj, property); + if (pp == 0) { + mprAssert(pp); + return 0; + } + vp = ejsGetVarPtr(pp); + if (ejsWriteVar(ep, vp, value, EJS_SHALLOW_COPY) < 0) { + mprAssert(0); + return 0; + } + return ejsGetVarPtr(pp); +} + +/******************************************************************************/ +/*********************************** Constructors *****************************/ +/******************************************************************************/ +#if UNUSED +/* + * Object constructor. We don't use this for speed. Think very carefully if + * you add an object constructor. + */ + +int ejsObjectConstructor(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + return 0; +} + +#endif +/******************************************************************************/ +/******************************** Visible Methods *****************************/ +/******************************************************************************/ + +static int cloneMethod(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + int copyDepth; + + copyDepth = EJS_DEEP_COPY; + + if (argc == 1 && ejsVarToBoolean(argv[0])) { + copyDepth = EJS_RECURSIVE_DEEP_COPY; + } + + ejsWriteVar(ep, ep->result, thisObj, copyDepth); + + return 0; +} + +/******************************************************************************/ + +static int toStringMethod(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + MprBuf *bp; + int saveMaxDepth, saveDepth, saveFlags; + + saveMaxDepth = ep->maxDepth; + + if (argc >= 1) { + ep->maxDepth = ejsVarToInteger(argv[0]); + } else if (ep->maxDepth == 0) { + ep->maxDepth = MAXINT; + } + + saveFlags = ep->flags; + if (argc >= 2) { + if (ejsVarToBoolean(argv[1])) { + ep->flags |= EJS_FLAGS_ENUM_HIDDEN; + } + } + if (argc == 3) { + if (ejsVarToBoolean(argv[2])) { + ep->flags |= EJS_FLAGS_ENUM_BASE; + } + } + + bp = mprCreateBuf(ep, 0, 0); + + saveDepth = ep->depth; + + formatVar(ep, bp, thisObj); + + ep->depth = saveDepth; + ep->maxDepth = saveMaxDepth; + + mprAddNullToBuf(bp); + + ejsWriteVarAsString(ep, ep->result, mprGetBufStart(bp)); + mprFree(bp); + + ep->flags = saveFlags; + + return 0; +} + +/******************************************************************************/ + +static int valueOfMethod(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + if (argc != 0) { + mprAssert(0); + return -1; + } + + switch (thisObj->type) { + default: + case EJS_TYPE_UNDEFINED: + case EJS_TYPE_NULL: + case EJS_TYPE_CMETHOD: + case EJS_TYPE_OBJECT: + case EJS_TYPE_METHOD: + case EJS_TYPE_STRING_CMETHOD: + ejsWriteVar(ep, ep->result, thisObj, EJS_SHALLOW_COPY); + break; + + case EJS_TYPE_STRING: + ejsWriteVarAsInteger(ep, ep->result, atoi(thisObj->string)); + break; + + case EJS_TYPE_BOOL: + case EJS_TYPE_INT: +#if BLD_FEATURE_INT64 + case EJS_TYPE_INT64: +#endif +#if BLD_FEATURE_FLOATING_POINT + case EJS_TYPE_FLOAT: +#endif + ejsWriteVar(ep, ep->result, thisObj, EJS_SHALLOW_COPY); + break; + } + return 0; +} + +/******************************************************************************/ + +static int hashGetAccessor(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + ejsSetReturnValueToInteger(ejs, (int) thisObj->objectState); + return 0; +} + +/******************************************************************************/ + +static int classGetAccessor(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + if (thisObj->objectState == 0 || thisObj->objectState->baseClass == 0) { + ejsSetReturnValueToString(ejs, "object"); + } else { + ejsSetReturnValueToString(ejs, + thisObj->objectState->baseClass->objectState->className); + } + return 0; +} + +/******************************************************************************/ +/* + * Format an object. Called recursively to format properties and contained + * objects. + */ + +static void formatVar(Ejs *ep, MprBuf *bp, EjsVar *vp) +{ + EjsProperty *pp, *first; + EjsVar *propVar, *baseClass; + char *buf, *value; + int i; + + if (vp->type == EJS_TYPE_OBJECT) { + if (!vp->objectState->visited) { + + mprPutStringToBuf(bp, vp->isArray ? "[\n" : "{\n"); + + ep->depth++; + vp->objectState->visited = 1; + + if (ep->depth <= ep->maxDepth) { + first = ejsGetFirstProperty(vp, EJS_ENUM_ALL); + + if (ep->flags & EJS_FLAGS_ENUM_BASE) { + baseClass = vp->objectState->baseClass; + if (baseClass) { + for (i = 0; i < ep->depth; i++) { + mprPutStringToBuf(bp, " "); + } + mprPutStringToBuf(bp, baseClass->objectState->objName); + mprPutStringToBuf(bp, ": /* Base Class */ "); + if (baseClass->objectState == vp->objectState) { + value = "this"; + } else if (ejsRunMethodCmd(ep, baseClass, "toString", + "%d", ep->maxDepth) < 0) { + value = "[object Object]"; + } else { + mprAssert(ejsVarIsString(ep->result)); + value = ep->result->string; + } + mprPutStringToBuf(bp, value); + if (first) { + mprPutStringToBuf(bp, ",\n"); + } + } + } + + pp = first; + while (pp) { + if (! pp->dontEnumerate || + ep->flags & EJS_FLAGS_ENUM_HIDDEN) { + for (i = 0; i < ep->depth; i++) { + mprPutStringToBuf(bp, " "); + } + + if (! vp->isArray) { + mprPutStringToBuf(bp, pp->name); + mprPutStringToBuf(bp, ": "); + } + + propVar = ejsGetVarPtr(pp); + if (propVar->type == EJS_TYPE_OBJECT) { + if (pp->var.objectState == vp->objectState) { + value = "this"; + } else if (ejsRunMethodCmd(ep, propVar, + "toString", "%d", ep->maxDepth) < 0) { + value = "[object Object]"; + } else { + mprAssert(ejsVarIsString(ep->result)); + value = ep->result->string; + } + mprPutStringToBuf(bp, value); + + } else { + formatVar(ep, bp, &pp->var); + } + + pp = ejsGetNextProperty(pp, EJS_ENUM_ALL); + if (pp) { + mprPutStringToBuf(bp, ",\n"); + } + } else { + pp = ejsGetNextProperty(pp, EJS_ENUM_ALL); + } + } + } + vp->objectState->visited = 0; + + mprPutCharToBuf(bp, '\n'); + + ep->depth--; + for (i = 0; i < ep->depth; i++) { + mprPutStringToBuf(bp, " "); + } + mprPutCharToBuf(bp, vp->isArray ? ']' : '}'); + } + + } else if (vp->type == EJS_TYPE_METHOD) { + + 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); + for (i = 0; i < ep->depth; i++) { + mprPutStringToBuf(bp, " "); + } + mprPutStringToBuf(bp, "}"); + + } else { + + if (vp->type == EJS_TYPE_STRING) { + mprPutCharToBuf(bp, '\"'); + } + + /* + * We don't use ejsVarToString for arrays, objects and strings. + * This is because ejsVarToString does not call "obj.toString" + * and it is not required for strings. + * MOB - rc + */ + buf = ejsVarToString(ep, vp); + mprPutStringToBuf(bp, buf); + + if (vp->type == EJS_TYPE_STRING) { + mprPutCharToBuf(bp, '\"'); + } + } +} + +/******************************************************************************/ +/* + * mixin code. Blends code at the "thisObj" level. + */ + +static int mixinMethod(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + EjsProperty *pp; + char *buf; + int fid, i, rc; + + mprAssert(argv); + + /* + * Create a variable scope block set to the current object + */ + rc = 0; + fid = ejsSetBlock(ep, thisObj); + + for (i = 0; i < argc; i++) { + + if (ejsVarIsString(argv[i])) { + rc = ejsEvalScript(ep, argv[i]->string, 0); + + } else if (ejsVarIsObject(argv[i])) { + + /* MOB -- OPT. When we have proper scope chains, we should just + refer to the module and not copy */ + pp = ejsGetFirstProperty(argv[i], EJS_ENUM_ALL); + while (pp) { + ejsSetProperty(ep, thisObj, pp->name, ejsGetVarPtr(pp)); + pp = ejsGetNextProperty(pp, EJS_ENUM_ALL); + } + + } else { + /* MOB - rc */ + buf = ejsVarToString(ep, argv[i]); + rc = ejsEvalScript(ep, buf, 0); + + } + if (rc < 0) { + ejsCloseBlock(ep, fid); + return -1; + } + } + ejsCloseBlock(ep, fid); + return 0; +} + +/******************************************************************************/ +/* + * Create the object class + */ + +int ejsDefineObjectClass(Ejs *ep) +{ + EjsMethods *methods; + EjsProperty *objectProp, *protoProp; + EjsVar *op, *globalClass; + + /* + * Must specially hand-craft the object class as it is the base class + * of all objects. + */ + op = ejsCreateObjVar(ep); + if (op == 0) { + return MPR_ERR_CANT_CREATE; + } + ejsSetClassName(ep, op, "Object"); + + /* + * Don't use a constructor for objects for speed + */ + ejsMakeClassNoConstructor(op); + + /* + * MOB -- should mark properties as public / private and class or instance. + */ + ejsDefineCMethod(ep, op, "clone", cloneMethod, EJS_NO_LOCAL); + ejsDefineCMethod(ep, op, "toString", toStringMethod, EJS_NO_LOCAL); + ejsDefineCMethod(ep, op, "valueOf", valueOfMethod, EJS_NO_LOCAL); + ejsDefineCMethod(ep, op, "mixin", mixinMethod, EJS_NO_LOCAL); + + ejsDefineCAccessors(ep, op, "hash", hashGetAccessor, 0, EJS_NO_LOCAL); + ejsDefineCAccessors(ep, op, "baseClass", classGetAccessor, 0, EJS_NO_LOCAL); + + /* + * MOB -- make this an accessor + */ + protoProp = ejsSetProperty(ep, op, "prototype", op); + if (protoProp == 0) { + ejsFreeVar(ep, op); + return MPR_ERR_CANT_CREATE; + } + + /* + * Setup the internal methods. Most classes will never override these. + * The XML class will. We rely on talloc to free internal. Use "ep" as + * the parent as we need "methods" to live while the interpreter lives. + */ + methods = mprAllocTypeZeroed(ep, EjsMethods); + op->objectState->methods = methods; + + methods->createProperty = createObjProperty; + methods->deleteProperty = deleteObjProperty; + methods->getProperty = getObjProperty; + methods->setProperty = setObjProperty; + + objectProp = ejsSetPropertyAndFree(ep, ep->global, "Object", op); + + /* + * Change the global class to use Object's methods + */ + globalClass = ep->service->globalClass; + globalClass->objectState->methods = methods; + globalClass->objectState->baseClass = ejsGetVarPtr(protoProp); + + ep->objectClass = ejsGetVarPtr(objectProp); + + if (ejsObjHasErrors(ejsGetVarPtr(objectProp))) { + ejsFreeVar(ep, op); + return MPR_ERR_CANT_CREATE; + } + return 0; +} + +/******************************************************************************/ + +#else +void ejsObjectDummy() {} + +/******************************************************************************/ +#endif /* BLD_FEATURE_EJS */ + +/* + * 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 + */ |