diff options
Diffstat (limited to 'source4/lib/appweb/ejs-2.0/ejs')
44 files changed, 22116 insertions, 0 deletions
diff --git a/source4/lib/appweb/ejs-2.0/ejs/.ignore b/source4/lib/appweb/ejs-2.0/ejs/.ignore new file mode 100644 index 0000000000..47f4ac63b2 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/.ignore @@ -0,0 +1,2 @@ +ejs +future diff --git a/source4/lib/appweb/ejs-2.0/ejs/Makefile b/source4/lib/appweb/ejs-2.0/ejs/Makefile new file mode 100644 index 0000000000..ea6be8c401 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/Makefile @@ -0,0 +1,61 @@ +# +# Makefile for Embedded Javascript (EJS) +# +# Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved. +# + +# +# Ejs may be linked into shared handlers so we must build the objects both +# shared and static if --shared was specified to configure. +# +COMPILE := *.c +EXPORT_OBJECTS := yes +PRE_DIRS := classes system db +MAKE_IFLAGS := -I../mpr -I../exml + +include make.dep + +ifeq ($(BLD_PRODUCT),ejs) +POST_DIRS := package +endif + +ifeq ($(BLD_FEATURE_TEST),1) +POST_DIRS += test +endif + +ifeq ($(BLD_FEATURE_EJS_DB),1) +LIBS += sqlite +endif + +TARGETS += $(BLD_BIN_DIR)/libejs$(BLD_LIB) +TARGETS += $(BLD_BIN_DIR)/ejs$(BLD_EXE) + +ifeq ($(BLD_FEATURE_EJS),1) +compileExtra: $(TARGETS) +endif + +$(BLD_BIN_DIR)/libejs$(BLD_LIB): files \ + $(shell BLD_OBJ=$(BLD_OBJ) \; BLD_OBJ_DIR=$(BLD_OBJ_DIR) \; \ + eval echo `cat files`) + @bld --library $(BLD_BIN_DIR)/libejs \ + --objectsDir $(BLD_OBJ_DIR) --objectList files \ + --libs "exml mpr $(LIBS)" + +$(BLD_BIN_DIR)/ejs$(BLD_EXE): $(BLD_BIN_DIR)/libejs$(BLD_LIB) \ + $(BLD_BIN_DIR)/libmpr$(BLD_LIB) \ + $(BLD_BIN_DIR)/libejs$(BLD_LIB) $(FILES) + @bld --executable $(BLD_BIN_DIR)/ejs$(BLD_EXE) \ + --rpath "$(BLD_PREFIX)/bin" \ + --preferStatic --smartLibs "ejs exml mpr $(LIBS)" \ + --objectsDir $(BLD_OBJ_DIR) \ + --objects "$(BLD_OBJ_DIR)/ejsCmd$(BLD_OBJ)" + +cleanExtra: + @echo "rm -f $(TARGETS)" | $(BLDOUT) + @rm -f $(TARGETS) + @rm -f $(BLD_BIN_DIR)/libejs.* + +## Local variables: +## tab-width: 4 +## End: +## vim: tw=78 sw=4 ts=4 diff --git a/source4/lib/appweb/ejs-2.0/ejs/classes/.ignore b/source4/lib/appweb/ejs-2.0/ejs/classes/.ignore new file mode 100644 index 0000000000..fb5a29031e --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/classes/.ignore @@ -0,0 +1 @@ +.updated diff --git a/source4/lib/appweb/ejs-2.0/ejs/classes/Makefile b/source4/lib/appweb/ejs-2.0/ejs/classes/Makefile new file mode 100644 index 0000000000..ce12bb3829 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/classes/Makefile @@ -0,0 +1,21 @@ +# +# Makefile to build the EJS Classes +# +# Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved. +# + +COMPILE := *.c +EXPORT_OBJECTS := yes +MAKE_IFLAGS := -I.. -I../../mpr -I../../exml + +include make.dep + +compileExtra: .updated + +.updated: $(FILES) + @touch .updated + +## Local variables: +## tab-width: 4 +## End: +## vim: tw=78 sw=4 ts=4 diff --git a/source4/lib/appweb/ejs-2.0/ejs/classes/ejsArray.c b/source4/lib/appweb/ejs-2.0/ejs/classes/ejsArray.c new file mode 100644 index 0000000000..feb64b1aa8 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/classes/ejsArray.c @@ -0,0 +1,167 @@ +/* + * @file ejsArray.c + * @brief Array 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 + +/************************************ Code ************************************/ + +int ejsDefineArrayClass(Ejs *ep) +{ + if (ejsDefineClass(ep, "Array", "Object", ejsArrayConstructor) == 0) { + return MPR_ERR_CANT_INITIALIZE; + } + return 0; +} + +/******************************************************************************/ +/* + * Routine to create the base array type + */ + +EjsVar *ejsCreateArrayInternal(EJS_LOC_DEC(ep, loc), int size) +{ + EjsProperty *pp; + EjsVar *obj, *vp; + + /* MOB -- need to supply hash size -- max(size, 503)); */ + + obj = ejsCreateSimpleObjInternal(EJS_LOC_PASS(ep, loc), "Array"); + if (obj == 0) { + mprAssert(0); + return obj; + } + obj->isArray = 1; + + /* MOB -- call constructor here and replace this code */ + + pp = ejsSetPropertyToInteger(ep, obj, "length", size); + ejsMakePropertyEnumerable(pp, 0); + + vp = ejsGetVarPtr(pp); + vp->isArrayLength = 1; + + return obj; +} + +/******************************************************************************/ + +EjsVar *ejsAddArrayElt(Ejs *ep, EjsVar *op, EjsVar *element, + EjsCopyDepth copyDepth) +{ + EjsProperty *pp; + EjsVar *vp; + char idx[16]; + int length; + + mprAssert(op->isArray); + + length = ejsGetPropertyAsInteger(ep, op, "length"); + + mprItoa(idx, sizeof(idx), length); + pp = ejsCreateProperty(ep, op, idx); + vp = ejsGetVarPtr(pp); + + ejsWriteVar(ep, vp, element, copyDepth); + + ejsSetPropertyToInteger(ep, op, "length", length + 1); + + return vp; +} + +/******************************************************************************/ +/* + * Constructor + */ + +int ejsArrayConstructor(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + EjsProperty *pp; + EjsVar *vp; + char idx[16]; + int i, max; + + thisObj->isArray = 1; + max = 0; + + if (argc > 0) { + if (argc == 1 && ejsVarIsNumber(argv[0])) { + /* + * x = new Array(size); + */ + max = (int) ejsVarToInteger(argv[0]); + + } else { + /* + * x = new Array(element0, element1, ..., elementN): + */ + max = argc; + for (i = 0; i < max; i++) { + mprItoa(idx, sizeof(idx), i); + pp = ejsCreateSimpleProperty(ep, thisObj, idx); + vp = ejsGetVarPtr(pp); + ejsWriteVar(ep, vp, argv[i], EJS_SHALLOW_COPY); + } + } + } + + pp = ejsCreateSimpleProperty(ep, thisObj, "length"); + ejsMakePropertyEnumerable(pp, 0); + vp = ejsGetVarPtr(pp); + ejsWriteVarAsInteger(ep, vp, max); + vp->isArrayLength = 1; + + return 0; +} + +/******************************************************************************/ + +#else +void ejsArrayDummy() {} + +/******************************************************************************/ +#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 + */ diff --git a/source4/lib/appweb/ejs-2.0/ejs/classes/ejsDate.c b/source4/lib/appweb/ejs-2.0/ejs/classes/ejsDate.c new file mode 100755 index 0000000000..096316a822 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/classes/ejsDate.c @@ -0,0 +1,197 @@ +/* + * @file ejsStndClasses.c + * @brief EJS support methods + */ +/********************************* 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 && 0 + +/******************************************************************************/ +/* + * Date constructor + + * + * Date(); + * Date(milliseconds); + * Date(dateString); + * Date(year, month, date); + * Date(year, month, date, hour, minute, second); + */ + +int ejsDateConstructor(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + return 0; +} + +/******************************************************************************/ + +static int load(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + const char *fileName; + XmlState *parser; + Exml *xp; + MprFile *file; + + if (argc != 1 || !ejsVarIsString(argv[0])) { + ejsError(ep, EJS_ARG_ERROR, "Bad args. Usage: load(fileName);"); + return -1; + } + fileName = argv[0]->string; + + /* FUTURE -- not romable + Need rom code in MPR not MprServices + */ + file = mprOpen(ep, fileName, O_RDONLY, 0664); + if (file == 0) { + ejsError(ep, EJS_IO_ERROR, "Can't open: %s", fileName); + return -1; + } + + xp = initParser(ep, thisObj, fileName); + parser = exmlGetParseArg(xp); + + exmlSetInputStream(xp, readFileData, (void*) file); + + if (exmlParse(xp) < 0) { + if (! ejsGotException(ep)) { + ejsError(ep, EJS_IO_ERROR, "Can't parse XML file: %s\nDetails %s", + fileName, exmlGetErrorMsg(xp)); + } + termParser(xp); + mprClose(file); + return -1; + } + + ejsSetReturnValue(ep, parser->nodeStack[0].obj); + + termParser(xp); + mprClose(file); + + return 0; +} + +/******************************************************************************/ + +int ejsDefineDateClass(Ejs *ep) +{ + EjsVar *dateClass; + + dateClass = ejsDefineClass(ep, "Date", "Object", ejsDateConstructor); + if (dateClass == 0) { + return MPR_ERR_CANT_INITIALIZE; + } + + ejsDefineCMethod(ep, dateClass, "getDate", xxxProc, EJS_NO_LOCAL); + + /* Returns "Friday" or 4 ? */ + ejsDefineCMethod(ep, dateClass, "getDay", xxxProc, EJS_NO_LOCAL); + + ejsDefineCMethod(ep, dateClass, "getMonth", xxxProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, dateClass, "getFullYear", xxxProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, dateClass, "getYear", xxxProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, dateClass, "getHours", xxxProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, dateClass, "getMinutes", xxxProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, dateClass, "getSeconds", xxxProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, dateClass, "getMilliseconds", xxxProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, dateClass, "getTime", xxxProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, dateClass, "getTimeZoneOffset", xxxProc, EJS_NO_LOCAL); + + ejsDefineCMethod(ep, dateClass, "parse", xxxProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, dateClass, "setDate", xxxProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, dateClass, "setMonth", xxxProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, dateClass, "setFullYear", xxxProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, dateClass, "setYear", xxxProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, dateClass, "setMinutes", xxxProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, dateClass, "setSeconds", xxxProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, dateClass, "setMilliseconds", xxxProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, dateClass, "setTime", xxxProc, EJS_NO_LOCAL); + + ejsDefineCMethod(ep, dateClass, "toString", xxxProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, dateClass, "toGMTString", xxxProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, dateClass, "toUTCString", xxxProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, dateClass, "toLocaleString", xxxProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, dateClass, "UTC", xxxProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, dateClass, "valueOf", xxxProc, EJS_NO_LOCAL); + /* + UTC: getUTCDate, getUTCDay, getUTCMonth, getUTCFullYear, getUTCHours, + getUTCMinutes, getUTCSeconds, getUTCMilliseconds + setUTCDate, setUTCDay, setUTCMonth, setUTCFullYear, setUTCHours, + setUTCMinutes, setUTCSeconds, setUTCMilliseconds + */ + + return ejsObjHasErrors(dateClass) ? MPR_ERR_CANT_INITIALIZE : 0; +} + +/******************************************************************************/ +/* + Time is since 1970/01/01 GMT + + Normal: Fri Feb 10 2006 05:06:44 GMT-0800 (Pacific Standard Time) + UTC: Sat, 11 Feb 2006 05:06:44 GMT + + // Using without New + + println(Date()); + + var myDate = new Date(); + myDate.setFullYear(2010, 0, 14); + + var today = new Date(); + + if (myDate > today) { + } else { + } + + + X=Date() should be equivalent to X=(new Date()).toString() + + */ +/******************************************************************************/ + +#else +void ejsStndClassesDummy() {} + +/******************************************************************************/ +#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 + */ diff --git a/source4/lib/appweb/ejs-2.0/ejs/classes/ejsError.c b/source4/lib/appweb/ejs-2.0/ejs/classes/ejsError.c new file mode 100755 index 0000000000..99445afc7c --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/classes/ejsError.c @@ -0,0 +1,140 @@ +/* + * @file ejsError.c + * @brief Error 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 + +/************************************ Code ************************************/ +/* + * Parse the args and return the message. Convert non-string args using + * .toString. + */ + +static char *getMessage(Ejs *ep, int argc, EjsVar **argv) +{ + if (argc == 0) { + return ""; + + } else if (argc == 1) { + if (! ejsVarIsString(argv[0])) { + if (ejsRunMethod(ep, argv[0], "toString", 0) < 0) { + return 0; + } + return ep->result->string; + + } else { + return argv[0]->string; + } + + } else { + /* Don't call ejsError here or it will go recursive. */ + return 0; + } +} + + +/******************************************************************************/ +/* + * Error Constructor and also used for constructor for sub classes. + * + * Usage: new Error([message]) + */ + +int ejsErrorCons(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + char *msg, *stack; + + msg = getMessage(ep, argc, argv); + if (msg == 0) { + return -1; + } + + ejsSetPropertyToString(ep, thisObj, "name", ejsGetBaseClassName(thisObj)); + ejsSetPropertyToString(ep, thisObj, "message", msg); + + ejsSetPropertyToUndefined(ep, thisObj, "stack"); + + stack = ejsFormatStack(ep); + if (stack) { + ejsSetPropertyToString(ep, thisObj, "stack", stack); + mprFree(stack); + } + + if (ejsObjHasErrors(thisObj)) { + return -1; + } + + return 0; +} + +/******************************************************************************/ + +int ejsDefineErrorClasses(Ejs *ep) +{ + if (ejsDefineClass(ep, "Error", "Object", ejsErrorCons) == 0 || + ejsDefineClass(ep, "AssertError", "Error", ejsErrorCons) == 0 || + ejsDefineClass(ep, "EvalError", "Error", ejsErrorCons) == 0 || + ejsDefineClass(ep, "InternalError", "Error", ejsErrorCons) == 0 || + ejsDefineClass(ep, "IOError", "Error", ejsErrorCons) == 0 || + ejsDefineClass(ep, "MemoryError", "Error", ejsErrorCons) == 0 || + ejsDefineClass(ep, "RangeError", "Error", ejsErrorCons) == 0 || + ejsDefineClass(ep, "ReferenceError", "Error", ejsErrorCons) == 0 || + ejsDefineClass(ep, "SyntaxError", "Error", ejsErrorCons) == 0 || + ejsDefineClass(ep, "TypeError", "Error", ejsErrorCons) == 0) { + + return MPR_ERR_CANT_INITIALIZE; + } + return 0; +} + +/******************************************************************************/ + +#else +void ejsErrorDummy() {} + +/******************************************************************************/ +#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 + */ 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 + */ diff --git a/source4/lib/appweb/ejs-2.0/ejs/classes/ejsStndClasses.c b/source4/lib/appweb/ejs-2.0/ejs/classes/ejsStndClasses.c new file mode 100644 index 0000000000..fd6cda7813 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/classes/ejsStndClasses.c @@ -0,0 +1,144 @@ +/* + * @file ejsStndClasses.c + * @brief EJS support methods + */ +/********************************* 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 + +/******************************************************************************/ +/******************************* Function Class *******************************/ +/******************************************************************************/ + +int ejsDefineFunctionClass(Ejs *ep) +{ + if (ejsDefineClass(ep, "Function", "Object", ejsFunctionConstructor) == 0) { + return MPR_ERR_CANT_INITIALIZE; + } + return 0; +} + +/******************************************************************************/ +/* + * Function constructor + */ + +int ejsFunctionConstructor(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + int rc; + + if (argc != 1 || !ejsVarIsString(argv[0])) { + ejsArgError(ep, "Usage: Function(\"function (arg) { script };\");"); + } + + rc = ejsEvalScript(ep, argv[0]->string, 0); + + /* + * Note: this will convert the object into a method. It will cease to be + * an object. + */ + if (rc == 0 && ejsVarIsMethod(ep->result)) { + /* + * Must make thisObj collectable. + */ + ejsMakeObjPermanent(thisObj, 0); + ejsMakeObjLive(thisObj, 1); + mprAssert(ejsObjIsCollectable(thisObj)); + ejsWriteVar(ep, thisObj, ep->result, EJS_SHALLOW_COPY); + } + return rc; +} + +/******************************************************************************/ +/******************************* Boolean Class ********************************/ +/******************************************************************************/ + +int ejsDefineBooleanClass(Ejs *ep) +{ + if (ejsDefineClass(ep, "Boolean", "Object", ejsBooleanConstructor) == 0){ + return MPR_ERR_CANT_INITIALIZE; + } + return 0; +} + +/******************************************************************************/ +/* + * Boolean constructor + */ + +int ejsBooleanConstructor(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + return 0; +} + +/******************************************************************************/ +/******************************** Number Class ********************************/ +/******************************************************************************/ + +int ejsDefineNumberClass(Ejs *ep) +{ + if (ejsDefineClass(ep, "Number", "Object", ejsNumberConstructor) == 0) { + return MPR_ERR_CANT_INITIALIZE; + } + return 0; +} + +/******************************************************************************/ +/* + * Number constructor + */ + +int ejsNumberConstructor(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + return 0; +} + +/******************************************************************************/ + +#else +void ejsStndClassesDummy() {} + +/******************************************************************************/ +#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 + */ diff --git a/source4/lib/appweb/ejs-2.0/ejs/classes/ejsString.c b/source4/lib/appweb/ejs-2.0/ejs/classes/ejsString.c new file mode 100644 index 0000000000..2339650361 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/classes/ejsString.c @@ -0,0 +1,381 @@ +/* + * @file ejsString.c + * @brief EJScript string 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 +/******************************************************************************/ +/*********************************** Constructors *****************************/ +/******************************************************************************/ +/* + * String constructor. + */ + +int ejsStringConstructor(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + char *str; + + if (argc == 0) { + ejsSetReturnValueToString(ejs, ""); + + } else if (argc == 1) { + /* MOB -- rc */ + str = ejsVarToString(ejs, argv[0]); + ejsSetReturnValueToString(ejs, str); + + } else { + ejsArgError(ejs, "usage: String([var])"); + return -1; + } + + return 0; +} + +/******************************************************************************/ +/******************************** Visible Methods *****************************/ +/******************************************************************************/ +/* + * Return a string containing the character at a given index + * + * String string.charAt(Number) + */ + +static int charAt(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + EjsNum num; + char buf[2]; + + if (argc != 1) { + ejsArgError(ejs, "usage: charAt(integer)"); + return -1; + } + + num = ejsVarToNumber(argv[0]); + if (num < 0 || num >= thisObj->length) { + ejsError(ejs, EJS_RANGE_ERROR, "Bad index"); + return -1; + } + + mprAssert(ejsVarIsString(thisObj)); + + buf[0] = argv[0]->string[num]; + buf[1] = '\0'; + ejsSetReturnValueToString(ejs, buf); + + return 0; +} + +/******************************************************************************/ +/* + * Return an integer containing the character at a given index + * + * Number string.charCodeAt(Number) + */ + +static EjsNum charCodeAt(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + EjsNum num; + + if (argc != 1) { + ejsArgError(ejs, "usage: charCodeAt(integer)"); + return -1; + } + + num = ejsVarToNumber(argv[0]); + if (num < 0 || num >= thisObj->length) { + ejsError(ejs, EJS_RANGE_ERROR, "Bad index"); + return -1; + } + ejsSetReturnValueToNumber(ejs, (EjsNum) argv[0]->string[num]); + + return 0; +} + +/******************************************************************************/ +/* + * Catenate + * + * String string.catenate(var, ...) + */ + +static int concat(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + int i; + + if (argc == 0) { + ejsArgError(ejs, "usage: concat(String, ...)"); + return -1; + } + + mprAssert(ejsVarIsString(thisObj)); + + for (i = 0; i < argc; i++) { + if (ejsStrcat(ejs, thisObj, argv[i]) < 0) { + ejsMemoryError(ejs); + return -1; + } + } + ejsSetReturnValue(ejs, thisObj); + return 0; +} + +/******************************************************************************/ +/* + * Return the position of the first occurance of a substring + * + * Number string.indexOf(String subString [, Number start]) + */ + +static int indexOf(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + char *pat, *s1, *s2, *origin; + int start, i; + + if (argc == 0 || argc > 2) { + ejsArgError(ejs, "usage: indexOf(String [, Number])"); + return -1; + } + + pat = ejsVarToString(ejs, argv[0]); + + if (argc == 2) { + start = ejsVarToNumber(argv[1]); + if (start > thisObj->length) { + start = thisObj->length; + } + } else { + start = 0; + } + + i = start; + for (origin = &thisObj->string[i]; i < thisObj->length; i++, origin++) { + s1 = origin; + for (s2 = pat; *s1 && *s2; s1++, s2++) { + if (*s1 != *s2) { + break; + } + } + if (*s2 == '\0') { + ejsSetReturnValueToNumber(ejs, (EjsNum) (origin - thisObj->string)); + } + } + + ejsSetReturnValueToNumber(ejs, (EjsNum) -1); + return 0; +} + +/******************************************************************************/ +/* + * Return the position of the last occurance of a substring + * + * Number string.lastIndexOf(String subString [, Number start]) + */ + +static int lastIndexOf(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + char *pat, *s1, *s2, *origin; + int start; + + if (argc == 0 || argc > 2) { + ejsArgError(ejs, "usage: indexOf(String [, Number])"); + return -1; + } + + pat = ejsVarToString(ejs, argv[0]); + + if (argc == 2) { + start = ejsVarToNumber(argv[1]); + if (start > thisObj->length) { + start = thisObj->length; + } + } else { + start = 0; + } + + origin = &thisObj->string[thisObj->length - 1]; + for (; origin >= &thisObj->string[start]; origin--) { + + s1 = origin; + for (s2 = pat; *s1 && *s2; s1++, s2++) { + if (*s1 != *s2) { + break; + } + } + if (*s2 == '\0') { + ejsSetReturnValueToNumber(ejs, (EjsNum) (origin - thisObj->string)); + } + } + + ejsSetReturnValueToNumber(ejs, (EjsNum) -1); + return 0; +} + +/******************************************************************************/ +/* + * Return a substring + * + * Number string.slice(Number start, Number end) + */ + +static int slice(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + EjsNum start, end; + + if (argc != 2) { + ejsArgError(ejs, "usage: slice(Number, Number)"); + return -1; + } + + start = ejsVarToNumber(argv[0]); + end = ejsVarToNumber(argv[1]); + if (start < 0 || start >= thisObj->length) { + ejsError(ejs, EJS_RANGE_ERROR, "Bad start index"); + return-1; + } + if (end < 0 || end >= thisObj->length) { + ejsError(ejs, EJS_RANGE_ERROR, "Bad end index"); + return -1; + } + + mprAssert(ejsVarIsString(thisObj)); + + ejsSetReturnValueToBinaryString(ejs, (uchar*) &thisObj->string[start], + end - start); + + return 0; +} + +/******************************************************************************/ +/* + * Split a string + * + * Number string.split(String delimiter [, Number limit]) + */ + +static int split(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + EjsVar *array, *vp; + char *delim, *last, *cp; + int len, limit, alloc; + + if (argc == 0 || argc > 2) { + ejsArgError(ejs, "usage: split(String [, Number])"); + return -1; + } + + delim = ejsVarToStringEx(ejs, argv[0], &alloc); + + limit = ejsVarToNumber(argv[1]); + + array = ejsCreateArray(ejs, 0); + + len = strlen(delim); + + last = thisObj->string; + for (cp = last; *cp; cp++) { + if (*cp == *delim && strncmp(cp, delim, len) == 0) { + if (cp > last) { + vp = ejsCreateBinaryStringVar(ejs, (uchar*) last, (cp - last)); + ejsAddArrayElt(ejs, array, vp, EJS_SHALLOW_COPY); + ejsFreeVar(ejs, vp); + } + } + } + + ejsSetReturnValue(ejs, array); + ejsFreeVar(ejs, array); + + if (alloc) { + mprFree(delim); + } + + return 0; +} + +/******************************************************************************/ +/* + * Create the object class + */ + +int ejsDefineStringClass(Ejs *ejs) +{ + EjsVar *sc; + + sc = ejsDefineClass(ejs, "String", "Object", ejsStringConstructor); + if (sc == 0) { + return MPR_ERR_CANT_INITIALIZE; + } + + ejsDefineCMethod(ejs, sc, "charAt", charAt, EJS_NO_LOCAL); + ejsDefineCMethod(ejs, sc, "charCodeAt", charCodeAt, EJS_NO_LOCAL); + ejsDefineCMethod(ejs, sc, "concat", concat, EJS_NO_LOCAL); + ejsDefineCMethod(ejs, sc, "indexOf", indexOf, EJS_NO_LOCAL); + ejsDefineCMethod(ejs, sc, "lastIndexOf", lastIndexOf, EJS_NO_LOCAL); + ejsDefineCMethod(ejs, sc, "slice", slice, EJS_NO_LOCAL); + ejsDefineCMethod(ejs, sc, "split", split, EJS_NO_LOCAL); +#if UNUSED + ejsDefineCMethod(ejs, sc, "match", match, EJS_NO_LOCAL); + ejsDefineCMethod(ejs, sc, "replace", replace, EJS_NO_LOCAL); + ejsDefineCMethod(ejs, sc, "search", search, EJS_NO_LOCAL); + ejsDefineCMethod(ejs, sc, "substring", substring, EJS_NO_LOCAL); + // MOB bad name + ejsDefineCMethod(ejs, sc, "substr", substr, EJS_NO_LOCAL); + ejsDefineCMethod(ejs, sc, "toLowerCase", toLowerCase, EJS_NO_LOCAL); + ejsDefineCMethod(ejs, sc, "toUpperCase", toUpperCase, EJS_NO_LOCAL); + + // Static method + ejsDefineCMethod(ejs, sc, "fromCharCode", fromCharCode, 0, EJS_NO_LOCAL); +#endif + + if (ejsObjHasErrors(sc)) { + ejsFreeVar(ejs, sc); + return MPR_ERR_CANT_CREATE; + } + return 0; +} + +/******************************************************************************/ +#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 + */ diff --git a/source4/lib/appweb/ejs-2.0/ejs/classes/ejsXml.c b/source4/lib/appweb/ejs-2.0/ejs/classes/ejsXml.c new file mode 100644 index 0000000000..a2ef8d1390 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/classes/ejsXml.c @@ -0,0 +1,1327 @@ +/* + * @file ejsXml.c + * @brief E4X XML support + */ +/********************************* 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 + */ +/************************************ Doc *************************************/ +/* + * Javascript class definition + * + * class XML { + * public XML(); + * public XML(string xmlString); // "<tag... " + * public XML(string file); // "file" + * + * public void load(string file); + * public void save(string file); + * public Array children(); + * public Array attributes(); + * } + * + [[Internal Properties / Methods]] + - prototype - Ptr to class prototype (base class) + - class - Type of class + Object.prototype.toString + - Value - + - Get(name) - Returns the value + - Put(name, value) - Sets the value + - HasProperty(name) - Bool if property exists + - Delete(name) - Delete property + - DefaultValue(hint) - Return default primitive (not obj) value + toString, if result is obj, then call valueOf + if hint is number, then call valueOf, then toString + - Construct(arg list) - Constructor + - Call(arg list) - Function call + - HasInstance(value) - ?? + - Scope - Frame scope chain + - Match(string, index) - Regexp match + + - Example: + XML attribute @name + @* + * + var node = new XML("<order/>"); + Operators: + var prices = order..price; + var urgentItems = order.item(@level == "rush"); + var itemAttrs = order.item[0].@*; # @ for attributes + XML Literals + order.customer.address = + <address>..... + <zip>{zipCode}</zip> Where {var} is a JS var + <tag attribute={prefix}> ... Also for attributes + </address> + Omit namespaces + Example: + var html = <html/>; + html.head.title = "My title"; + head.body@bgcolor = "#e4e4e4"; +*/ + +/********************************** Includes **********************************/ + +#include "ejs.h" +#include "exml.h" + +/************************************ Data ************************************/ +#if BLD_FEATURE_EJS_E4X + +/* + * Per tag state + */ +typedef struct XmlTagState { + EjsVar *obj; + EjsVar *attributes; + EjsVar *comments; +} XmlTagState; + +/* + * Parser state + */ +typedef struct XmlState { + Ejs *ep; + EjsVar *xmlClass; + EjsVar *xmlListClass; + XmlTagState nodeStack[E4X_MAX_NODE_DEPTH]; + int topOfStack; + long inputSize; + long inputPos; + const char *inputBuf; + const char *fileName; +} XmlState; + +/****************************** Forward Declarations **************************/ +/* + * XML methods + */ +static int text(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv); +static int name(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv); +static int load(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv); +static int save(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv); +static int toString(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv); +static int valueOf(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv); + +/* MOB -- temp */ +static int getList(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv); +static int setText(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv); + +#if FUTURE +static int length(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv); +static int toXmlString(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv); + +static int appendChild(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv); +static int attributes(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv); +static int child(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv); +static int children(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv); +static int comments(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv); +static int decendants(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv); +static int elements(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv); +static int insertChildAfter(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv); +static int insertChildBefore(Ejs *ep, EjsVar *thisObj, int argc, + EjsVar **argv); +static int replace(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv); +static int setName(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv); +static int text(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv); +#endif + +/* + * Internal methods + */ +static EjsVar *createXmlProperty(Ejs *ep, EjsVar *obj, const char *property); +static int deleteXmlProperty(Ejs *ep, EjsVar *obj, const char *property); +static EjsVar *getXmlProperty(Ejs *ep, EjsVar *obj, const char *property); +static EjsVar *setXmlProperty(Ejs *ep, EjsVar *obj, const char *property, + const EjsVar *value); +static int loadXmlString(Ejs *ep, EjsVar *thisObj, const char *xmlString); + +/* + * XMLList methods + */ +static EjsVar *createXmlListProperty(Ejs *ep, EjsVar *obj, + const char *property); +static int deleteXmlListProperty(Ejs *ep, EjsVar *obj, + const char *property); +static EjsVar *getXmlListProperty(Ejs *ep, EjsVar *obj, const char *property); +static EjsVar *setXmlListProperty(Ejs *ep, EjsVar *obj, const char *property, + const EjsVar *value); + +/* + * Misc + */ +static int readFileData(Exml *xp, void *data, char *buf, int size); +static int readStringData(Exml *xp, void *data, char *buf, int size); +static int parserHandler(Exml *xp, int state, const char *tagName, + const char *attName, const char *value); +static void termParser(Exml *xp); +static Exml *initParser(Ejs *ep, EjsVar *thisObj, const char *fileName); +static int getNumElements(EjsVar *obj); +static int getText(MprBuf *buf, EjsVar *obj); +static int xmlToString(Ejs *ep, MprBuf *buf, EjsVar *obj, int indentLevel); +static void indent(MprBuf *bp, int level); +static char *cleanTagName(char *name); + +/******************************************************************************/ +/* + * Define the E4X classes (XML, XMLList) + */ + +int ejsDefineXmlClasses(Ejs *ep) +{ + EjsMethods *methods; + EjsVar *xmlClass, *xmlListClass; + + /* + * Create the XML class + */ + xmlClass = ejsDefineClass(ep, "XML", "Object", ejsXmlConstructor); + if (xmlClass == 0) { + return MPR_ERR_CANT_INITIALIZE; + } + + /* + * Define the XML class methods + */ + ejsDefineCMethod(ep, xmlClass, "text", text, EJS_NO_LOCAL); + ejsDefineCMethod(ep, xmlClass, "name", name, EJS_NO_LOCAL); + ejsDefineCMethod(ep, xmlClass, "load", load, EJS_NO_LOCAL); + ejsDefineCMethod(ep, xmlClass, "save", save, EJS_NO_LOCAL); + ejsDefineCMethod(ep, xmlClass, "toString", toString, EJS_NO_LOCAL); + ejsDefineCMethod(ep, xmlClass, "valueOf", valueOf, EJS_NO_LOCAL); + +/* MOB -- temporary only */ + ejsDefineCMethod(ep, xmlClass, "getList", getList, EJS_NO_LOCAL); + ejsDefineCMethod(ep, xmlClass, "setText", setText, EJS_NO_LOCAL); + + /* + * Setup the XML internal methods. + */ + methods = mprAllocTypeZeroed(ep, EjsMethods); + xmlClass->objectState->methods = methods; + + methods->createProperty = createXmlProperty; + methods->deleteProperty = deleteXmlProperty; + methods->getProperty = getXmlProperty; + methods->setProperty = setXmlProperty; + + /* + * Create the XMLList class + */ + xmlListClass = ejsDefineClass(ep, "XMLList", "Array", + ejsXmlListConstructor); + + /* + * Define the XMLList class methods + */ + + /* + * Setup the XML internal methods. + */ + methods = mprAllocTypeZeroed(ep, EjsMethods); + xmlListClass->objectState->methods = methods; + + methods->createProperty = createXmlListProperty; + methods->deleteProperty = deleteXmlListProperty; + methods->getProperty = getXmlListProperty; + methods->setProperty = setXmlListProperty; + + /* MOB -- need to complete xmlListClass */ + + return (ejsObjHasErrors(xmlClass) || ejsObjHasErrors(xmlListClass)) + ? MPR_ERR_CANT_INITIALIZE : 0; + return 0; +} + +/******************************************************************************/ +/* + * Routine to create an XML object using a default constructor + */ + +EjsVar *ejsCreateXml(Ejs *ep) +{ + EjsVar *op; + + op = ejsCreateSimpleObj(ep, "XML"); + if (op == 0) { + mprAssert(op); + return op; + } + ejsSetVarName(ep, op, "xmlNode"); + + /* + * Invoke class constructors manually (for speed and space) + */ + if (ejsXmlConstructor(ep, op, 0, 0) < 0) { + mprFree(op); + mprAssert(0); + return 0; + } + return op; +} + +/******************************************************************************/ +/* + * XML constructor + */ + +int ejsXmlConstructor(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + EjsVar *vp; + const char *str; + + ejsSetVarFlags(thisObj, EJS_XML_FLAGS_ELEMENT); + + if (argc == 1) { + vp = argv[0]; + + if (ejsVarIsObject(vp)) { + /* Convert DOM to XML. Not implemented */; + + } else if (ejsVarIsString(vp)) { + str = vp->string; + if (str == 0) { + return 0; + } + if (*str == '<') { + /* XML Literal */ + return loadXmlString(ep, thisObj, str); + + } else { + /* Load from file */ + return load(ep, thisObj, argc, argv); + } + } else { + ejsError(ep, EJS_TYPE_ERROR, "Bad type passed to XML constructor"); + return -1; + } + } + return 0; +} + +/******************************************************************************/ +/* + * Routine to create an XMLList object + */ + +EjsVar *ejsCreateXmlList(Ejs *ep) +{ + EjsVar *op; + + /* Sanity limit for size of hash table */ + + op = ejsCreateSimpleObj(ep, "XMLList"); + if (op == 0) { + mprAssert(0); + return op; + } + if (ejsArrayConstructor(ep, op, 0, 0) < 0 || + ejsXmlConstructor(ep, op, 0, 0) < 0) { + mprFree(op); + mprAssert(0); + return 0; + } + return op; +} + +/******************************************************************************/ +/* + * XMLList constructor + */ + +int ejsXmlListConstructor(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + // ejsSetVarFlags(vp, EJS_XML_FLAGS_ELEMENT); + return 0; +} + +/******************************************************************************/ +/******************************** Internal Methods ****************************/ +/******************************************************************************/ + +static EjsVar *createXmlProperty(Ejs *ep, EjsVar *obj, const char *property) +{ + return ejsGetVarPtr(ejsCreateSimpleProperty(ep, obj, property)); +} + +/******************************************************************************/ + +static int deleteXmlProperty(Ejs *ep, EjsVar *obj, const char *property) +{ + return ejsDeleteProperty(ep, obj, property); +} + +/******************************************************************************/ +/* MOB -- need ep as an arg */ +static EjsVar *getXmlProperty(Ejs *ep, EjsVar *obj, const char *property) +{ +#if NEW + EjsVar *lp; + + lp = ejsCreateXmlList(ep); + if (isdigit(*property)) { + /* MOB -- where do we store these. Do we need them ? */ + lp->targetObject = obj + lp->targetProperty = property + return getXmlListProperty(lp, property); + } + + /* What about a simple elment. Should it not return the text */ + + if (*property == '@') { + ap = ejsGetFirstProperty(obj, EJS_ENUM_ALL); + while (ap) { + vp = ejsGetVarPtr(ap); + /* MOB -- are attributes unique ? */ + if (vp->flags & EJS_XML_FLAGS_ATTRIBUTE && + strcmp(property, ap->name) == 0) { + ejsAppendXml(lp, vp); + } + ap = ejsGetNexttProperty(ap, EJS_ENUM_ALL); + } + } else { + while (ap) { + vp = ejsGetVarPtr(ap); + /* MOB -- are attributes unique ? */ + if (vp->flags & EJS_XML_FLAGS_ELEMENT && + strcmp(property, ap->name) == 0) { + ejsAppendXml(lp, vp); + } + ap = ejsGetNexttProperty(ap, EJS_ENUM_ALL); + } + } + return l; + + // Must always return XML or XMLList event for comments and attributes +#endif + return ejsGetVarPtr(ejsGetSimpleProperty(ep, obj, property)); +} + +/******************************************************************************/ + +static EjsVar *setXmlProperty(Ejs *ep, EjsVar *obj, const char *property, + const EjsVar *value) +{ + EjsProperty *pp; + EjsVar *vp; + + pp = ejsCreateSimpleProperty(ep, obj, property); + if (pp == 0) { + /* Should never happen */ + mprAssert(pp); + return 0; + } + vp = ejsGetVarPtr(pp); + if (ejsWriteVar(ep, vp, value, EJS_SHALLOW_COPY) < 0) { + return 0; + } + return ejsGetVarPtr(pp); +} + +/******************************************************************************/ +/* + NEW + +static EjsVar *setXmlProperty(Ejs *ep, EjsVar *op, const char *property, + EjsVar *value) +{ + + if ((value->objectState->baseClass != XML && + value->objectState->baseClass != XMLList) || + value->string[0] != '<') { + ejsVarToString(luevalue.toString(); + ejsRunMethod(ep, value, "toString", 0); + value = ejsDupVar(ep->result); + + } else { + value = ejsDupVar(value); + } + + if (isdigit(*property)) { + // ERROR -- reserved for future versions + return 0; + } + + if (*property == '@') { + if (op->objectState->baseClass == XMLList) { + if (op->obj.LENGTH_PROPERTY == 0) { + c = ""; + } else { + // Catenate all result of toString on all elts in list + } + } else { + c = c.toString(); + } + // Replace existing attribute of same name or insert + return; + } + for (i = op->obj.LENGTH - 1; i >= 0; i--) { + // Delete item of same name + } + if (not Found) { + Append new Xml object + - set [[name]], [[class]] == "element" + } + + mprFree(value); +} + + */ +/******************************************************************************/ +/************************************ Methods *********************************/ +/******************************************************************************/ + +static int load(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + const char *fileName; + XmlState *parser; + Exml *xp; + MprFile *file; + + if (argc != 1 || !ejsVarIsString(argv[0])) { + ejsError(ep, EJS_ARG_ERROR, "Bad args. Usage: load(fileName);"); + return -1; + } + fileName = argv[0]->string; + + /* MOB -- not romable + Need rom code in MPR not MprServices + */ + file = mprOpen(ep, fileName, O_RDONLY, 0664); + if (file == 0) { + ejsError(ep, EJS_IO_ERROR, "Can't open: %s", fileName); + return -1; + } + + /* MOB -- should we empty thisObj of all existing properties ? */ + + xp = initParser(ep, thisObj, fileName); + parser = exmlGetParseArg(xp); + + exmlSetInputStream(xp, readFileData, (void*) file); + + if (exmlParse(xp) < 0) { + if (! ejsGotException(ep)) { + ejsError(ep, EJS_IO_ERROR, "Can't parse XML file: %s\nDetails %s", + fileName, exmlGetErrorMsg(xp)); + } + termParser(xp); + mprClose(file); + return -1; + } + + ejsSetReturnValue(ep, parser->nodeStack[0].obj); + + termParser(xp); + mprClose(file); + + return 0; +} + +/******************************************************************************/ + +static int loadXmlString(Ejs *ep, EjsVar *thisObj, const char *xmlString) +{ + XmlState *parser; + Exml *xp; + + xp = initParser(ep, thisObj, "string"); + parser = exmlGetParseArg(xp); + + parser->inputBuf = xmlString; + parser->inputSize = strlen(xmlString); + + exmlSetInputStream(xp, readStringData, (void*) 0); + + if (exmlParse(xp) < 0) { + if (! ejsGotException(ep)) { + ejsError(ep, EJS_IO_ERROR, "Can't parse XML string\nError %s", + exmlGetErrorMsg(xp)); + } + termParser(xp); + return -1; + } + + ejsSetReturnValue(ep, parser->nodeStack[0].obj); + + termParser(xp); + + return 0; +} + +/******************************************************************************/ + +static int text(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + EjsVar *vp; + + vp = ejsGetVarPtr(ejsGetSimpleProperty(ep, thisObj, E4X_TEXT_PROPERTY)); + if (vp == 0) { + ejsSetReturnValueToString(ep, ""); + return 0; + } + ejsSetReturnValue(ep, vp); + return 0; +} + +/******************************************************************************/ +/* + * Return the tag name + */ + +static int name(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + EjsVar *vp; + + vp = ejsGetVarPtr(ejsGetSimpleProperty(ep, thisObj, E4X_TAG_NAME_PROPERTY)); + if (vp == 0) { + ejsSetReturnValueToString(ep, ""); + return 0; + } + ejsSetReturnValue(ep, vp); +#if UNDEFINED + char *name; + /* MOB -- not ideal as we can't guarantee thisObj is a property */ + name = ejsGetPropertyPtr(thisObj)->name; + if (name == 0) { + name = ""; + } + ejsSetReturnValueToString(ep, name); +#endif + return 0; +} + +/******************************************************************************/ +/* MOB -- temporary only */ + +static int setText(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + if (argc != 1) { + ejsArgError(ep, "usage: setText(string)"); + } + + ejsSetProperty(ep, thisObj, E4X_TEXT_PROPERTY, argv[0]); + ejsSetReturnValue(ep, argv[0]); + return 0; +} + +/******************************************************************************/ + +static Exml *initParser(Ejs *ep, EjsVar *thisObj, const char *fileName) +{ + XmlState *parser; + Exml *xp; + + xp = exmlOpen(ep, 512, E4X_BUF_MAX); + mprAssert(xp); + + /* + * Create the parser stack + */ + parser = mprAllocTypeZeroed(ep, XmlState); + parser->ep = ep; + parser->nodeStack[0].obj = thisObj; + parser->xmlClass = ejsGetClass(ep, 0, "XML"); + parser->xmlListClass = ejsGetClass(ep, 0, "XMLList"); + parser->fileName = fileName; + + exmlSetParseArg(xp, parser); + exmlSetParserHandler(xp, parserHandler); + + return xp; +} + +/******************************************************************************/ + +static void termParser(Exml *xp) +{ + mprFree(exmlGetParseArg(xp)); + exmlClose(xp); +} + +/******************************************************************************/ +/* + * XML parsing callback. Called for each elt and attribute/value pair. + * For speed, we handcraft the object model here rather than calling + * putXmlProperty. + * + * "<!-- txt -->" parseHandler(efd, , EXML_COMMENT); + * "<elt" parseHandler(efd, , EXML_NEW_ELT); + * "...att=value" parseHandler(efd, , EXML_NEW_ATT); + * "<elt ...>" parseHandler(efd, , EXML_ELT_DEFINED); + * "<elt/>" parseHandler(efd, , EXML_SOLO_ELT_DEFINED); + * "<elt> ...<" parseHandler(efd, , EXML_ELT_DATA); + * "...</elt>" parseHandler(efd, , EXML_END_ELT); + * + * Note: we recurse on every new nested elt. + */ + +static int parserHandler(Exml *xp, int state, const char *tagName, + const char *attName, const char *value) +{ + XmlState *parser; + XmlTagState *tos; + EjsVar *currentNode, *vp, *tagNode, *parent, *vpx; + EjsProperty *pp; + Ejs *ep; + char *name; + + parser = (XmlState*) xp->parseArg; + ep = parser->ep; + tos = &parser->nodeStack[parser->topOfStack]; + currentNode = tos->obj; + + mprAssert(state >= 0); + mprAssert(tagName && *tagName); + + switch (state) { + case EXML_PI: + /* + * By using a property name with a leading space, we can store + * non-user-visible processing instructions as regular properties. + */ + pp = ejsCreateSimpleNonUniqueProperty(ep, currentNode, E4X_PI_PROPERTY); + ejsMakePropertyEnumerable(pp, 1); + vp = ejsGetVarPtr(pp); + ejsWriteVarAsString(ep, vp, value); + ejsSetVarFlags(vp, EJS_XML_FLAGS_PI); + break; + + case EXML_COMMENT: + /* + * By using a property name with a leading space, we can store + * non- user-visible comments as regular properties. + */ + pp = ejsCreateSimpleNonUniqueProperty(ep, currentNode, + E4X_COMMENT_PROPERTY); + ejsMakePropertyEnumerable(pp, 1); + vp = ejsGetVarPtr(pp); + ejsWriteVarAsString(ep, vp, value); + ejsSetVarFlags(vp, EJS_XML_FLAGS_COMMENT); + break; + + case EXML_NEW_ELT: + if (parser->topOfStack > E4X_MAX_NODE_DEPTH) { + ejsError(ep, EJS_IO_ERROR, + "XML nodes nested too deeply in %s at line %d", + parser->fileName, exmlGetLineNumber(xp)); + return MPR_ERR_BAD_SYNTAX; + } + + name = mprStrdup(xp, tagName); + if (name == 0) { + return MPR_ERR_MEMORY; + } + + if (cleanTagName(name) < 0) { + ejsError(ep, EJS_TYPE_ERROR, "Bad XML tag name in %s at %d", + parser->fileName, exmlGetLineNumber(xp)); + mprFree(name); + return MPR_ERR_BAD_SYNTAX; + } + + pp = ejsCreateSimpleNonUniqueProperty(ep, currentNode, name); + ejsMakePropertyEnumerable(pp, 1); + + tagNode = ejsGetVarPtr(pp); + + /* MOB -- OPT */ + vpx = ejsCreateXml(ep); + vp = ejsWriteVar(ep, tagNode, vpx, EJS_SHALLOW_COPY); + ejsMakeObjLive(vp, 1); + ejsFreeVar(ep, vpx); + + /* MOB -- return code */ + pp = ejsSetPropertyToString(ep, vp, E4X_TAG_NAME_PROPERTY, name); + ejsMakePropertyEnumerable(pp, 0); + + ejsSetVarFlags(vp, EJS_XML_FLAGS_ELEMENT); + ejsMakePropertyEnumerable(ejsGetPropertyPtr(vp), 1); + + tos = &parser->nodeStack[++(parser->topOfStack)]; + currentNode = tos->obj = vp; + tos->attributes = 0; + tos->comments = 0; + mprFree(name); + break; + + case EXML_NEW_ATT: + if (mprAllocSprintf(MPR_LOC_ARGS(xp), &name, 0, "@%s", attName) < 0) { + return MPR_ERR_MEMORY; + } + pp = ejsCreateProperty(ep, currentNode, name); + ejsMakePropertyEnumerable(pp, 1); + + vp = ejsGetVarPtr(pp); + ejsWriteVarAsString(ep, vp, value); + ejsSetVarFlags(vp, EJS_XML_FLAGS_ATTRIBUTE); + mprFree(name); + break; + + case EXML_SOLO_ELT_DEFINED: + parser->topOfStack--; + mprAssert(parser->topOfStack >= 0); + tos = &parser->nodeStack[parser->topOfStack]; + break; + + case EXML_ELT_DEFINED: + if (parser->topOfStack > 0) { + parent = parser->nodeStack[parser->topOfStack - 1].obj; + ejsSetProperty(ep, currentNode, E4X_PARENT_PROPERTY, parent); + } + break; + + case EXML_ELT_DATA: + case EXML_CDATA: + pp = ejsCreateSimpleNonUniqueProperty(ep, currentNode, + E4X_TEXT_PROPERTY); + ejsMakePropertyEnumerable(pp, 1); + vp = ejsGetVarPtr(pp); + ejsWriteVarAsString(ep, vp, value); + ejsSetVarFlags(vp, EJS_XML_FLAGS_TEXT); + break; + + case EXML_END_ELT: + /* + * This is the closing element in a pair "<x>...</x>". + * Pop the stack frame off the elt stack + */ + parser->topOfStack--; + mprAssert(parser->topOfStack >= 0); + tos = &parser->nodeStack[parser->topOfStack]; + break; + + default: + ejsError(ep, EJS_IO_ERROR, "XML error in %s at %d\nDetails %s", + parser->fileName, exmlGetLineNumber(xp), exmlGetErrorMsg(xp)); + mprAssert(0); + return MPR_ERR_BAD_SYNTAX; + } + return 0; +} + +/******************************************************************************/ + +static char *cleanTagName(char *name) +{ + char *cp; + + for (cp = name; *cp; cp++) { + if (*cp == ':') { + *cp = '_'; + } else if (!isalnum(*cp) && *cp != '_' && *cp != '$' && *cp != '@') { + return 0; + } + } + return name; +} + +/******************************************************************************/ + +static int readFileData(Exml *xp, void *data, char *buf, int size) +{ + mprAssert(xp); + mprAssert(data); + mprAssert(buf); + mprAssert(size > 0); + + return mprRead((MprFile*) data, buf, size); +} + +/******************************************************************************/ + +static int readStringData(Exml *xp, void *data, char *buf, int size) +{ + XmlState *parser; + int rc, len; + + mprAssert(xp); + mprAssert(buf); + mprAssert(size > 0); + + parser = (XmlState*) xp->parseArg; + + if (parser->inputPos < parser->inputSize) { + len = min(size, (parser->inputSize - parser->inputPos)); + rc = mprMemcpy(buf, size, &parser->inputBuf[parser->inputPos], len); + parser->inputPos += len; + return rc; + } + return 0; +} + +/******************************************************************************/ + +static int save(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + const char *fileName; + MprBuf *buf; + MprFile *file; + int bytes, len; + + if (argc != 1 || !ejsVarIsString(argv[0])) { + ejsError(ep, EJS_ARG_ERROR, "Bad args. Usage: save(fileName);"); + return -1; + } + fileName = argv[0]->string; + + /* MOB -- not romable + Need rom code in MPR not MprServices + */ + + /* + * Convert to a string + */ + buf = mprCreateBuf(ep, E4X_BUF_SIZE, E4X_BUF_MAX); + if (xmlToString(ep, buf, thisObj, -1) < 0) { + mprFree(buf); + return -1; + } + + file = mprOpen(ep, fileName, + O_CREAT | O_TRUNC | O_WRONLY | O_TEXT, 0664); + if (file == 0) { + ejsError(ep, EJS_IO_ERROR, "Can't open: %s, %d", fileName, + mprGetOsError()); + return -1; + } + + len = mprGetBufLength(buf); + bytes = mprWrite(file, buf->start, len); + if (bytes != len) { + ejsError(ep, EJS_IO_ERROR, "Can't write to: %s", fileName); + mprClose(file); + return -1; + } + mprWrite(file, "\n", 1); + mprFree(buf); + + mprClose(file); + + return 0; +} + +/******************************************************************************/ + +static int toString(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + MprBuf *buf; + + buf = mprCreateBuf(ep, E4X_BUF_SIZE, E4X_BUF_MAX); + + if (xmlToString(ep, buf, thisObj, -1) < 0) { + mprFree(buf); + return -1; + } + ejsWriteVarAsString(ep, ep->result, (char*) buf->start); + + mprFree(buf); + + return 0; +} + +/******************************************************************************/ +/* MOB -- need to support XMLList */ + +static int xmlToString(Ejs *ep, MprBuf *buf, EjsVar *obj, int indentLevel) +{ + EjsProperty *pp; + EjsVar *vp; + char *varBuf; + int endTag, sawElements; + + if (indentLevel < 0) { + mprPutStringToBuf(buf, "<?xml version=\"1.0\"?>"); + } + + switch (obj->type) { + case EJS_TYPE_STRING: + if (obj->flags & EJS_XML_FLAGS_ATTRIBUTE) { + mprPutFmtStringToBuf(buf, " %s=\"%s\"", + &ejsGetPropertyPtr(obj)->name[1], obj->string); + /* No new line */ + + } else if (obj->flags & EJS_XML_FLAGS_COMMENT) { + mprPutCharToBuf(buf, '\n'); + indent(buf, indentLevel); + mprPutFmtStringToBuf(buf, "<!-- %s -->", obj->string); + + } else if (obj->flags & EJS_XML_FLAGS_TEXT) { + mprPutStringToBuf(buf, obj->string); + + } else { +// indent(buf, indentLevel); + mprPutStringToBuf(buf, obj->string); +// mprPutCharToBuf(buf, '\n'); + } + break; + + default: + /* Primitive types come here */ + indent(buf, indentLevel); + /* MOB -- rc */ + varBuf = ejsVarToString(ep, obj); + mprPutStringToBuf(buf, varBuf); + break; + + case EJS_TYPE_OBJECT: + if (obj->objectState->baseClass == ejsGetClass(ep, 0, "XML")) { + if (!obj->objectState->visited) { + obj->objectState->visited = 1; + + /* MOB -- opt. Flags would be quicker */ + if (strcmp(ejsGetPropertyPtr(obj)->name, + E4X_PARENT_PROPERTY) == 0) { + return 0; + } + /* + * MOB -- short term fix for tags with no body but with + * attributes + */ + if (getNumElements(obj) == 0 && 0) { + /* + * XML element is simple with no elements, so return just + * the text. + */ + if (getText(buf, obj) < 0) { + ejsError(ep, EJS_IO_ERROR, + "XML is to big to convert to a string"); + obj->objectState->visited = 0; + return -1; + } + + } else if (obj->flags & (EJS_XML_FLAGS_ELEMENT)) { + /* + * XML object is complex (has elements) so return full XML + * content. + */ + mprPutCharToBuf(buf, '\n'); + indent(buf, indentLevel); + + /* + * When called from toString, obj is not a property + */ + if (indentLevel >= 0) { + mprPutFmtStringToBuf(buf, "<%s", + ejsGetPropertyPtr(obj)->name); + endTag = 0; + + } else { + endTag = 1; + } + + sawElements = 0; + pp = ejsGetFirstProperty(obj, 0); + while (pp) { + vp = ejsGetVarPtr(pp); + + if (! (vp->flags & EJS_XML_FLAGS_ATTRIBUTE)) { + if (endTag == 0) { + endTag++; + mprPutStringToBuf(buf, ">"); + } + } + if (vp->flags & EJS_XML_FLAGS_ELEMENT) { + if (strcmp(ejsGetPropertyPtr(vp)->name, + E4X_PARENT_PROPERTY) != 0) { + sawElements++; + } + } + + if (xmlToString(ep, buf, ejsGetVarPtr(pp), + indentLevel + 1) < 0){ + return -1; + } + + pp = ejsGetNextProperty(pp, 0); + } + if (indentLevel >= 0) { + if (sawElements) { + mprPutCharToBuf(buf, '\n'); + indent(buf, indentLevel); + } + mprPutFmtStringToBuf(buf, "</%s>", + ejsGetPropertyPtr(obj)->name); + } + } + obj->objectState->visited = 0; + } + return 0; + } + + if (obj->objectState->baseClass == ejsGetClass(ep, 0, "XMLList")) { + indent(buf, indentLevel); + /* MOB -- TBD */ + return 0; + } + + /* + * All other objects. Allow other objects to override toString + */ + if (ejsRunMethod(ep, obj->objectState->baseClass, "toString", + 0) < 0) { + return -1; + } + if (ejsVarIsString(ep->result)) { + indent(buf, indentLevel); + mprPutStringToBuf(buf, obj->string); + } + break; + } + return 0; +} + +/******************************************************************************/ + +static void indent(MprBuf *bp, int level) +{ + int i; + + for (i = 0; i < level; i++) { + mprPutCharToBuf(bp, '\t'); + } +} + +/******************************************************************************/ + +static int valueOf(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 getList(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + const char *nodeName; + EjsProperty *pp; + EjsVar *list, *vp; + + if (argc != 1) { + nodeName = 0; + } else { + nodeName = argv[0]->string; + } + + list = ejsCreateArray(ep, 0); + + pp = ejsGetFirstProperty(thisObj, EJS_ENUM_ALL); + while (pp) { + vp = ejsGetVarPtr(pp); + if (vp->type == EJS_TYPE_OBJECT) { + if (strcmp(ejsGetPropertyPtr(vp)->name, E4X_PARENT_PROPERTY) != 0) { + if (vp->flags & EJS_XML_FLAGS_ELEMENT && + (nodeName == 0 || strcmp(nodeName, pp->name) == 0)) { + ejsAddArrayElt(ep, list, vp, EJS_SHALLOW_COPY); + } + } + } + pp = ejsGetNextProperty(pp, EJS_ENUM_ALL); + } + + ejsSetReturnValueAndFree(ep, list); + return 0; +} + +/******************************************************************************/ + +static int getNumElements(EjsVar *obj) +{ + EjsProperty *pp; + int count; + + count = 0; + pp = ejsGetFirstProperty(obj, EJS_ENUM_ALL); + while (pp) { + if (ejsGetVarPtr(pp)->flags & EJS_XML_FLAGS_ELEMENT) { + count++; + } + pp = ejsGetNextProperty(pp, EJS_ENUM_ALL); + } + return count; +} + +/******************************************************************************/ +/* MOB - This needs to be a public method */ + +static int getText(MprBuf *buf, EjsVar *obj) +{ + EjsProperty *pp; + EjsVar *vp; + + pp = ejsGetFirstProperty(obj, EJS_ENUM_ALL); + while (pp) { + vp = ejsGetVarPtr(pp); + if (vp->flags & EJS_XML_FLAGS_TEXT) { + /* MOB -- should test for overflow */ + mprPutStringToBuf(buf, vp->string); + } + pp = ejsGetNextProperty(pp, EJS_ENUM_ALL); + } + return 0; +} + +/******************************************************************************/ +/******************************************************************************/ +/******************************** Internal Methods ****************************/ +/******************************************************************************/ + +static EjsVar *createXmlListProperty(Ejs *ep, EjsVar *obj, const char *property) +{ + return ejsGetVarPtr(ejsCreateProperty(ep, obj, property)); +} + +/******************************************************************************/ + +static int deleteXmlListProperty(Ejs *ep, EjsVar *obj, const char *property) +{ + return ejsDeleteProperty(ep, obj, property); +} + +/******************************************************************************/ + +static EjsVar *getXmlListProperty(Ejs *ep, EjsVar *obj, const char *property) +{ + // Must always return XML or XMLList event for comments and attributes + return ejsGetVarPtr(ejsGetSimpleProperty(ep, obj, property)); +} + +/******************************************************************************/ + +static EjsVar *setXmlListProperty(Ejs *ep, EjsVar *obj, const char *property, + const EjsVar *value) +{ + EjsProperty *pp; + EjsVar *vp; + + pp = ejsGetSimpleProperty(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); +} + +/******************************************************************************/ +/* + NEW + +static EjsVar *putXmlListProperty(EjsVar *op, const char *property, + EjsVar *value) +{ + + if ((value->objectState->baseClass != XML && + value->objectState->baseClass != XMLList) || + value->string[0] != '<') { + c = value.toString(); + } else { + value = ejsDupVar(value); + ?? + } + if (isdigit(*property)) { + // ERROR + return 0; + } + if (*property == '@') { + if (op->objectState->baseClass == XMLList) { + if (op->obj.LENGTH_PROPERTY == 0) { + c = ""; + } else { + // Catenate all result of toString on all elts in list + } + } else { + c = c.toString(); + } + // Replace existing attribute of same name or insert + return; + } + for (i = op->obj.LENGTH - 1; i >= 0; i--) { + // Delete item of same name + } + if (not Found) { + Append new Xml object + - set [[name]], [[class]] == "element" + } +} + + */ + +/******************************************************************************/ +#else +void ejs4xDummy() {} + +/******************************************************************************/ +#endif /* BLD_FEATURE_EJS_E4X */ + +/* + * 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 + */ diff --git a/source4/lib/appweb/ejs-2.0/ejs/ejs.c b/source4/lib/appweb/ejs-2.0/ejs/ejs.c new file mode 100644 index 0000000000..0fcc6f0545 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/ejs.c @@ -0,0 +1,1378 @@ +/* + * @file ejs.c + * @brief Embedded JavaScript (EJS) + * @overview Main module interface logic. + * @remarks The initialization code must be run single-threaded. Includes: + * ejsOpen, ejsClose. + */ +/********************************* Copyright **********************************/ +/* + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2006. 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 + +/************************************* Code ***********************************/ +/* + * Initialize the EJS subsystem + */ + +EjsService *ejsOpenService(MprCtx ctx) +{ + EjsService *service; + Ejs *interp; + + service = mprAllocTypeZeroed(ctx, EjsService); + if (service == 0) { + mprError(ctx, MPR_LOC, "Can't allocate service memory"); + return 0; + } + + interp = ejsCreateInterp(service, 0, 0, 0, 1); + if (interp == 0) { + mprError(ctx, MPR_LOC, "Can't create master interpreter"); + mprFree(service); + return 0; + } + service->master = interp; + + /* + * Restore the default GC settings for the master interpreter. + * ejsCreateInterp will have initialized them. + */ + ejsGCInit(interp, EJS_DEFAULT_OBJ_INC, EJS_DEFAULT_PROP_INC, + EJS_DEFAULT_VAR_INC, EJS_DEFAULT_STR_INC); + + /* + * Save the default interpreter and global class for all to access + * MOB -- don't store these. Store the service + */ + mprSetKeyValue(interp, "ejsMaster", interp); + mprSetKeyValue(interp, "ejsGlobalClass", interp->global); + + /* + * Once the Object class is created, this routine will also make the + * Global class a subclass of Object. + */ + if (ejsDefineObjectClass(interp) < 0) { + mprError(ctx, MPR_LOC, "Can't define EJS object class"); + mprFree(service); + return 0; + } + + /* + * Create all the standard classes + */ + if (ejsDefineStandardClasses(interp) < 0) { + mprError(ctx, MPR_LOC, "Can't define EJS standard classes"); + mprFree(service); + return 0; + } + + if (ejsDefineSystemClasses(interp) < 0) { + mprError(ctx, MPR_LOC, "Can't define EJS system classes"); + mprFree(service); + return 0; + } + + if (ejsCreateObjectModel(interp) < 0) { + mprError(ctx, MPR_LOC, "Can't create EJS object model"); + mprFree(service); + return 0; + } + +#if UNUSED && BLD_FEATURE_ALLOC_STATS +{ + EjsVar v; + mprLog(ctx, 0, "Obj %d, Var %d, Prop %d\n", sizeof(EjsObj), sizeof(EjsVar), + sizeof(EjsProperty)); + mprLog(ctx, 0, "GCLink %d\n", sizeof(EjsGCLink)); + mprLog(ctx, 0, "objectState %d\n", (uint) &v.objectState - (uint) &v); +} +#endif + + return service; +} + +/******************************************************************************/ +/* + * Close down the EJS Service + */ + +void ejsCloseService(EjsService *sp, bool doStats) +{ + Ejs *ep; + + mprAssert(sp); + + ep = sp->master; + mprAssert(ep); + + ejsTermSystemClasses(ep); + + if (ep) { + ejsFreeVar(ep, sp->globalClass); + +#if BLD_FEATURE_ALLOC_STATS + if (doStats) { + mprLog(sp, 0, "GC Statistics for the Global Interpreter"); + } +#endif + ejsDestroyInterp(ep, doStats); + } + + mprRemoveKeyValue(sp, "ejsMaster"); + mprRemoveKeyValue(sp, "ejsGlobalClass"); + + mprFree(sp); +} + +/******************************************************************************/ + +Ejs *ejsGetMasterInterp(EjsService *sp) +{ + return sp->master; +} + +/******************************************************************************/ +#if BLD_FEATURE_MULTITHREAD + +int ejsSetServiceLocks(EjsService *sp, EjsLockFn lock, EjsUnlockFn unlock, + void *data) +{ + mprAssert(sp); + + sp->lock = lock; + sp->unlock = unlock; + sp->lockData = data; + return 0; +} + +#endif +/******************************************************************************/ +/* + * Create and initialize an EJS interpreter. Interpreters have a global object + * that has the service global class set as a base class. This way, it + * inherits all the desired global properties, methods and classes. + * + * The primary and alternate handles are provided to C methods depending on + * the flags provided when the C methods are defined. The global variable + * (optionally) defines a predefined global variable space. + */ + +Ejs *ejsCreateInterp(EjsService *sp, void *primaryHandle, void *altHandle, + EjsVar *global, bool useOwnSlab) +{ + EjsProperty *pp; + EjsVar *baseClass; + Ejs *ep; + + ep = mprAllocTypeZeroed(sp, Ejs); + if (ep == 0) { + mprAssert(0); + return ep; + } + + ep->stkPtr = &ep->stack[EJS_MAX_STACK]; + + ep->service = sp; + ep->primaryHandle = primaryHandle; + ep->altHandle = altHandle; + + if (sp->master) { + ep->objectClass = sp->master->objectClass; + } + + if (useOwnSlab) { + ep->slabs = (EjsSlab*) mprAllocZeroed(ep, sizeof(EjsSlab) * + EJS_SLAB_MAX); + ep->slabAllocContext = ep; + + } else { + ep->slabs = sp->master->slabs; + ep->slabAllocContext = sp->master; + ep->flags |= EJS_FLAGS_SHARED_SLAB; + } + + ep->frames = mprCreateItemArray(ep, EJS_INC_FRAMES, EJS_MAX_FRAMES); + if (ep->frames == 0) { + mprFree(ep); + return 0; + } + + ejsGCInit(ep, EJS_OBJ_INC, EJS_PROP_INC, EJS_VAR_INC, EJS_STR_INC); + + if (sp->globalClass == 0) { + /* + * Only do this for the Global interpreter. Create a global class + * (prototype) object. This is base class from which all global + * spaces will inherit. + */ + sp->globalClass = ejsCreateObjVar(ep); + if (sp->globalClass == 0) { + mprFree(ep); + return 0; + } + ejsSetClassName(ep, sp->globalClass, "Global"); + global = sp->globalClass; + } + + if (global) { + /* + * The default interpreter uses the Global class as its global + * space. + */ + ep->global = ejsDupVar(ep, global, EJS_SHALLOW_COPY); + if (ep->global == 0) { + mprFree(ep); + return 0; + } + if (ep->global->objectState != sp->globalClass->objectState) { + ejsSetBaseClass(ep->global, sp->globalClass); + } + + } else { + /* + * Use the global class as our global so we can find the object class + */ + baseClass = ejsGetClass(ep, sp->globalClass, "Object"); + if (baseClass) { + ep->global = ejsCreateSimpleObjUsingClass(ep, baseClass); + if (ep->global == 0) { + mprFree(ep); + return 0; + } + + /* + * Override the base class and set to the master Global class + */ + ejsSetBaseClass(ep->global, sp->globalClass); + + } else { + ep->global = ejsCreateObjVar(ep); + } + } + + /* + * The "global" variable points to the global space + */ + pp = ejsSetProperty(ep, ep->global, "global", ep->global); + if (pp == 0) { + mprFree(ep); + return 0; + } + ejsMakePropertyEnumerable(pp, 0); + + /* + * The "Global" variable points to the Global class + */ + pp = ejsSetProperty(ep, ep->global, "Global", sp->globalClass); + if (pp == 0) { + mprFree(ep); + return 0; + } + ejsMakePropertyEnumerable(pp, 0); + + ep->local = ejsDupVar(ep, ep->global, EJS_SHALLOW_COPY); + if (ep->frames == 0 || ep->global == 0 || ep->local == 0) { + mprFree(ep); + return 0; + } + ejsSetVarName(ep, ep->local, "topLevelLocal"); + + if (mprAddItem(ep->frames, ep->global) < 0 || + mprAddItem(ep->frames, ep->local) < 0) { + mprFree(ep); + return 0; + } + + ep->result = ejsCreateUndefinedVar(ep); + if (ep->result == 0) { + mprFree(ep); + return 0; + } + + return ep; +} + +/******************************************************************************/ +/* + * Close an EJS interpreter + */ + +void ejsDestroyInterp(Ejs *ep, bool doStats) +{ + ejsCleanInterp(ep, doStats); + + mprFree(ep); +} + +/******************************************************************************/ +/* + * Clean an EJS interpreter of all allocated variables, but DONT destroy. + * We use this rather than DestroyInterp so we delay freeing the Ejs struct + * until after the service is closed. + */ + +void ejsCleanInterp(Ejs *ep, bool doStats) +{ + int i; + + if (ep->global) { + ejsDeleteProperty(ep, ep->local, "global"); + ejsDeleteProperty(ep, ep->global, "global"); + ep->global = 0; + } + if (ep->local) { + ejsFreeVar(ep, ep->local); + ep->local = 0; + } + if (ep->global) { + ejsFreeVar(ep, ep->global); + ep->global = 0; + } + if (ep->result) { + ejsFreeVar(ep, ep->result); + ep->result = 0; + } + if (ep->castAlloc && ep->castTemp) { + mprFree(ep->castTemp); + ep->castTemp = 0; + } + if (ep->frames) { + for (i = ep->frames->length - 1; i >= 0; i--) { + mprRemoveItemByIndex(ep->frames, i); + } + mprFree(ep->frames); + ep->frames = 0; + } + + if (doStats) { + +#if BLD_FEATURE_ALLOC_STATS + mprLog(ep, 0, " "); + mprLog(ep, 0, "GC Statistics for Interpreter (0x%X)", (uint) ep); +#endif + + /* + * Cleanup before printing the alloc report + */ + ejsSetGCDebugLevel(ep, 3); + ejsCollectGarbage(ep, -1); + +#if BLD_DEBUG + /* + * If we are the master, dump objects + */ + if (ep->service->master == ep) { + ejsDumpObjects(ep); + } +#endif + +#if BLD_FEATURE_ALLOC_STATS + /* + * Print an alloc report. 1 == do leak report + */ + ejsPrintAllocReport(ep, 1); +#endif + + } else { + /* + * Must collect garbage here incase sharing interpreters with the + * master. If we don't, the mprFree later in DestroyInterp will free + * all memory and when the master does GC --> crash. + */ + ejsCollectGarbage(ep, -1); + } +} + +/******************************************************************************/ +/* + * Evaluate an EJS script file. This will evaluate the script at the current + * context. Ie. if inside a function, declarations will be local. + */ + +int ejsEvalFile(Ejs *ep, const char *path, EjsVar *result) +{ + MprFile *file; + MprFileInfo info; + char *script; + char *saveFileName; + int rc; + + mprAssert(path && *path); + + if ((file = mprOpen(ep, path, O_RDONLY | O_BINARY, 0666)) == 0) { + ejsError(ep, EJS_IO_ERROR, "Can't open %s", path); + return -1; + } + + if (mprGetFileInfo(ep, path, &info) < 0) { + ejsError(ep, EJS_IO_ERROR, "Can't get file info for %s", path); + goto error; + } + + if ((script = (char*) mprAlloc(ep, info.size + 1)) == NULL) { + ejsError(ep, "MemoryError", "Cant malloc %d", (int) info.size); + goto error; + } + + if (mprRead(file, script, info.size) != (int) info.size) { + mprFree(script); + ejsError(ep, EJS_IO_ERROR, "Error reading %s", path); + goto error; + } + mprClose(file); + script[info.size] = '\0'; + + saveFileName = ep->fileName; + ep->fileName = mprStrdup(ep, path); + + rc = ejsEvalScript(ep, script, result); + mprFree(script); + + mprFree(ep->fileName); + ep->fileName = saveFileName; + + return rc; + +/* + * Error return + */ +error: + mprClose(file); + return -1; +} + +/******************************************************************************/ +/* + * Create a new variable scope block. This pushes the old local frame down + * the stack and creates a new local variables frame. + */ + +int ejsOpenBlock(Ejs *ep) +{ + EjsProperty *pp; + int fid; + + ep->local = ejsCreateSimpleObj(ep, "Object"); + ejsSetVarName(ep, ep->local, "local"); + + if (ep->local == 0) { + ejsMemoryError(ep); + return -1; + } + + if (ep->frames->length > EJS_MAX_FRAMES && !ep->gotException) { + ejsError(ep, EJS_RANGE_ERROR, "Recursion too deep: Max depth %d", + EJS_MAX_FRAMES); + return -1; + } + + /* + * Must add to frames before ejsSetProperty which will make the object live + */ + fid = mprAddItem(ep->frames, ep->local); + if (fid < 0) { + ejsMemoryError(ep); + return -1; + } + + /* Self reference */ + pp = ejsSetProperty(ep, ep->local, "local", ep->local); + ejsMakePropertyEnumerable(pp, 0); + + return fid; +} + +/******************************************************************************/ +/* + * Set a new variable scope block. This pushes the old local frame down + * the stack and creates a new local variables frame. + */ + +int ejsSetBlock(Ejs *ep, EjsVar *local) +{ + ep->local = ejsDupVar(ep, local, EJS_SHALLOW_COPY); + ejsMakeObjPermanent(ep->local, 1); + return mprAddItem(ep->frames, ep->local); +} + +/******************************************************************************/ +/* + * Close a variable scope block opened via ejsOpenBlock. Pop back the old + * local variables frame. + */ + +int ejsCloseBlock(Ejs *ep, int fid) +{ + mprAssert(ep->local >= 0); + mprAssert(fid >= 0); + + mprAssert(ep->local == (EjsVar*) mprGetItem(ep->frames, fid)); + + if (ep->local) { + /* Allow GC */ + ejsMakeObjPermanent(ep->local, 0); + ejsFreeVar(ep, ep->local); + } + + mprRemoveItemByIndex(ep->frames, fid); + + ep->local = (EjsVar*) mprGetItem(ep->frames, + mprGetItemCount(ep->frames) - 1); + + return 0; +} + +/******************************************************************************/ +/* + * Create a new variable scope block and evaluate a script. All frames + * created during this context will be automatically deleted when complete. + * vp is optional. i.e. created local variables will be discarded + * when this routine returns. + */ + +int ejsEvalBlock(Ejs *ep, char *script, EjsVar *vp) +{ + int rc, fid; + + mprAssert(script); + + fid = ejsOpenBlock(ep); + if (fid < 0) { + return fid; + } + + rc = ejsEvalScript(ep, script, vp); + + ejsCloseBlock(ep, fid); + + return rc; +} + +/******************************************************************************/ +/* + * Parse and evaluate a EJS. The script is evaluated at the current context. + * Return the result in *vp. The result is "owned" by EJ and the caller + * must not free it. Returns -1 on errors and zero for success. + */ + +int ejsEvalScript(Ejs *ep, const char *script, EjsVar *vp) +{ + int state; + + ejsClearVar(ep, ep->result); + ep->gotException = 0; + + if (script == 0) { + return 0; + } + + /* + * Allocate a new evaluation block, and save the old one + */ + if (ejsLexOpenScript(ep, script) < 0) { + return MPR_ERR_MEMORY; + } + + /* + * Do the actual parsing and evaluation + */ + ep->scriptStatus = 0; + + do { + state = ejsParse(ep, EJS_STATE_BEGIN, EJS_FLAGS_EXE); + + if (state == EJS_STATE_RET) { + state = EJS_STATE_EOF; + } + } while (state != EJS_STATE_EOF && state != EJS_STATE_ERR); + + ejsLexCloseScript(ep); + + if (state == EJS_STATE_ERR) { + return -1; + } + + if (vp) { + /* Caller must not free. */ + *vp = *ep->result; + } + + return ep->scriptStatus; +} + +/******************************************************************************/ + +void ejsSetFileName(Ejs *ep, const char *fileName) +{ + mprFree(ep->fileName); + ep->fileName = mprStrdup(ep, fileName); +} + +/******************************************************************************/ +/* + * Format the stack backtrace + */ + +char *ejsFormatStack(Ejs* ep) +{ + EjsInput *ip; + char *errbuf; + int frame, len; + + mprAssert(ep); + + ip = ep->input; + + errbuf = 0; + + len = 0; + frame = 0; + while (ip && frame < EJS_MAX_BACKTRACE) { + char *traceLine, *newErrbuf, *line; + for (line = ip->line; *line && isspace(*line); line++) { + ; + } + mprAllocSprintf(MPR_LOC_ARGS(ep), &traceLine, MPR_MAX_STRING, + " [%02d] %s, %s, line %d -> %s\n", + frame++, + ip->fileName ? ip->fileName : "script", + ip->procName ? ip->procName: "global", + ip->lineNumber, line); + if (traceLine == 0) { + break; + } + newErrbuf = mprRealloc(ep, errbuf, len + strlen(traceLine) + 1); + if (newErrbuf == NULL) { + break; + } + errbuf = newErrbuf; + memcpy(&errbuf[len], traceLine, strlen(traceLine) + 1); + len += strlen(traceLine); + mprFree(traceLine); + ip = ip->next; + } + return errbuf; +} + +/******************************************************************************/ +/* + * Internal use method to set the error message + * + * Error, ArgError, AssertError, IOError, MemoryError, RangeError, + * ReferenceError, SyntaxError, TypeError, MemoryError + */ + +void ejsError(Ejs* ep, const char *errorType, const char* fmt, ...) +{ + va_list fmtArgs; + EjsVar *error; + char *msg, *stack; + + va_start(fmtArgs, fmt); + mprAllocVsprintf(MPR_LOC_ARGS(ep), &msg, MPR_MAX_STRING, fmt, fmtArgs); + va_end(fmtArgs); + + /* + * Create a new Error exception object. If bad error type, default to + * "Error" + */ + if (ejsGetClass(ep, 0, errorType) == 0) { + errorType = "Error"; + } + ep->gotException = 1; + + error = ejsCreateObj(ep, 0, errorType, msg); + if (error == 0) { + return; + } + mprFree(msg); + + stack = ejsFormatStack(ep); + ejsSetPropertyToString(ep, error, "stack", stack); + mprFree(stack); + + ejsWriteVar(ep, ep->result, error, EJS_SHALLOW_COPY); + ejsFreeVar(ep, error); +} + +/******************************************************************************/ + +void ejsSyntaxError(Ejs *ep, const char *msg) +{ + if (msg == 0) { + msg = " "; + } + ejsError(ep, EJS_SYNTAX_ERROR, msg); +} + +/******************************************************************************/ + +void ejsMemoryError(Ejs *ep) +{ + ejsError(ep, EJS_MEMORY_ERROR, "Memory allocation error"); +} + +/******************************************************************************/ + +void ejsArgError(Ejs *ep, const char *msg) +{ + mprAssert(msg && *msg); + + ejsError(ep, EJS_ARG_ERROR, msg); +} + +/******************************************************************************/ + +void ejsInternalError(Ejs *ep, const char *msg) +{ + mprAssert(msg && *msg); + + ejsError(ep, EJS_INTERNAL_ERROR, msg); +} + +/******************************************************************************/ +/* + * Public routine to set the error message. Caller MUST NOT free. + */ + +char *ejsGetErrorMsg(Ejs *ep) +{ + EjsVar *error; + const char *message, *stack, *name; + char *buf; + + error = ep->result; + + if (! ejsVarIsObject(error)) { + name = message = stack = 0; + } else { + name = ejsGetPropertyAsString(ep, error, "name"); + message = ejsGetPropertyAsString(ep, error, "message"); + stack = ejsGetPropertyAsString(ep, error, "stack"); + } + if (name == 0 || message == 0) { + buf = mprStrdup(ep, "Unspecified execution error\n"); + } else { + mprAllocSprintf(MPR_LOC_ARGS(ep), &buf, 0, + "%s Exception: %s\nStack:\n%s\n", + name, message, stack ? stack : " " ); + } + mprFree(ep->errorMsg); + ep->errorMsg = buf; + return buf; +} + +/******************************************************************************/ +/* + * Get the current line number + */ + +int ejsGetLineNumber(Ejs *ep) +{ + if (ep->input == 0) { + return -1; + } + return ep->input->lineNumber; +} + +/******************************************************************************/ +/* + * Return the local object + */ + +EjsVar *ejsGetLocalObj(Ejs *ep) +{ + return ep->local; +} + +/******************************************************************************/ +/* + * Return the global object + */ + +EjsVar *ejsGetGlobalObj(Ejs *ep) +{ + return ep->global; +} + +/******************************************************************************/ +/* + * Set the expression return value + */ + +void ejsSetReturnValue(Ejs *ep, EjsVar *vp) +{ + mprAssert(ep); + mprAssert(vp); + + if (vp == 0) { + return; + } + ejsWriteVar(ep, ep->result, vp, EJS_SHALLOW_COPY); +} + +/******************************************************************************/ +/* + * Set the expression return value and free the arg. + */ + +void ejsSetReturnValueAndFree(Ejs *ep, EjsVar *vp) +{ + mprAssert(ep); + mprAssert(vp); + + ejsWriteVar(ep, ep->result, vp, EJS_SHALLOW_COPY); + ejsFreeVar(ep, vp); +} + +/******************************************************************************/ +/* + * Set the expression return value to a string value. + */ + +void ejsSetReturnValueToString(Ejs *ep, const char *value) +{ + mprAssert(ep); + mprAssert(value); + + ejsWriteVarAsString(ep, ep->result, value); +} + +/******************************************************************************/ +/* + * Set the expression return value to a binary string value. + */ + +void ejsSetReturnValueToBinaryString(Ejs *ep, const uchar *value, int len) +{ + mprAssert(ep); + mprAssert(value); + + ejsWriteVarAsBinaryString(ep, ep->result, value, len); +} + +/******************************************************************************/ +/* + * Set the expression return value to a integer value. + */ + +void ejsSetReturnValueToInteger(Ejs *ep, int value) +{ + mprAssert(ep); + + ejsWriteVarAsInteger(ep, ep->result, value); +} + +/******************************************************************************/ +/* + * Set the expression return value to an EjsNum value. + */ + +void ejsSetReturnValueToNumber(Ejs *ep, EjsNum value) +{ + mprAssert(ep); + + ejsWriteVarAsNumber(ep, ep->result, value); +} + +/******************************************************************************/ +/* + * Set the expression return value to a boolean value. + */ + +void ejsSetReturnValueToBoolean(Ejs *ep, int value) +{ + mprAssert(ep); + + ejsWriteVarAsBoolean(ep, ep->result, value); +} + +/******************************************************************************/ +/* + * Set the expression return value to a boolean value. + */ + +void ejsSetReturnValueToUndefined(Ejs *ep) +{ + mprAssert(ep); + + ejsWriteVarAsUndefined(ep, ep->result); +} + +/******************************************************************************/ +/* + * Get the expression return value + */ + +EjsVar *ejsGetReturnValue(Ejs *ep) +{ + mprAssert(ep); + + return ep->result; +} + +/******************************************************************************/ + +void *ejsGetUserData(Ejs *ep) +{ + mprAssert(ep); + + return ep->userData; +} + +/******************************************************************************/ +/* + * Get a variable given a full variable spec possibly containing "." or "[]". + */ + +EjsVar *ejsGetVar(Ejs *ep, const char *fullName) +{ + mprAssert(ep); + mprAssert(fullName && *fullName); + + return ejsFindProperty(ep, 0, 0, ep->global, ep->local, fullName, 0); +} + +/******************************************************************************/ +/* + * Get a string var given a full variable spec possibly containing "." or "[]". + */ + +const char *ejsGetStr(Ejs *ep, const char *fullName, const char *defaultValue) +{ + EjsVar *vp; + + mprAssert(fullName && *fullName); + + vp = ejsFindProperty(ep, 0, 0, ep->global, ep->local, fullName, 0); + if (vp == 0 || !ejsVarIsString(vp)) { + return defaultValue; + } + /* MOB -- what about VarToStr */ + return vp->string; +} + +/******************************************************************************/ +/* + * Get an int var given a full variable spec possibly containing "." or "[]". + */ + +int ejsGetInt(Ejs *ep, const char *fullName, int defaultValue) +{ + EjsVar *vp; + + mprAssert(ep); + mprAssert(fullName && *fullName); + + vp = ejsFindProperty(ep, 0, 0, ep->global, ep->local, fullName, 0); + if (vp == 0 || !ejsVarIsInteger(vp)) { + return defaultValue; + } + /* MOB -- should use VarToInt */ + return vp->integer; +} + +/******************************************************************************/ +/* + * Get an bool var given a full variable spec possibly containing "." or "[]". + */ + +int ejsGetBool(Ejs *ep, const char *fullName, int defaultValue) +{ + EjsVar *vp; + + mprAssert(ep); + mprAssert(fullName && *fullName); + + vp = ejsFindProperty(ep, 0, 0, ep->global, ep->local, fullName, 0); + if (vp == 0 || !ejsVarIsBoolean(vp)) { + return defaultValue; + } + /* MOB -- should use VarToBool */ + return vp->boolean; +} + +/******************************************************************************/ +/* + * Set a variable that may be an arbitrarily complex object or array reference. + * Will always define in the top most variable frame. + */ + +int ejsSetVar(Ejs *ep, const char *fullName, const EjsVar *value) +{ + EjsVar *vp; + + mprAssert(fullName && *fullName); + + vp = ejsFindProperty(ep, 0, 0, ep->global, ep->local, fullName, 1); + if (vp == 0) { + return MPR_ERR_CANT_CREATE; + } + + if (ejsWriteVar(ep, vp, value, EJS_SHALLOW_COPY) == 0) { + return MPR_ERR_CANT_WRITE; + } + + return 0; +} + +/******************************************************************************/ +/* + * Set a variable that may be an arbitrarily complex object or array reference. + * Will always define in the top most variable frame. + */ + +int ejsSetStr(Ejs *ep, const char *fullName, const char *value) +{ + EjsVar *vp; + + mprAssert(fullName && *fullName); + + vp = ejsFindProperty(ep, 0, 0, ep->global, ep->local, fullName, 1); + if (vp == 0) { + return MPR_ERR_CANT_CREATE; + } + + if (ejsWriteVarAsString(ep, vp, value) == 0) { + return MPR_ERR_CANT_WRITE; + } + + return 0; +} + +/******************************************************************************/ +/* + * Set a variable that may be an arbitrarily complex object or array reference. + * Will always define in the top most variable frame. + */ + +int ejsSetInt(Ejs *ep, const char *fullName, int value) +{ + EjsVar *vp; + + mprAssert(fullName && *fullName); + + vp = ejsFindProperty(ep, 0, 0, ep->global, ep->local, fullName, 1); + if (vp == 0) { + return MPR_ERR_CANT_CREATE; + } + + /* Can't fail */ + ejsWriteVarAsInteger(ep, vp, value); + + return 0; +} + +/******************************************************************************/ +/* + * Set a variable that may be an arbitrarily complex object or array reference. + * Will always define in the top most variable frame. + */ + +int ejsSetBool(Ejs *ep, const char *fullName, bool value) +{ + EjsVar *vp; + + mprAssert(fullName && *fullName); + + vp = ejsFindProperty(ep, 0, 0, ep->global, ep->local, fullName, 1); + if (vp == 0) { + return MPR_ERR_CANT_CREATE; + } + + /* Can't fail */ + ejsWriteVarAsBoolean(ep, vp, value); + + return 0; +} + +/******************************************************************************/ +/* + * Set a variable that may be an arbitrarily complex object or array reference. + * Will always define in the top most variable frame. Free the value passed in. + */ + +int ejsSetVarAndFree(Ejs *ep, const char *fullName, EjsVar *value) +{ + EjsVar *vp; + + mprAssert(fullName && *fullName); + + vp = ejsFindProperty(ep, 0, 0, ep->global, ep->local, fullName, 1); + if (vp == 0) { + return MPR_ERR_CANT_CREATE; + } + + if (ejsWriteVar(ep, vp, value, EJS_SHALLOW_COPY) == 0) { + ejsFreeVar(ep, value); + return MPR_ERR_CANT_WRITE; + } + + ejsFreeVar(ep, value); + return 0; +} + +/******************************************************************************/ +/* + * Delete a variable + */ + +int ejsDeleteVar(Ejs *ep, const char *fullName) +{ + EjsVar *vp; + EjsVar *obj; + char *propertyName; + + vp = ejsFindProperty(ep, &obj, &propertyName, ep->global, ep->local, + fullName, 0); + if (vp == 0) { + return -1; + } + + mprAssert(propertyName); + mprAssert(propertyName); + + return ejsDeleteProperty(ep, obj, propertyName); +} + +/******************************************************************************/ +/* + * Utility routine to crack JavaScript arguments. Return the number of args + * seen. This routine only supports %s and %d type args. + * + * Typical usage: + * + * if (ejsParseArgs(argc, argv, "%s %d", &name, &age) < 2) { + * // Insufficient args + * return -1; + * } + */ + +int ejsParseArgs(int argc, char **argv, const char *fmt, ...) +{ + va_list vargs; + const char *cp; + char **sp, *s; + int *bp, *ip, argn; + + va_start(vargs, fmt); + + if (argv == 0) { + return 0; + } + + for (argn = 0, cp = fmt; cp && *cp && argn < argc && argv[argn]; ) { + if (*cp++ != '%') { + continue; + } + + s = argv[argn]; + switch (*cp) { + case 'b': + bp = va_arg(vargs, int*); + if (bp) { + if (strcmp(s, "true") == 0 || s[0] == '1') { + *bp = 1; + } else { + *bp = 0; + } + } else { + *bp = 0; + } + break; + + case 'd': + ip = va_arg(vargs, int*); + *ip = atoi(s); + break; + + case 's': + sp = va_arg(vargs, char**); + *sp = s; + break; + + default: + mprAssert(0); + } + argn++; + } + + va_end(vargs); + return argn; +} + +/******************************************************************************/ +/* + * Define the standard classes + */ + +int ejsDefineStandardClasses(Ejs *master) +{ + if (ejsDefineArrayClass(master) != 0 || + ejsDefineBooleanClass(master) != 0 || + ejsDefineErrorClasses(master) != 0 || + ejsDefineFunctionClass(master) != 0 || + ejsDefineNumberClass(master) != 0 || +#if FUTURE + ejsDefineDateClass(master) != 0 || +#endif +#if BLD_FEATURE_EJS_E4X + ejsDefineXmlClasses(master) != 0 || +#endif +#if BLD_FEATURE_EJS_DB && NOT_HERE + ejsDefineDbClasses(master) != 0 || +#endif + ejsDefineStringClass(master) != 0) { + return MPR_ERR_MEMORY; + } + return 0; +} + +/******************************************************************************/ +/* + * Define the EJS System Object Model + */ + +int ejsDefineSystemClasses(Ejs *master) +{ + if (ejsDefineSystemClass(master) != 0 || + ejsDefineAppClass(master) != 0 || + ejsDefineMemoryClass(master) != 0 || + ejsDefineLogClass(master) != 0 || + ejsDefineDebugClass(master) != 0 || + ejsDefineGCClass(master) != 0 || + ejsDefineFileSystemClass(master) != 0 || +#if BREW + ejsDefineFileClass(master) != 0 || + ejsDefineHTTPClass(master) != 0 || +#endif + ejsDefineGlobalProperties(master) != 0) { + return MPR_ERR_MEMORY; + } + return 0; +} + +/******************************************************************************/ +/* + * Terminate the system object model and classes + */ + +int ejsTermSystemClasses(Ejs *master) +{ +#if BREW + ejsTermHTTPClass(master); +#endif + return 0; +} + +/******************************************************************************/ +/* + * Define the EJS object model + */ + +int ejsCreateObjectModel(Ejs *ejs) +{ + EjsProperty *pp; + + pp = ejsSetPropertyToNewObj(ejs, ejs->global, "system", "System", 0); + if (pp == 0) { + return MPR_ERR_MEMORY; + } + + if (ejsSetPropertyToNewObj(ejs, ejsGetVarPtr(pp), "app", "System.App", + 0) == 0) { + return MPR_ERR_MEMORY; + } + return 0; +} + +/******************************************************************************/ + +void ejsTrace(Ejs *ep, const char *fmt, ...) +{ + va_list args; + char buf[MPR_MAX_LOG_STRING]; + int len; + + va_start(args, fmt); + len = mprVsprintf(buf, sizeof(buf) - 1, fmt, args); + va_end(args); + + mprLog(ep, 0, buf, len); + + va_end(args); +} + +/******************************************************************************/ + +bool ejsGotException(Ejs *ep) +{ + return (bool) ep->gotException; +} + +/******************************************************************************/ + +void ejsSetPrimaryHandle(Ejs *ep, void *primaryHandle) +{ + mprAssert(ep); + + ep->primaryHandle = primaryHandle; +} + +/******************************************************************************/ + +void ejsSetAlternateHandle(Ejs *ep, void *alternateHandle) +{ + mprAssert(ep); + + ep->altHandle = alternateHandle; +} + +/******************************************************************************/ + +#else +void ejsDummy() {} + +#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 + */ diff --git a/source4/lib/appweb/ejs-2.0/ejs/ejs.h b/source4/lib/appweb/ejs-2.0/ejs/ejs.h new file mode 100644 index 0000000000..a926446524 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/ejs.h @@ -0,0 +1,849 @@ +/* + * ejs.h - EJScript Language (ECMAScript) header. + */ + +/********************************* Copyright **********************************/ +/* + * @copy default.g + * + * Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved. + * Copyright (c) Michael O'Brien, 1994-1995. All Rights Reserved. + * Portions Copyright (c) GoAhead Software, 1995-2000. 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 **********************************/ + +#ifndef _h_EJS +#define _h_EJS 1 + +#include "mpr.h" +#include "ejsVar.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/********************************* Prototypes *********************************/ +/* + * Constants + */ +#if BLD_FEATURE_SQUEEZE + #define EJS_GC_WORK_QUOTA 160 /* Allocations required before + garbage colllection */ + + #define EJS_PARSE_INCR 256 /* Growth factor */ + #define EJS_MAX_RECURSE 25 /* Sanity for maximum recursion */ + #define EJS_SMALL_OBJ_HASH_SIZE 11 /* Small object hash size */ + #define EJS_LIST_INCR 8 /* Growth increment for lists */ + #define EJS_MAX_BACKTRACE 10 /* Recursion limit for assert */ + +#else + #define EJS_GC_WORK_QUOTA 500 + + #define EJS_PARSE_INCR 1024 + #define EJS_MAX_RECURSE 100 + #define EJS_SMALL_OBJ_HASH_SIZE 11 + #define EJS_LIST_INCR 16 + #define EJS_MAX_BACKTRACE 25 + +#endif + +/* + * Allocation increments for the default interpreter + */ +#define EJS_DEFAULT_VAR_INC 8 /* Var allocation increment */ +#define EJS_DEFAULT_PROP_INC 96 /* Property allocation increment */ +#define EJS_DEFAULT_OBJ_INC 24 /* Object allocation increment */ +#define EJS_DEFAULT_STR_INC 64 /* Object allocation increment */ + +#define EJS_MIN_TIME_FOR_GC 300 /**< Need 1/3 sec for GC */ +#define EJS_GC_MIN_WORK_QUOTA 50 /**< Min to stop thrashing */ + +/* + * Allocation increments for all non-default interpreters + */ +#define EJS_VAR_INC 32 +#define EJS_PROP_INC 64 +#define EJS_OBJ_INC 64 +#define EJS_STR_INC 64 + +#define EJS_INC_FRAMES 8 /* Frame stack increment */ +#define EJS_MAX_FRAMES 64 /* Max frame stack */ + +/* + * Lexical analyser tokens + */ +#define EJS_TOK_ERR -1 /* Any error */ +#define EJS_TOK_LPAREN 1 /* ( */ +#define EJS_TOK_RPAREN 2 /* ) */ +#define EJS_TOK_IF 3 /* if */ +#define EJS_TOK_ELSE 4 /* else */ +#define EJS_TOK_LBRACE 5 /* { */ +#define EJS_TOK_RBRACE 6 /* } */ +#define EJS_TOK_LOGICAL 7 /* ||, &&, ! */ +#define EJS_TOK_EXPR 8 /* +, -, /, % */ +#define EJS_TOK_SEMI 9 /* ; */ +#define EJS_TOK_LITERAL 10 /* literal string */ +#define EJS_TOK_METHOD_NAME 11 /* methodName( */ +#define EJS_TOK_NEWLINE 12 /* newline white space */ +#define EJS_TOK_ID 13 /* Identifier */ +#define EJS_TOK_EOF 14 /* End of script */ +#define EJS_TOK_COMMA 15 /* Comma */ +#define EJS_TOK_VAR 16 /* var */ +#define EJS_TOK_ASSIGNMENT 17 /* = */ +#define EJS_TOK_FOR 18 /* for */ +#define EJS_TOK_INC_DEC 19 /* ++, -- */ +#define EJS_TOK_RETURN 20 /* return */ +#define EJS_TOK_PERIOD 21 /* . */ +#define EJS_TOK_LBRACKET 22 /* [ */ +#define EJS_TOK_RBRACKET 23 /* ] */ +#define EJS_TOK_NEW 24 /* new */ +#define EJS_TOK_DELETE 25 /* delete */ +#define EJS_TOK_IN 26 /* in */ +#define EJS_TOK_FUNCTION 27 /* function */ +#define EJS_TOK_NUMBER 28 /* Number */ +#define EJS_TOK_CLASS 29 /* class */ +#define EJS_TOK_EXTENDS 30 /* extends */ +#define EJS_TOK_PUBLIC 31 /* public */ +#define EJS_TOK_PRIVATE 32 /* private */ +#define EJS_TOK_PROTECTED 33 /* private */ +#define EJS_TOK_TRY 34 /* try */ +#define EJS_TOK_CATCH 35 /* catch */ +#define EJS_TOK_FINALLY 36 /* finally */ +#define EJS_TOK_THROW 37 /* throw */ +#define EJS_TOK_COLON 38 /* : */ +#define EJS_TOK_GET 39 /* get */ +#define EJS_TOK_SET 40 /* set */ +#define EJS_TOK_MODULE 41 /* module */ +#define EJS_TOK_EACH 42 /* each */ + +/* + * Expression operators + */ +#define EJS_EXPR_LESS 1 /* < */ +#define EJS_EXPR_LESSEQ 2 /* <= */ +#define EJS_EXPR_GREATER 3 /* > */ +#define EJS_EXPR_GREATEREQ 4 /* >= */ +#define EJS_EXPR_EQ 5 /* == */ +#define EJS_EXPR_NOTEQ 6 /* != */ +#define EJS_EXPR_PLUS 7 /* + */ +#define EJS_EXPR_MINUS 8 /* - */ +#define EJS_EXPR_DIV 9 /* / */ +#define EJS_EXPR_MOD 10 /* % */ +#define EJS_EXPR_LSHIFT 11 /* << */ +#define EJS_EXPR_RSHIFT 12 /* >> */ +#define EJS_EXPR_MUL 13 /* * */ +#define EJS_EXPR_ASSIGNMENT 14 /* = */ +#define EJS_EXPR_INC 15 /* ++ */ +#define EJS_EXPR_DEC 16 /* -- */ +#define EJS_EXPR_BOOL_COMP 17 /* ! */ + +/* + * Conditional operators + */ +#define EJS_COND_AND 1 /* && */ +#define EJS_COND_OR 2 /* || */ +#define EJS_COND_NOT 3 /* ! */ + +/** + * EJ Parsing States. Error and Return are be negative. + */ +#define EJS_STATE_ERR -1 /**< Error state */ +#define EJS_STATE_RET -2 /**< Return statement */ +#define EJS_STATE_EOF -3 /**< End of file */ +#define EJS_STATE_COND 2 /* Parsing a conditional stmt */ +#define EJS_STATE_COND_DONE 3 +#define EJS_STATE_RELEXP 4 /* Parsing a relational expr */ +#define EJS_STATE_RELEXP_DONE 5 +#define EJS_STATE_EXPR 6 /* Parsing an expression */ +#define EJS_STATE_EXPR_DONE 7 +#define EJS_STATE_STMT 8 /* Parsing General statement */ +#define EJS_STATE_STMT_DONE 9 +#define EJS_STATE_STMT_BLOCK_DONE 10 /* End of block "}" */ +#define EJS_STATE_ARG_LIST 11 /* Method arg list */ +#define EJS_STATE_ARG_LIST_DONE 12 +#define EJS_STATE_DEC_LIST 16 /* Declaration list */ +#define EJS_STATE_DEC_LIST_DONE 17 +#define EJS_STATE_DEC 18 /* Declaration statement */ +#define EJS_STATE_DEC_DONE 19 + +#define EJS_STATE_BEGIN EJS_STATE_STMT + +/* + * General parsing flags. + */ +#define EJS_FLAGS_EXE 0x1 /* Execute statements */ +#define EJS_FLAGS_LOCAL 0x2 /* Get local vars only */ +#define EJS_FLAGS_GLOBAL 0x4 /* Get global vars only */ +#define EJS_FLAGS_CREATE 0x8 /* Create var */ +#define EJS_FLAGS_ASSIGNMENT 0x10 /* In assignment stmt */ +#define EJS_FLAGS_DELETE 0x20 /* Deleting a variable */ +#define EJS_FLAGS_NEW 0x80 /* In a new stmt() */ +#define EJS_FLAGS_EXIT 0x100 /* Must exit */ +#define EJS_FLAGS_LHS 0x200 /* Left-hand-side of assignment */ +#define EJS_FLAGS_FORIN 0x400 /* In "for (v in ..." */ +#define EJS_FLAGS_CLASS_DEC 0x800 /* "class name [extends] name " */ +#define EJS_FLAGS_TRY 0x2000 /* In a try {} block */ +#define EJS_FLAGS_CATCH 0x4000 /* "catch (variable)" */ +#define EJS_FLAGS_DONT_GC 0x8000 /* Don't garbage collect */ +#define EJS_FLAGS_NO_ARGS 0x10000 /* Accessors don't use args */ +#define EJS_FLAGS_ENUM_HIDDEN 0x20000 /* Enumerate hidden fields */ +#define EJS_FLAGS_ENUM_BASE 0x40000 /* Enumerate base classes */ +#define EJS_FLAGS_TRACE_ARGS 0x80000 /* Support for printv */ +#define EJS_FLAGS_SHARED_SLAB 0x100000/* Using a shared slab */ + +/* + * Exceptions + */ +#define EJS_ARG_ERROR "ArgError" /**< Method argument error */ +#define EJS_ASSERT_ERROR "AssertError" /**< Assertion error */ +#define EJS_EVAL_ERROR "EvalError" /**< General evalation error */ +#define EJS_INTERNAL_ERROR "InternalError" /**< Internal error */ +#define EJS_IO_ERROR "IOError" /**< IO or data error */ +#define EJS_MEMORY_ERROR "MemoryError" /**< Memory allocation error */ +#define EJS_RANGE_ERROR "RangeError" /**< Data out of range (div by 0) */ +#define EJS_REFERENCE_ERROR "ReferenceError"/**< Object or property reference */ +#define EJS_SYNTAX_ERROR "SyntaxError" /**< Javascript syntax error */ +#define EJS_TYPE_ERROR "TypeError" /**< Wrong type supplied */ + +/* + * E4X + */ +#if BLD_FEATURE_EJS_E4X +#if BLD_FEATURE_SQUEEZE +#define E4X_BUF_SIZE 512 /* Initial buffer size for tokens */ +#define E4X_BUF_MAX (32 * 1024) /* Max size for tokens */ +#define E4X_MAX_NODE_DEPTH 24 /* Max nesting of tags */ +#else +#define E4X_BUF_SIZE 4096 +#define E4X_BUF_MAX (128 * 1024) +#define E4X_MAX_NODE_DEPTH 128 +#endif + +#define E4X_MAX_ELT_SIZE (E4X_BUF_MAX-1) +#define E4X_TEXT_PROPERTY "-txt" +#define E4X_TAG_NAME_PROPERTY "-tag" +#define E4X_COMMENT_PROPERTY "-com" +#define E4X_ATTRIBUTES_PROPERTY "-att" +#define E4X_PI_PROPERTY "-pi" +#define E4X_PARENT_PROPERTY "-parent" +#endif + +#if BLD_FEATURE_MULTITHREAD +/** + * Multithreaded lock function + */ +typedef void (*EjsLockFn)(void *lockData); +/** + * Multithreaded unlock function + */ +typedef void (*EjsUnlockFn)(void *lockData); +#endif + +/* + * Token limits + */ +#define EJS_MAX_LINE 128 /* Maximum input line buffer */ +#define EJS_MAX_TOKEN 640 /* Max input parse token */ +#define EJS_TOKEN_STACK 3 /* Put back token stack */ + +/* + * Putback token + */ + +typedef struct EjsToken { + char tokbuf[EJS_MAX_TOKEN]; + int tid; /* Token ID */ +} EjsToken; + +/* + * EJ evaluation block structure + */ +typedef struct EjsInput { + EjsToken putBack[EJS_TOKEN_STACK]; /* Put back token stack */ + int putBackIndex; /* Top of stack index */ + char line[EJS_MAX_LINE]; /* Current line */ + char *fileName; /* File or script name */ + int lineLength; /* Current line length */ + int lineNumber; /* Parse line number */ + int lineColumn; /* Column in line */ + struct EjsInput *next; /* Used for backtraces */ + const char *procName; /* Gives name in backtrace */ + const char *script; /* Input script for parsing */ + char *scriptServp; /* Next token in the script */ + int scriptSize; /* Length of script */ + char tokbuf[EJS_MAX_TOKEN]; /* Current token */ + int tid; /* Token ID */ + char *tokEndp; /* Pointer past end of token */ + char *tokServp; /* Pointer to next token char */ + struct EjsInput *nextInput; /* Free list of input structs */ +} EjsInput; + +/* + * Method call structure + */ +typedef struct EjsProc { + MprArray *args; /* Args for method */ + EjsVar *fn; /* Method definition */ + char *procName; /* Method name */ +} EjsProc; + + +/** + * @overview EJScript Service structure + * @description The EJScript service manages the overall language runtime. It + * is the factory that creates interpreter instances via ejsCreateInterp. + * The EJScript service creates a master interpreter that holds the + * standard language classes and properties. When user interpreters are + * created, they reference (without copying) the master interpreter to + * gain access to the standard classes and types. + * @stability Prototype. + * @library libejs. + * @see ejsOpenService, ejsCloseService, ejsCreateInterp, ejsDestoryInterp + */ +typedef struct EjsService { + EjsVar *globalClass; /* Global class */ + struct Ejs *master; /* Master Interp inherited by all */ +#if BLD_FEATURE_MULTITHREAD + EjsLockFn lock; + EjsUnlockFn unlock; + void *lockData; +#endif +} EjsService; + + +/* + * Memory statistics + */ +typedef struct EjsMemStats { + uint maxMem; + uint usedMem; +} EjsMemStats; + + +/* + * Garbage collection block alignment + */ +#define EJS_ALLOC_ALIGN(ptr) \ + (((ptr) + sizeof(void*) - 1) & ~(sizeof(void*) - 1)) + +/* + * Default GC tune factors + */ +#define EJS_GC_START_THRESHOLD (32 * 1024) + +/* + * The Garbage collector is a generational collector. It ages blocks and + * optimizes the mark / sweep algorithm to focus on new and recent blocks + */ +typedef enum EjsGeneration { + EJS_GEN_NEW = 0, + EJS_GEN_RECENT_1 = 1, + EJS_GEN_RECENT_2 = 2, + EJS_GEN_OLD = 3, + EJS_GEN_PERMANENT = 4, + EJS_GEN_MAX = 5, +} EjsGeneration; + +/* + * Garbage collector control + */ +typedef struct EjsGC { + bool enable; + bool enableDemandCollect; + bool enableIdleCollect; + /* + * maxMemory should be set to be 95% of the real max memory limit + */ + uint maxMemory; /* Above this, Throw Memory exception. */ + int workQuota; /* Quota of work before GC */ + int workDone; /* Count of allocations */ + int degraded; /* Have exceeded maxMemory */ + + /* + * Debug Levels 0-N (increases verbosity) + * 1 -- Sweep and collection count + * 2 -- Trace objects deleted + * 3 -- Trace objects marked + * 4 -- Print alloc report when needing a demand allocation + * + */ + int debugLevel; /* In debug mode */ + int collecting; /* Running garbage collection */ + uint collectionCount; /* Number of times GC ran */ +#if BLD_DEBUG + int gcIndent; /* Indent formatting */ + int objectsInUse; /* Objects currently reachable */ + int propertiesInUse; /* Properties currently reachable */ +#endif +} EjsGC; + +/* + * Slab memory allocation + */ +typedef struct EjsSlab { + uint allocIncrement; /* Growth increment in slab */ + uint size; /* Size of allocations */ + EjsGCLink freeList; /* Free list (only next ptr is used) */ + EjsObj *lastRecentBlock; /* Saved for GC age generations phase */ + EjsGCLink allocList[EJS_GEN_MAX]; /* Allocated block list */ + +#if BLD_FEATURE_ALLOC_STATS + uint totalAlloc; /* Total count of allocation calls */ + uint freeCount; /* Number of blocks on the slab freelist */ + uint allocCount; /* Number of allocated blocks */ + uint peakAllocated; /* Peak allocated */ + uint peakFree; /* Peak on the free list */ + uint totalReclaimed; /* Total blocks reclaimed on sweeps */ + uint totalSweeps; /* Total sweeps */ +#endif +} EjsSlab; + + +/** + * @overview EJ interpreter control structure. + * @description EJ allocates one control structure per active interpreter. + * The \ref ejsCreateInterp routine creates the Ejs structure and returns + * a reference to be used in subsequent EJ API calls. + * @stability Prototype. + * @library libejs. + * @see ejsCreateInterp, ejsDestroyInterp, ejsOpenService + */ +struct Ejs { + void *altHandle; /* Alternate callback handle */ + bool castAlloc; /* True if castTemp is allocated */ + char *castTemp; /* Temporary string for casting */ + char *currentClass; /* Current class name */ + EjsVar *currentObj; /* Ptr to current object */ + EjsVar *thisObject; /* Ptr to current "this" */ + EjsProperty *currentProperty; /* Ptr to current property */ + EjsGC gc; /* Garbage collector control */ + char *errorMsg; /* Error message */ + char *fileName; /* File or script name */ + int lineNumber; /* File line number */ + int scriptStatus; /* Status to exit() */ + int flags; /* Flags */ + MprArray *frames; /* List of variable frames */ + EjsVar *global; /* Global object */ + EjsVar *objectClass; /* Object class */ + int gotException; /* Exception thrown */ + EjsInput *input; /* Input evaluation block */ + int depth; /* Recursion depth */ + EjsVar *local; /* Local object */ + int maxDepth; /* Maximum depth for formatting */ + void *primaryHandle; /* primary callback handle */ + EjsProc *proc; /* Current method */ + int recurseCount; /* Recursion counter */ + EjsVar *result; /* Variable result */ + int tid; /* Current token id */ + char *token; /* Pointer to token string */ + EjsVar tokenNumber; /* Parsed number */ + EjsService *service; /* Service object */ + void *userData; /* Method user data */ + + EjsSlab *slabs; /* Memory allocation slabs */ + MprCtx slabAllocContext; /* Allocation context */ + EjsInput *inputList; /* Free list of input structs */ + +#if BLD_FEATURE_MULTITHREAD + EjsLockFn lock; /* Lock method */ + EjsUnlockFn unlock; /* Unlock method */ + void *lockData; /* Lock data argument */ +#endif +#define EJS_MAX_STACK (10 * 1024) + char stack[EJS_MAX_STACK]; /* Local variable stack */ + char *stkPtr; /* Local variable stack ptr */ + void *inputMarker; /* Recurse protection */ +}; + + +typedef struct EjsModule +{ + int dummy; +} EjsModule; + + +/* + * Method callback when using Alternate handles. GaCompat uses these and + * passes the web server request structure via the altHandle. + */ +typedef void *EjsHandle; +typedef int (*EjsAltCMethod)(Ejs *ejs, EjsHandle altHandle, + EjsVar *thisObj, int argc, EjsVar **argv); +typedef int (*EjsAltStringCMethod)(Ejs *ejs, EjsHandle altHandle, + EjsVar *thisObj, int argc, char **argv); + + +/* + * API Constants + */ +#define EJS_USE_OWN_SLAB 1 + +/******************************** Internal API ********************************/ +/* + * Ejs Lex + */ +extern int ejsLexOpenScript(Ejs *ejs, const char *script); +extern void ejsLexCloseScript(Ejs *ejs); +extern int ejsInitInputState(EjsInput *ip); +extern void ejsLexSaveInputState(Ejs *ejs, EjsInput* state); +extern void ejsLexFreeInputState(Ejs *ejs, EjsInput* state); +extern void ejsLexRestoreInputState(Ejs *ejs, EjsInput* state); +extern int ejsLexGetToken(Ejs *ejs, int state); +extern void ejsLexPutbackToken(Ejs *ejs, int tid, char *string); + +/* + * Parsing + */ +extern int ejsParse(Ejs *ejs, int state, int flags); +extern int ejsGetFlags(Ejs *ejs); + +/* + * Create variable scope blocks + */ +extern int ejsOpenBlock(Ejs *ejs); +extern int ejsSetBlock(Ejs *ejs, EjsVar *local); +extern int ejsCloseBlock(Ejs *ejs, int vid); +extern int ejsEvalBlock(Ejs *ejs, char *script, EjsVar *vp); +extern void ejsSetFileName(Ejs *ejs, const char *fileName); + +/* + * Class definitions + */ +extern EjsVar *ejsCreateSimpleClass(Ejs *ejs, EjsVar *baseClass, + const char *className); +extern int ejsDefineObjectClass(Ejs *ejs); +extern int ejsDefineArrayClass(Ejs *ejs); +extern int ejsDefineBooleanClass(Ejs *ejs); +extern int ejsDefineErrorClasses(Ejs *ejs); +extern int ejsDefineFileClass(Ejs *ejs); +extern int ejsDefineFileSystemClass(Ejs *ejs); +extern int ejsDefineHTTPClass(Ejs *ejs); +extern int ejsDefineFunctionClass(Ejs *ejs); +extern int ejsDefineNumberClass(Ejs *ejs); +extern int ejsDefineStringClass(Ejs *ejs); +extern int ejsDefineDateClass(Ejs *ejs); +extern int ejsDefineStandardClasses(Ejs *ejs); + +#if BLD_FEATURE_EJS_E4X +extern int ejsDefineXmlClasses(Ejs *ejs); +extern EjsVar *ejsCreateXml(Ejs *ejs); +#endif + +#if BLD_FEATURE_EJS_DB +extern int ejsDefineDbClasses(Ejs *ejs); +#endif + +/* + * System class definitions + */ +extern int ejsDefineSystemClasses(Ejs *ejs); +extern int ejsDefineSystemClass(Ejs *ejs); +extern int ejsDefineAppClass(Ejs *ejs); +extern int ejsDefineDebugClass(Ejs *ejs); +extern int ejsDefineLogClass(Ejs *ejs); +extern int ejsDefineMemoryClass(Ejs *ejs); +extern int ejsDefineGCClass(Ejs *ejs); +extern int ejsDefineGlobalProperties(Ejs *ejs); + +extern int ejsTermSystemClasses(Ejs *ejs); +extern void ejsTermHTTPClass(Ejs *ejs); + +extern int ejsCreateObjectModel(Ejs *ejs); + +/* + * Class constructors + */ +extern int ejsArrayConstructor(Ejs *ejs, EjsVar *thisObj, int argc, + EjsVar **argv); +extern int ejsXmlConstructor(Ejs *ejs, EjsVar *thisObj, int argc, + EjsVar **argv); +extern int ejsXmlListConstructor(Ejs *ejs, EjsVar *thisObj, int argc, + EjsVar **argv); +extern int ejsBooleanConstructor(Ejs *ejs, EjsVar *thisObj, int argc, + EjsVar **agv); +extern int ejsFunctionConstructor(Ejs *ejs, EjsVar *thisObj, int argc, + EjsVar **agv); +extern int ejsNumberConstructor(Ejs *ejs, EjsVar *thisObj, int argc, + EjsVar **argv); +extern int ejsStringConstructor(Ejs *ejs, EjsVar *thisObj, int argc, + EjsVar **argv); +extern int ejsDateConstructor(Ejs *ejs, EjsVar *thisObj, + int argc, EjsVar **argv); + +/* + * Garbage collection + */ +extern void ejsGCInit(Ejs *ejs, int objInc, int propInc, int varInc, + int strInc); +extern int ejsIsTimeForGC(Ejs *ep, int timeTillNextEvent); + +extern bool ejsSetGCDebugLevel(Ejs *ep, int debugLevel); +extern void ejsSweepAll(Ejs *ep); + +extern EjsObj *ejsAllocObj(EJS_LOC_DEC(ejs, loc)); +extern EjsProperty *ejsAllocProperty(EJS_LOC_DEC(ejs, loc)); +extern EjsVar *ejsAllocVar(EJS_LOC_DEC(ejs, loc)); +extern void ejsFree(Ejs *ejs, void *ptr, int slabIndex); + +extern int ejsCollectGarbage(Ejs *ejs, int slabIndex); +extern int ejsIncrementalCollectGarbage(Ejs *ejs); + +#if BLD_DEBUG +extern void ejsDumpObjects(Ejs *ejs); +#endif + +#if BLD_FEATURE_ALLOC_STATS +extern void ejsPrintAllocReport(Ejs *ejs, bool printLeakReport); +#endif + +extern void ejsCleanInterp(Ejs *ejs, bool doStats); +extern void ejsSetInternalMethods(Ejs *ejs, EjsVar *op); +extern void ejsSetPrimaryHandle(Ejs *ep, void *primaryHandle); +extern void ejsSetAlternateHandle(Ejs *ep, void *alternateHandle); +extern void *ejsGetUserData(Ejs *ejs); + +/* + * Could possibly make these routines public + */ + +extern int ejsSetGCMaxMemory(Ejs *ep, uint maxMemory); +extern uint ejsGetUsedMemory(Ejs *ejs); +extern uint ejsGetAllocatedMemory(Ejs *ejs); +extern uint ejsGetAvailableMemory(Ejs *ejs); +extern char *ejsFormatStack(Ejs* ep);; + +/********************************* Prototypes *********************************/ +#if BLD_FEATURE_MULTITHREAD +extern int ejsSetServiceLocks(EjsService *sp, EjsLockFn lock, + EjsUnlockFn unlock, void *data); +#endif + +/* + * Ejs service and interpreter management + */ +extern EjsService *ejsOpenService(MprCtx ctx); +extern void ejsCloseService(EjsService *sp, bool doStats); + +extern Ejs *ejsCreateInterp(EjsService *sp, void *primaryHandle, + void *altHandle, EjsVar *global, bool useOwnSlab); +extern void ejsDestroyInterp(Ejs *ejs, bool doStats); + +extern Ejs *ejsGetMasterInterp(EjsService *sp); +extern EjsVar *ejsGetGlobalClass(Ejs *ejs); + +/* + * Module support + */ +extern EjsModule *ejsCreateModule(const char *name, const char *version, + int (*start)(EjsModule*), int (*stop)(EjsModule*)); + +/* + * Native Objects + */ + +void ejsSetNativeData(EjsVar *obj, void *data); +void ejsSetNativeHelpers(Ejs *ejs, EjsVar *nativeClass, + int (*createInstance)(Ejs *ejs, EjsVar *thisObj, int argc, + EjsVar **argv), + void (*disposeInstance)(Ejs *ejs, EjsVar *thisObj), + bool (*hasProperty)(Ejs *ejs, EjsVar *thisObj, const char *prop), + int (*deleteProperty)(Ejs *ejs, EjsVar *thisObj, const char *prop), + int (*getProperty)(Ejs *ejs, EjsVar *thisObj, const char *prop, + EjsVar *dest), + int (*setProperty)(Ejs *ejs, EjsVar *thisObj, const char *prop, + EjsVar *value), + int (*doOperator)(Ejs *ejs, EjsVar *thisObj, EjsOp *op, EjsVar + *result, EjsVar *lhs, EjsVar *rhs, int *code) + ); + +/* + * Evaluation methods + */ +extern int ejsEvalFile(Ejs *ejs, const char *path, EjsVar *result); +extern int ejsEvalScript(Ejs *ejs, const char *script, EjsVar *result); +extern int ejsRunMethod(Ejs *ejs, EjsVar *obj, + const char *methodName, MprArray *args); +extern int ejsRunMethodCmd(Ejs *ejs, EjsVar *obj, + const char *methodName, const char *cmdFmt, ...); +extern EjsVar *ejsGetReturnValue(Ejs *ejs); + +extern EjsVar *ejsGetLocalObj(Ejs *ejs); +extern EjsVar *ejsGetGlobalObj(Ejs *ejs); + +/* + * Define a class in the specified interpreter. If used with the default + * interpeter, then the class is defined for all interpreters. + */ +extern EjsVar *ejsDefineClass(Ejs *ejs, const char *className, + const char *extends, EjsCMethod constructor); +extern EjsVar *ejsGetClass(Ejs *ejs, EjsVar *parentClass, + const char *className); + +extern const char *ejsGetClassName(EjsVar *obj); +extern const char *ejsGetBaseClassName(EjsVar *obj); + +extern bool ejsIsSubClass(EjsVar *target, EjsVar *baseClass); +extern EjsVar *ejsGetBaseClass(EjsVar *obj); +extern void ejsSetBaseClass(EjsVar *obj, EjsVar *baseClass); + + +#define ejsCreateSimpleObj(ejs, className) \ + ejsCreateSimpleObjInternal(EJS_LOC_ARGS(ejs), className) +extern EjsVar *ejsCreateSimpleObjInternal(EJS_LOC_DEC(ejs, loc), + const char *className); + +#define ejsCreateSimpleObjUsingClass(ejs, baseClass) \ + ejsCreateSimpleObjUsingClassInt(EJS_LOC_ARGS(ejs), \ + baseClass) +extern EjsVar *ejsCreateSimpleObjUsingClassInt(EJS_LOC_DEC(ejs, loc), + EjsVar *baseClass); + +/* + * This will create an object and call all required constructors + */ +extern EjsVar *ejsCreateObj(Ejs *ejs, EjsVar *obj, + const char *className, const char *constructorArgs); + +#define ejsCreateObjUsingArgv(ejs, obj, className, args) \ + ejsCreateObjUsingArgvInternal(EJS_LOC_ARGS(ejs), obj, \ + className, args) +extern EjsVar *ejsCreateObjUsingArgvInternal(EJS_LOC_DEC(ejs, loc), + EjsVar *obj, const char *className, MprArray *args); + +#define ejsCreateArray(ejs, size) \ + ejsCreateArrayInternal(EJS_LOC_ARGS(ejs), size) +extern EjsVar *ejsCreateArrayInternal(EJS_LOC_DEC(ejs, loc), + int size); + +/* + * Array methods. MOB -- need other array methods + */ +/* MOB -- spell out element */ +extern EjsVar *ejsAddArrayElt(Ejs *ejs, EjsVar *op, EjsVar *element, + EjsCopyDepth copyDepth); +/* + * Required: Array methods + * + array = obj.getMethods(); + array = obj.getProperties(); + + array.property.isPublic(); + array.property.isPrivate(); + array.property.isMethod(); + array.property.isEnumerable(); + array.property.isReadOnly(); + array.property.allowsNonUnique(); + array.property.getParent(); +*/ + +/* MOB -- should we have an API that takes a EjsCopyDepth */ +extern void ejsSetReturnValue(Ejs *ejs, EjsVar *vp); +extern void ejsSetReturnValueAndFree(Ejs *ejs, EjsVar *vp); +extern void ejsSetReturnValueToBoolean(Ejs *ejs, bool value); +extern void ejsSetReturnValueToBinaryString(Ejs *ejs, + const uchar *value, int len); +extern void ejsSetReturnValueToInteger(Ejs *ejs, int value); +extern void ejsSetReturnValueToNumber(Ejs *ejs, EjsNum value); +extern void ejsSetReturnValueToString(Ejs *ejs, const char *value); +extern void ejsSetReturnValueToUndefined(Ejs *ejs); + +/* + * Variable access and control. The fullName arg can contain "[]" and "." + */ +extern bool ejsGetBool(Ejs *ejs, const char *fullName, bool defaultValue); +extern int ejsGetInt(Ejs *ejs, const char *fullName, int defaultValue); +extern const char *ejsGetStr(Ejs *ejs, const char *fullName, + const char *defaultValue); +extern EjsVar *ejsGetVar(Ejs *ejs, const char *fullName); + +extern int ejsSetBool(Ejs *ejs, const char *fullName, bool value); +extern int ejsSetInt(Ejs *ejs, const char *fullName, int value); +extern int ejsSetStr(Ejs *ejs, const char *fullName, const char *value); +extern int ejsSetVar(Ejs *ejs, const char *fullName, const EjsVar *value); +extern int ejsSetVarAndFree(Ejs *ejs, const char *fullName, EjsVar *value); + +extern int ejsDeleteVar(Ejs *ejs, const char *fullName); + +/* + * Error handling + */ +extern void ejsError(Ejs *ejs, const char *errorType, const char *fmt, + ...) PRINTF_ATTRIBUTE(3,4); +/* MOB -- this should take no arguments */ +extern void ejsArgError(Ejs *ejs, const char *msg); +extern void ejsInternalError(Ejs *ejs, const char *msg); +extern void ejsMemoryError(Ejs *ejs); +extern void ejsSyntaxError(Ejs *ejs, const char *msg); + +/* + * Utility methods + */ +extern int ejsParseArgs(int argc, char **argv, const char *fmt, ...); + +extern void ejsExit(Ejs *ejs, int status); +extern bool ejsIsExiting(Ejs *ejs); +extern void ejsClearExiting(Ejs *ejs); + +extern bool ejsGotException(Ejs *ejs); + +/* MOB -- rename Method to Function */ +extern void ejsFreeMethodArgs(Ejs *ep, MprArray *args); +extern int ejsStrcat(Ejs *ep, EjsVar *dest, EjsVar *src); + +/* + * Debugging routines + */ +extern char *ejsGetErrorMsg(Ejs *ejs); +extern int ejsGetLineNumber(Ejs *ejs); +extern void ejsTrace(Ejs *ejs, const char *fmt, ...); + +/* + * Multithreaded lock routines + */ +#if BLD_FEATURE_MULTITHREAD +#define ejsLock(sp) if (sp->lock) { (sp->lock)(sp->lockData); } else +#define ejsUnlock(sp) if (sp->unlock) { (sp->unlock)(sp->lockData); } else +#else +#define ejsLock(sp) +#define ejsUnlock(sp) +#endif + +#ifdef __cplusplus +} +#endif +#endif /* _h_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 + */ diff --git a/source4/lib/appweb/ejs-2.0/ejs/ejsClass.c b/source4/lib/appweb/ejs-2.0/ejs/ejsClass.c new file mode 100644 index 0000000000..58609adf3f --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/ejsClass.c @@ -0,0 +1,273 @@ +/* + * @file ejsClass.c + * @brief EJS class support + */ +/********************************* 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 + +/************************************ Code ************************************/ +/* + * Internal API + * + * Routine to create a simple class object. This routine will create a + * stand-alone class object. Callers must insert this into the relevant + * "global" object for name resolution. From these class objects, instance + * objects may be created via the javascript "new" command. + * + * Users should use ejsDefineClass + */ + +EjsVar *ejsCreateSimpleClass(Ejs *ep, EjsVar *baseClass, const char *className) +{ + EjsProperty *pp; + EjsVar *classObj; + + /* + * Create an instance of an Object to act as the static class object + */ + classObj = ejsCreateSimpleObjUsingClass(ep, baseClass); + if (classObj == 0) { + mprAssert(classObj); + return 0; + } + ejsSetClassName(ep, classObj, className); + + /* + * Set the propotype property to point to this class. + * Note: this is a self reference so the alive bit will not be turned on. + */ + pp = ejsSetProperty(ep, classObj, "prototype", classObj); + ejsMakePropertyEnumerable(pp, 0); + + return classObj; +} + +/******************************************************************************/ +/* + * Define a class in the given interpreter. If parentClass is specified, the + * class is defined in the parent. Otherwise, the class will be defined + * locally/globally. ClassName and extends are full variable specs + * (may contain ".") + */ + +EjsVar *ejsDefineClass(Ejs *ep, const char *className, const char *extends, + EjsCMethod constructor) +{ + EjsVar *parentClass, *classObj, *baseClass, *vp; + char *name; + char *cp; + + /* + * If the className is a qualified name (with "."), then get the + * parent class name. + */ + name = mprStrdup(ep, className); + cp = strrchr(name, '.'); + if (cp != 0) { + *cp++ = '\0'; + className = cp; + parentClass = ejsFindProperty(ep, 0, 0, ep->global, ep->local, name, 0); + if (parentClass == 0 || parentClass->type != EJS_TYPE_OBJECT) { + mprError(ep, MPR_LOC, "Can't find class's parent class %s", name); + mprFree(name); + return 0; + } + + } else { + /* + * Simple class name without a "." so create the class locally + * if a local scope exists, otherwise globally. + */ + parentClass = (ep->local) ? ep->local : ep->global; + } + + if (parentClass == 0) { + mprError(ep, MPR_LOC, "Can't find parent class"); + mprFree(name); + return 0; + } + + /* OPT should use function that doesn't parse [] . */ + baseClass = ejsGetClass(ep, 0, extends); + if (baseClass == 0) { + mprAssert(baseClass); + mprFree(name); + return 0; + } + + classObj = ejsCreateSimpleClass(ep, baseClass, className); + if (classObj == 0) { + mprAssert(classObj); + mprFree(name); + return 0; + } + + if (constructor) { + ejsDefineCMethod(ep, classObj, className, constructor, 0); + } + + ejsSetPropertyAndFree(ep, parentClass, className, classObj); + + vp = ejsGetPropertyAsVar(ep, parentClass, className); + mprFree(name); + + return vp; +} + +/******************************************************************************/ +/* + * Find a class and return the property defining the class. ClassName may + * contain "." and is interpreted relative to obj. Obj is typically some + * parent object, ep->local or ep->global. If obj is null, then the global + * space is used. + */ + +EjsVar *ejsGetClass(Ejs *ep, EjsVar *obj, const char *className) +{ + EjsVar *vp; + + mprAssert(ep); + + /* + * Search first for a constructor of the name of class + * global may not be defined yet. + */ + if (obj) { + vp = ejsFindProperty(ep, 0, 0, obj, 0, className, 0); + + } else { + mprAssert(ep->global); + vp = ejsFindProperty(ep, 0, 0, ep->global, ep->local, className, 0); + } + if (vp == 0 || vp->type != EJS_TYPE_OBJECT) { + return 0; + } + + /* + * Return a reference to the prototype (self) reference. This + * ensures that even if "obj" is deleted, this reference will remain + * usable. + */ + return ejsGetPropertyAsVar(ep, vp, "prototype"); +} + +/******************************************************************************/ +/* + * Return the class name of a class or object + */ + +const char *ejsGetClassName(EjsVar *vp) +{ + EjsObj *obj; + + mprAssert(vp); + mprAssert(vp->type == EJS_TYPE_OBJECT); + mprAssert(vp->objectState->baseClass); + + if (vp == 0 || !ejsVarIsObject(vp)) { + return 0; + } + obj = vp->objectState; + + return obj->className; +} + +/******************************************************************************/ +/* + * Return the class name of an objects underlying class + * If called on an object, it returns the base class. + * If called on a class, it returns the base class for the class. + */ + +const char *ejsGetBaseClassName(EjsVar *vp) +{ + EjsObj *obj; + + mprAssert(vp); + mprAssert(vp->type == EJS_TYPE_OBJECT); + mprAssert(vp->objectState->baseClass); + + if (vp == 0 || !ejsVarIsObject(vp)) { + return 0; + } + obj = vp->objectState; + if (obj->baseClass == 0) { + return 0; + } + mprAssert(obj->baseClass->objectState); + + return obj->baseClass->objectState->className; +} + +/******************************************************************************/ + +EjsVar *ejsGetBaseClass(EjsVar *vp) +{ + if (vp == 0 || !ejsVarIsObject(vp) || vp->objectState == 0) { + mprAssert(0); + return 0; + } + return vp->objectState->baseClass; +} + +/******************************************************************************/ + +void ejsSetBaseClass(EjsVar *vp, EjsVar *baseClass) +{ + if (vp == 0 || !ejsVarIsObject(vp) || vp->objectState == 0) { + mprAssert(0); + return; + } + vp->objectState->baseClass = baseClass; +} + +/******************************************************************************/ + +#else +void ejsProcsDummy() {} + +/******************************************************************************/ +#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 + */ diff --git a/source4/lib/appweb/ejs-2.0/ejs/ejsCmd.c b/source4/lib/appweb/ejs-2.0/ejs/ejsCmd.c new file mode 100644 index 0000000000..b5279c949a --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/ejsCmd.c @@ -0,0 +1,468 @@ +/* + * @file ejsCmd.c + * @brief Embedded JavaScript (EJS) command line program. + * @overview + */ +/********************************* 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 && !BREW + +/************************************ Defines *********************************/ + +#define EJS_MAX_CMD_LINE (16 * 1024) +#define EJS_MAX_SCRIPT (4 * 1024 * 1024) +#define EJS_MAX_RESULT_SIZE (4 * 1024 * 1024) +#define EJS_PROMPT "ejs> " + +/****************************** Forward Declarations **************************/ + +static int parseFile(EjsService *ejsService, Ejs *ejs, const char *fileName, + const char *testName, MprFile *testLogFile); +static int ifConsole(); + +static int interactiveUse(MprApp *app, Ejs *ejs, FILE *input, + char *fileName); +static char *readCmd(MprApp *app, FILE *input); + +static int memoryFailure(MprApp *app, uint size, uint total, bool granted); + +static int isConsole = 0; +static int traceCmds = 0; +static int stats = 0; +static int verbose = 0; + +/************************************ Main ************************************/ + +int main(int argc, char *argv[]) +{ + MprApp *app; + const char *programName; + MprFile *testLogFile; + EjsService *ejsService; + Ejs *ejs; + char *commandLine; + const char *testName; + char *argp, *cmd, *testLog; + int i, rc, nextArg, err, len, firstArg, iterations, debugLevel; + + app = mprInit(memoryFailure); + + isConsole = ifConsole(); + programName = mprGetBaseName(argv[0]); + debugLevel = 0; + + ejsService = ejsOpenService(app); + if (ejsService == 0) { + mprError(app, MPR_LOC, "Can't initialize the EJS service."); + return -1; + } + + err = 0; + iterations = 1; + stats = 0; + testLog = getenv("TEST_LOG"); + testLogFile = 0; + testName = 0; + + for (nextArg = 1; nextArg < argc; nextArg++) { + argp = argv[nextArg]; + if (*argp != '-') { + break; + } + if (strcmp(argp, "--debug") == 0) { + if (nextArg >= argc) { + err++; + } else { + debugLevel = atoi(argv[++nextArg]); + } + + } else if (strcmp(argp, "--stats") == 0) { + stats++; + + } else if (strcmp(argp, "--trace") == 0) { + traceCmds++; + + } else if (strcmp(argp, "--iterations") == 0) { + if (nextArg >= argc) { + err++; + } else { + iterations = atoi(argv[++nextArg]); + } + + } else if (strcmp(argp, "--log") == 0) { + /* Get file to log test results to when using ejs as a test shell */ + if (nextArg >= argc) { + err++; + } else { + testLog = argv[++nextArg]; + } + + } else if (strcmp(argp, "--testName") == 0) { + if (nextArg >= argc) { + err++; + } else { + testName = argv[++nextArg]; + } + + } else if (strcmp(argp, "-v") == 0) { + verbose++; + + } else if (strcmp(argp, "-vv") == 0) { + verbose += 2; + + } else if (strcmp(argp, "--verbose") == 0) { + verbose += 2; + + } else { + err++; + break; + } + if (err) { + mprErrorPrintf(app, + "Usage: %s [options] files... or\n" + " %s < file or\n" + " %s or\n" + " Switches:\n" + " --iterations num # Number of iterations to eval file\n" + " --stats # Output stats on exit\n" + " --testName name # Set the test name", + programName, programName, programName); + return -1; + } + } + + if (testName) { + i = 0; + commandLine = 0; + len = mprAllocStrcat(MPR_LOC_ARGS(app), &commandLine, 0, " ", + mprGetBaseName(argv[i++]), 0); + for (; i < argc; i++) { + len = mprReallocStrcat(MPR_LOC_ARGS(app), &commandLine, 0, len, + " ", argv[i], 0); + } + mprPrintf(app, " %s\n", commandLine); + } + if (testLog) { + testLogFile = mprOpen(app, testLog, + O_CREAT | O_APPEND | O_WRONLY | O_TEXT, 0664); + if (testLogFile == 0) { + mprError(app, MPR_LOC, "Can't open %s", testLog); + return MPR_ERR_CANT_OPEN; + } + mprFprintf(testLogFile, "\n %s\n", commandLine); + } + + ejs = ejsCreateInterp(ejsService, 0, 0, 0, 0); + if (ejs == 0) { + mprError(app, MPR_LOC, "Can't create EJS interpreter"); + ejsCloseService(ejsService, stats); + if (testLogFile) { + mprClose(testLogFile); + } + mprTerm(app, stats); + exit(-1); + } + + if (debugLevel > 0) { + ejsSetGCDebugLevel(ejs, debugLevel); + } + + rc = 0; + + if (nextArg < argc) { + /* + * Process files supplied on the command line + */ + firstArg = nextArg; + for (i = 0; i < iterations; i++) { + for (nextArg = firstArg; nextArg < argc; nextArg++) { + rc = parseFile(ejsService, ejs, argv[nextArg], testName, + testLogFile); + if (rc < 0) { + return rc; + } + } + } + if (testName) { + if (verbose == 1) { + mprPrintf(app, "\n"); + } + if (verbose <= 1) { + mprPrintf(app, " # PASSED all tests for \"%s\"\n", testName); + } + } + + } else if (! isConsole) { + /* + * Read a script from stdin + */ + cmd = readCmd(app, stdin); + + ejsSetFileName(ejs, "stdin"); + + rc = ejsEvalScript(ejs, cmd, 0); + if (rc < 0) { + mprPrintf(app, "ejs: Error: %s\n", ejsGetErrorMsg(ejs)); + } + mprFree(cmd); + + } else { + /* + * Interactive use. Read commands from the command line. + */ + rc = interactiveUse(app, ejs, stdin, "stdin"); + } + + /* + * Cleanup. Do stats if required. + */ + if (ejs) { + ejsCleanInterp(ejs, 0); + ejsCleanInterp(ejs->service->master, 0); + ejsDestroyInterp(ejs, 0); + } + + ejsCloseService(ejsService, stats); + + if (testLogFile) { + mprClose(testLogFile); + } + + mprTerm(app, stats); + return rc; +} + +/******************************************************************************/ + +static int parseFile(EjsService *ejsService, Ejs *ejs, const char *fileName, + const char *testName, MprFile *testLogFile) +{ + int rc; + + if (testName && verbose == 1) { + mprPrintf(ejs, "."); + } + if (verbose > 1) { + mprPrintf(ejs, "File: %s\n", fileName); + } + + rc = ejsEvalFile(ejs, fileName, 0); + + if (testName) { + char fileBuf[MPR_MAX_FNAME], *cp; + mprStrcpy(fileBuf, sizeof(fileBuf), fileName); + if ((cp = strstr(fileBuf, ".ejs")) != 0) { + *cp = '\0'; + } + if (rc == 0) { + if (verbose > 1) { + mprPrintf(ejs, " # PASSED test \"%s.%s\"\n", testName, + fileBuf); + } + if (testLogFile) { + mprFprintf(testLogFile, " # PASSED test \"%s.%s\"\n", + testName, fileBuf); + } + + } else { + + mprPrintf(ejs, "FAILED test \"%s.%s\"\nDetails: %s\n", + testName, fileBuf, ejsGetErrorMsg(ejs)); + + if (testLogFile) { + mprFprintf(testLogFile, + "FAILED test \"%s.%s\"\nDetails: %s\n", + testName, fileBuf, ejsGetErrorMsg(ejs)); + } + } + } else if (rc < 0) { + mprPrintf(ejs, "ejs: %sIn file \"%s\"\n", + ejsGetErrorMsg(ejs), fileName); + } + return rc; +} + +/******************************************************************************/ + +static char *readCmd(MprApp *app, FILE *input) +{ + char line[EJS_MAX_CMD_LINE]; + char *cmd; + int len, cmdLen; + + cmd = 0; + cmdLen = 0; + + line[sizeof(line) - 1] = '\0'; + + while (1) { + + if (fgets(line, sizeof(line) - 1, input) == NULL) { + break; + } + + len = strlen(line); + + if (line[len - 1] == '\\') { + line[len - 1] = '\0'; + } + cmdLen = mprReallocStrcat(MPR_LOC_ARGS(app), &cmd, EJS_MAX_SCRIPT, + cmdLen, 0, line, 0); + } + return cmd; +} + +/******************************************************************************/ + +static int interactiveUse(MprApp *app, Ejs *ejs, FILE *input, char *fileName) +{ + EjsVar result; + char line[EJS_MAX_CMD_LINE]; + char *cmd, *buf; + int len, cmdLen, rc; + + cmd = 0; + cmdLen = 0; + + line[sizeof(line) - 1] = '\0'; + + ejsSetFileName(ejs, "console"); + + while (! ejsIsExiting(ejs)) { + + if (isConsole) { + write(1, EJS_PROMPT, strlen(EJS_PROMPT)); + } + + if (fgets(line, sizeof(line) - 1, input) == NULL) { + break; + } + + len = strlen(line); + while (len > 0 && + (line[len - 1] == '\n' || line[len - 1] == '\r')) { + len--; + line[len] = '\0'; + } + + if (line[len - 1] == '\\') { + line[len - 1] = '\0'; + cmdLen = mprReallocStrcat(MPR_LOC_ARGS(app), &cmd, EJS_MAX_SCRIPT, + cmdLen, 0, line, 0); + + } else { + + cmdLen = mprReallocStrcat(MPR_LOC_ARGS(app), &cmd, EJS_MAX_SCRIPT, + cmdLen, 0, line, 0); + + + if (traceCmds) { + mprPrintf(ejs, "# %s\n", cmd); + } + + if (cmd[0] == 0x4 || cmd[0] == 0x26 || strcmp(cmd, "quit") == 0) { + ejsExit(ejs, 0); + + } else if ((rc = ejsEvalScript(ejs, cmd, &result)) < 0) { + + mprPrintf(app, "ejs: Error: %s\n", ejsGetErrorMsg(ejs)); + + if (! isConsole) { + return rc; + } + + } else { + if (isConsole || traceCmds) { + buf = ejsVarToString(ejs, &result); + mprPrintf(ejs, "%s\n", buf); + } + } + mprFree(cmd); + cmd = 0; + cmdLen = 0; + } + } + return 0; +} + +/******************************************************************************/ + +static int ifConsole() +{ +#if WIN + INPUT_RECORD irec[1]; + int records = 0; + + if (PeekConsoleInput(GetStdHandle(STD_INPUT_HANDLE), irec, 1, + &records) != 0) { + return 1; + } +#else + return isatty(0); +#endif + return 0; +} + +/******************************************************************************/ + +static int memoryFailure(MprApp *app, uint size, uint total, bool granted) +{ + if (!granted) { + mprPrintf(app, "Can't allocate memory block of size %d\n", size); + mprPrintf(app, "Total memory used %d\n", total); + exit(255); + } + mprPrintf(app, "Memory request for %d bytes exceeds memory red-line\n", + size); + mprPrintf(app, "Total memory used %d\n", total); + return 0; +} + +/******************************************************************************/ + +#else +void ejsCmdLineDummy() {} + +/******************************************************************************/ +#endif /* BLD_FEATURE_EJS */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/source4/lib/appweb/ejs-2.0/ejs/ejsGarbage.c b/source4/lib/appweb/ejs-2.0/ejs/ejsGarbage.c new file mode 100755 index 0000000000..264da05721 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/ejsGarbage.c @@ -0,0 +1,1214 @@ +/* + * @file ejsGarbage.c + * @brief EJS Garbage collector. + * @overview This implements a generational mark and sweep collection scheme. + */ +/********************************* Copyright **********************************/ +/* + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2006. 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 **************************/ + +static void mark(Ejs *ep); +static void markObjByVar(Ejs *ep, EjsVar *op); +static void markObj(EjsObj *obj); +static void markPerm(Ejs *ep, uint gen); +static int sweep(Ejs *ep, uint gen); +static EjsGCLink *ejsAlloc(EJS_LOC_DEC(ep, loc), int slabIndex); +static void ejsGracefulDegrade(Ejs *ep); +static void resetMarks(Ejs *ep, EjsSlab *slab); + +#if FUTURE +static void ageGenerations(Ejs *ep); +#endif + +#if BLD_DEBUG && (!BREW || BREW_SIMULATOR) +uint breakAddr; +#endif + +/************************************* Code ***********************************/ + +void ejsGCInit(Ejs *ep, int objInc, int propInc, int varInc, int strInc) +{ + EjsSlab *slab; + + if (ep->service && ep->service->globalClass) { + ep->service->globalClass->objectState->gcMarked = 1; + } + + slab = &ep->slabs[EJS_SLAB_OBJ]; + slab->allocIncrement = objInc; + slab->size = EJS_ALLOC_ALIGN(sizeof(EjsObj)); + + slab = &ep->slabs[EJS_SLAB_PROPERTY]; + slab->allocIncrement = propInc; + slab->size = EJS_ALLOC_ALIGN(sizeof(EjsProperty)); + + slab = &ep->slabs[EJS_SLAB_VAR]; + slab->allocIncrement = varInc; + slab->size = EJS_ALLOC_ALIGN(sizeof(EjsVar)); + + /* + * Initialize GC. + * Enable GC both idle and demand collections. + * Set no limits and garbage collect if the slabs are + * empty and we have used more than the THRESHOLD of ram. + */ + ep->gc.debugLevel = 0; + ep->gc.enable = 1; + ep->gc.enableIdleCollect = 1; + ep->gc.enableDemandCollect = 1; + ep->gc.workQuota = EJS_GC_WORK_QUOTA; + ep->gc.maxMemory = 0; +} + + +/******************************************************************************/ +#if BLD_FEATURE_ALLOC_STATS + +void ejsPrintAllocReport(Ejs *ep, bool printLeakReport) +{ + EjsSlab *slab; + char *name; + int slabIndex, isObj; + + for (slabIndex = 0; slabIndex < EJS_SLAB_MAX; slabIndex++) { + slab = &ep->slabs[slabIndex]; + if (slabIndex == EJS_SLAB_VAR) { + name = "var"; + } else if (slabIndex == EJS_SLAB_PROPERTY) { + name = "prop"; + } else { + name = "obj"; + } + mprLog(ep, 0, " "); + mprLog(ep, 0, " GC \"%s\" local slab", name); + mprLog(ep, 0, " Total blocks %14d", + slab->allocCount + slab->freeCount); + mprLog(ep, 0, " Block size %14d", slab->size); + mprLog(ep, 0, " Slab RAM allocated %14d", + (slab->allocCount + slab->freeCount) * slab->size); + mprLog(ep, 0, " Slab RAM in use %14d", + slab->allocCount * slab->size); + mprLog(ep, 0, " Blocks in use %14d", slab->allocCount); + mprLog(ep, 0, " Free blocks %14d", slab->freeCount); + mprLog(ep, 0, " Peak allocated %14d", slab->peakAllocated); + mprLog(ep, 0, " Peak free %14d", slab->peakFree); + mprLog(ep, 0, " Total allocations %14d", slab->totalAlloc); + mprLog(ep, 0, " Total blocks reclaimed %14d", slab->totalReclaimed); + mprLog(ep, 0, " Total sweeps %14d", slab->totalSweeps); + mprLog(ep, 0, " Allocation inc %14d", slab->allocIncrement); + } + + mprLog(ep, 0, " "); + mprLog(ep, 0, " Total EJS memory in use %10d", ejsGetUsedMemory(ep)); + mprLog(ep, 0, " Total EJS memory allocated %10d", + ejsGetAllocatedMemory(ep)); + + if (printLeakReport) { + mprLog(ep, 0, " "); + for (slabIndex = 0; slabIndex < EJS_SLAB_MAX; slabIndex++) { + int size; + + slab = &ep->slabs[slabIndex]; + + isObj = 0; + mprLog(ep, 0, " "); + if (slabIndex == EJS_SLAB_VAR) { + name = "var"; + size = sizeof(EjsVar); + } else if (slabIndex == EJS_SLAB_PROPERTY) { + name = "prop"; + size = sizeof(EjsProperty); + } else { + name = "obj"; + size = sizeof(EjsObj); + isObj++; + } +#if BLD_FEATURE_ALLOC_LEAK_TRACK +{ + EjsGCLink *lp; + EjsObj *obj; + int count; + + mprLog(ep, 0, "EJS Leak Report for \"%s\"", name); + count = 0; + + for (lp = slab->allocList[0].next; lp; lp = lp->next) { + mprLog(ep, 0, " %-20s %10d", lp->allocatedBy, size); + if (isObj) { + obj = (EjsObj*) lp; + mprLog(ep, 0, " %-20s %10d %s %s", + lp->allocatedBy, size, + obj->permanent ? "permanent" : "", + obj->alive ? "alive" : "" + ); + } else { + mprLog(ep, 0, " %-20s %10d", lp->allocatedBy, + size); + } + count++; + } + mprLog(ep, 0, " Total blocks %14d", count); +} +#endif + } + mprLog(ep, 0, " "); + } +} + +#endif +/******************************************************************************/ +/* + * Slab allocator + */ + +static EjsGCLink *ejsAlloc(EJS_LOC_DEC(ep, loc), int slabIndex) +{ + EjsSlab *slab; + EjsGCLink *block; + EjsGC *gc; + uint allocatedMemory; + int i; + + mprStackCheck(ep); + + if (slabIndex < 0 || slabIndex >= EJS_SLAB_MAX) { + mprAssert(0); + return 0; + } + + /* + * See if the slab has some free blocks + */ + slab = &ep->slabs[slabIndex]; + if ((block = slab->freeList.next) == 0) { + + allocatedMemory = ejsGetAllocatedMemory(ep); + gc = &ep->gc; + + /* + * No blocks available. If demand collection is enabled, try + * to garbage collect first. We collect if we have done a good + * work quota or we are over the max memory limit. + */ + if (slabIndex != EJS_SLAB_VAR && + ep->gc.enable && ep->gc.enableDemandCollect) { + if ((ep->gc.workDone > ep->gc.workQuota) || + (gc->maxMemory > 0 && allocatedMemory > gc->maxMemory)) { + +#if DEBUG_USE_ONLY + if (ep->gc.debugLevel > 0) { + mprLog(ep, 0, "Need GC, EJS RAM %d, MPR RAM %d\n", + allocatedMemory, mprGetAllocatedMemory(ep)); + if (ep->gc.debugLevel > 4) { + ejsPrintAllocReport(ep, 0); + } + } +#endif + if (ejsCollectGarbage(ep, slabIndex) == 0) { + block = slab->freeList.next; + } + } + } + + if (block == 0) { + if (gc->maxMemory > 0 && allocatedMemory > gc->maxMemory) { + /* + * We are above the max memory limit. We will fail this + * memory allocation, but allow subsequent allocations to + * permit error recovery. We gracefully degrade by setting + * slab chunk sizes to 1. This minimizes real memory + * consumption. This allows us to create + * an exception block to be created by upper layers. + */ + if (! gc->degraded) { + ejsGracefulDegrade(ep); + return 0; + } + } + + /* + * Still non available, so allocate more memory for a set of blocks + * OPT -- should bypass mprAlloc. Need mprMalloc. + */ + block = mprAlloc(ep->slabAllocContext, + slab->size * slab->allocIncrement); + if (block == 0) { + /* + * Now we're in trouble. We should really never get here + * as the graceful degrade will have signaled a memory + * allocation failure. + */ + mprAssert(block != 0); + return 0; + } + + /* + * Chain all the blocks together onto the slab free list + */ + for (i = slab->allocIncrement - 1; i >= 0; i--) { + block->next = slab->freeList.next; +#if BLD_DEBUG + block->magic = EJS_MAGIC_FREE; +#endif + slab->freeList.next = block; + block = (EjsGCLink*) ((char*) block + slab->size); + } + + block = slab->freeList.next; + +#if BLD_FEATURE_ALLOC_STATS + slab->freeCount += slab->allocIncrement; + if (slab->freeCount > slab->peakFree) { + slab->peakFree = slab->freeCount; + } +#endif + } + } + + /* + * We use block to point to the user data in the block. We only + * store the magic number (if debug). No other data is stored in the + * user block. + */ +#if BLD_DEBUG + mprAssert(block->magic == EJS_MAGIC_FREE); +#endif + + /* + * Remove from the free list + */ + slab->freeList.next = block->next; + + /* + * Zero block + */ + memset(block, 0, slab->size); + +#if BLD_DEBUG + block->magic = EJS_MAGIC; +#endif + +#if BLD_FEATURE_ALLOC_STATS + slab->totalAlloc++; + if (++slab->allocCount > slab->peakAllocated) { + slab->peakAllocated = slab->allocCount; + } + slab->freeCount--; +#endif + +#if BLD_DEBUG && (!BREW || BREW_SIMULATOR) + if ((uint) block == breakAddr) { + mprBreakpoint(MPR_LOC, "Watched Block"); + } +#endif + return block; +} + + +/******************************************************************************/ + +EjsObj *ejsAllocObj(EJS_LOC_DEC(ep, loc)) +{ + EjsObj *obj; + EjsSlab *slab; + + obj = (EjsObj*) ejsAlloc(EJS_LOC_PASS(ep, loc), EJS_SLAB_OBJ); + + /* + * Add to the allocated block list for the New generation. + */ + if (obj) { + slab = &ep->slabs[EJS_SLAB_OBJ]; + obj->gc.next = slab->allocList[EJS_GEN_NEW].next; + +#if BLD_FEATURE_ALLOC_LEAK_TRACK + obj->gc.allocatedBy = loc; +#endif + + obj->ejs = ep; + slab->allocList[EJS_GEN_NEW].next = (EjsGCLink*) obj; + + ep->gc.workDone++; + } + + return obj; +} + + +/******************************************************************************/ + +EjsProperty *ejsAllocProperty(EJS_LOC_DEC(ep, loc)) +{ + EjsProperty *prop; + + prop = (EjsProperty*) ejsAlloc(EJS_LOC_PASS(ep, loc), EJS_SLAB_PROPERTY); + mprAssert(prop); + + if (prop) { + prop->var.type = EJS_TYPE_NULL; + prop->var.isProperty = 1; +#if BLD_FEATURE_ALLOC_LEAK_TRACK + prop->var.gc.allocatedBy = loc; +#endif + } + return prop; +} + + +/******************************************************************************/ + +EjsVar *ejsAllocVar(EJS_LOC_DEC(ep, loc)) +{ + EjsVar *vp; + + vp = (EjsVar*) ejsAlloc(EJS_LOC_PASS(ep, loc), EJS_SLAB_VAR); + mprAssert(vp); + + if (vp) { +#if BLD_FEATURE_ALLOC_LEAK_TRACK + EjsSlab *slab; + vp->gc.allocatedBy = loc; + slab = &ep->slabs[EJS_SLAB_VAR]; + vp->gc.next = slab->allocList[EJS_GEN_NEW].next; + slab->allocList[EJS_GEN_NEW].next = (EjsGCLink*) vp; +#endif +#if BLD_DEBUG + vp->propertyName = 0; +#endif + } + return vp; +} + + +/******************************************************************************/ +/* + * Return the block back to the relevant slab + */ + +void ejsFree(Ejs *ep, void *ptr, int slabIndex) +{ + EjsSlab *slab; + EjsGCLink *block; + + mprAssert(ep); + mprAssert(ptr); + + if (slabIndex < 0 || slabIndex >= EJS_SLAB_MAX) { + mprAssert(slabIndex >= 0 && slabIndex < EJS_SLAB_MAX); + return; + } + slab = &ep->slabs[slabIndex]; + +#if BLD_FEATURE_ALLOC_LEAK_TRACK + if (slabIndex == EJS_SLAB_VAR) { + EjsVar *vp, *np, *prev; + + /* + * Remove the block rom the alloc list. WARNING: this is slow + * and should not be used in production code. + */ + vp = (EjsVar*) ptr; + prev = 0; + for (np = (EjsVar*) slab->allocList[0].next; np; + np = (EjsVar*) np->gc.next) { + if (vp == np) { + if (prev) { + prev->gc.next = (EjsGCLink*) np->gc.next; + } else { + slab->allocList[0].next = (EjsGCLink*) np->gc.next; + } + break; + } + prev = np; + } + if (np == 0) { + mprAssert(0); + } + } +#endif + + /* + * Insert into the free list. Only use the next ptr + */ + block = (EjsGCLink*) ptr; + +#if BLD_DEBUG +#if !BREW || BREW_SIMULATOR + if ((uint) block == breakAddr) { + mprBreakpoint(MPR_LOC, "Watched Block"); + } +#endif + mprAssert(block->magic == EJS_MAGIC); + block->magic = EJS_MAGIC_FREE; +#endif + + block->next = slab->freeList.next; + slab->freeList.next = block; + +#if BLD_FEATURE_ALLOC_STATS + slab->allocCount--; + if (++slab->freeCount >= slab->peakFree) { + slab->peakFree = slab->freeCount; + } + slab->totalReclaimed++; + if (slabIndex != 2) { + slabIndex = slabIndex; + } +#endif +} + +/******************************************************************************/ +/* + * Mark an object as being in-use. Traverse all properties for referenced + * objects and base classes. + */ + +static void markObjByVar(Ejs *ep, EjsVar *obj) +{ + EjsProperty *pp; + EjsVar *vp, *baseClass; + + mprAssert(ep); + mprAssert(obj); + + obj->objectState->gcMarked = 1; + +#if BLD_DEBUG + if (ep->gc.debugLevel >= 3) { + int indent = min(ep->gc.gcIndent * 2, 32); + mprLog(ep, 0, "%.*s %-24s %.*s 0x%08X", + indent, " ", + obj->propertyName, + 32 - indent, "................................ ", + (uint) obj->objectState); + ep->gc.gcIndent++; + } + ep->gc.objectsInUse++; +#endif + + /* + * Traverse all referenced objects + * OPT -- optimize by directly accessing the object links and not using + * ejsGetFirst/NextProperty. Then just examine objects + * OPT -- first property in global is global. Should optimize this. + */ + pp = ejsGetFirstProperty(obj, EJS_ENUM_ALL); + while (pp) { + vp = ejsGetVarPtr(pp); + if (vp->type == EJS_TYPE_OBJECT) { + if (!vp->objectState->gcMarked) { +#if FUTURE + /* + * OPT -- we can use the dirty bit on objects to avoid + * visiting permanent objects that are clean. If so, don't + * forget the else case below. + */ + obj = vp->objectState; + if ((!obj->alive && !obj->permanent) || obj->dirty) +#endif + markObjByVar(ep, vp); + } + + } else { +#if BLD_DEBUG + if (ep->gc.debugLevel >= 3) { + int indent = min(ep->gc.gcIndent * 2, 32); + mprLog(ep, 0, "%.*s %-24s %.*s %s", + indent, " ", + vp->propertyName, + 32 - indent, "................................ ", + ejsGetVarTypeAsString(vp)); + } + ep->gc.propertiesInUse++; +#endif + } + pp = ejsGetNextProperty(pp, EJS_ENUM_ALL); + } + + /* + * Traverse the base class + */ + baseClass = obj->objectState->baseClass; + if (baseClass) { + mprAssert(baseClass->type == EJS_TYPE_OBJECT); + mprAssert(baseClass->objectState); + if (baseClass->objectState) { + if (! baseClass->objectState->gcMarked) { + markObjByVar(ep, baseClass); + } + } + } +#if BLD_DEBUG + if (ep->gc.debugLevel >= 3) { + ep->gc.gcIndent--; + } +#endif +} + + +/******************************************************************************/ +/* + * Mark phase. Examine all variable frames and the return result. + */ + +static void mark(Ejs *ep) +{ + EjsVar *vp; + int i; + +#if BLD_DEBUG + if (ep->gc.debugLevel >= 3) { + mprLog(ep, 0, " "); + mprLog(ep, 0, "GC: Marked Blocks:"); + } +#endif + + if (ep->frames) { + for (i = 0; i < mprGetItemCount(ep->frames); i++) { + + vp = (EjsVar*) mprGetItem(ep->frames, i); + mprAssert(vp->type == EJS_TYPE_OBJECT); + + if (! vp->objectState->gcMarked) { + markObjByVar(ep, vp); + } + } + } + + vp = ep->result; + if (vp && vp->type == EJS_TYPE_OBJECT && ! vp->objectState->gcMarked) { + markObjByVar(ep, vp); + } + + vp = ep->currentObj; + if (vp && vp->type == EJS_TYPE_OBJECT && ! vp->objectState->gcMarked) { + markObjByVar(ep, vp); + } + + vp = ejsGetVarPtr(ep->currentProperty); + if (vp && vp->type == EJS_TYPE_OBJECT && ! vp->objectState->gcMarked) { + markObjByVar(ep, vp); + } + + /* + * OPT -- we could mark master as "mark permanent" somehow and + * then we would not need to walk the master objects. + */ + if (ep->slabAllocContext == ep->service->master) { + if (ep->service->master->global) { + markObjByVar(ep, ep->service->master->global); + } + } + +#if BLD_DEBUG + if (ep->gc.debugLevel >= 3) { + mprLog(ep, 0, " "); + } +#endif +} + + +/******************************************************************************/ +#if UNUSED + +static void resetMark(EjsVar *obj) +{ + EjsProperty *pp; + EjsVar *vp, *baseClass; + + obj->objectState->gcMarked = 0; + obj->objectState->visited = 1; + + pp = ejsGetFirstProperty(obj, EJS_ENUM_ALL); + while (pp) { + vp = ejsGetVarPtr(pp); + if (vp->type == EJS_TYPE_OBJECT && !vp->objectState->visited) { + resetMark(vp); + } + pp = ejsGetNextProperty(pp, EJS_ENUM_ALL); + } + + baseClass = obj->objectState->baseClass; + if (baseClass) { + mprAssert(baseClass->type == EJS_TYPE_OBJECT); + mprAssert(baseClass->objectState); + if (baseClass->objectState) { + if (! baseClass->objectState->visited) { + resetMark(baseClass); + } + } + } + obj->objectState->visited = 0; +} + +/******************************************************************************/ +/* + * Mark phase. Examine all variable frames and the return result. + */ + +static void resetAllMarks(Ejs *ep) +{ + EjsVar *vp; + int i; + + for (i = 0; i < mprGetItemCount(ep->frames); i++) { + vp = (EjsVar*) mprGetItem(ep->frames, i); + resetMark(vp); + } + + if (ep->result && ep->result->type == EJS_TYPE_OBJECT && + ! ep->result->objectState->gcMarked) { + resetMark(ep->result); + } +} + +#endif +/******************************************************************************/ +/* + * Sweep up the garbage + */ + +static void resetMarks(Ejs *ep, EjsSlab *slab) +{ + EjsVar *vp; + EjsObj *obj; + int gen, i; + + for (gen = EJS_GEN_NEW; gen < EJS_GEN_MAX; gen++) { + obj = (EjsObj*) slab->allocList[gen].next; + for (; obj; obj = (EjsObj*) obj->gc.next) { + obj->gcMarked = 0; + obj->visited = 0; + } + } + + if (ep->frames) { + for (i = 0; i < mprGetItemCount(ep->frames); i++) { + + vp = (EjsVar*) mprGetItem(ep->frames, i); + mprAssert(vp->type == EJS_TYPE_OBJECT); + + vp->objectState->gcMarked = 0; + vp->objectState->visited = 0; + } + } + + if (ep->result && ep->result->type == EJS_TYPE_OBJECT) { + ep->result->objectState->gcMarked = 0; + } +} + +/******************************************************************************/ +/* + * Mark all permanent and non-alive objects + */ + +static void markPerm(Ejs *ep, uint gen) +{ + EjsSlab *slab; + EjsObj *obj; + + slab = &ep->slabs[EJS_SLAB_OBJ]; + + for (obj = (EjsObj*) slab->allocList[gen].next; obj; ) { + + if (! obj->gcMarked) { + if (!obj->alive || obj->permanent) { + markObj(obj); + } + } + obj = (EjsObj*) obj->gc.next; + + } +} + +/******************************************************************************/ + +static void markObj(EjsObj *obj) +{ + EjsProperty *pp; + EjsPropLink *lp, *head; + EjsObj *op; + + mprAssert(obj); + + obj->gcMarked = 1; + + head = &obj->link; + for (lp = head->next; lp != head; lp = lp->next) { + + pp = ejsGetPropertyFromLink(lp); + + if (pp->var.type == EJS_TYPE_OBJECT) { + op = pp->var.objectState; + if (op != 0 && !op->gcMarked) { + markObj(op); + } + } + } +} + +/******************************************************************************/ +/* + * Sweep up the garbage. Return the number of objects freed. + */ + +static int sweep(Ejs *ep, uint gen) +{ + EjsSlab *slab; + EjsObj *obj, *next, *prev; + int count; + + slab = &ep->slabs[EJS_SLAB_OBJ]; + + /* + * Examine allocated objects in the specified generation (only). + * NOTE: we only sweep object allocated to this interpreter and so + * we do not sweep any permanent objects in the default interpreter. + */ + prev = 0; + count = 0; + for (obj = (EjsObj*) slab->allocList[gen].next; obj; obj = next) { + + next = (EjsObj*) obj->gc.next; + +#if BLD_DEBUG && (!BREW || BREW_SIMULATOR) + if ((uint) obj == breakAddr) { + mprBreakpoint(MPR_LOC, "Watched Block"); + } +#endif + + /* + * If object has not been marked inuse and is not a permanent + * object, then free it. + */ + if (! obj->gcMarked && obj->alive && !obj->permanent) { + +#if BLD_DEBUG + if (ep->gc.debugLevel >= 2) { + if (obj->objName) { + mprLog(ep, 0, "GC: destroy %-18s %10d, %8X", + obj->objName, (uint) obj, (uint) obj); + } else { + mprLog(ep, 0, "GC: destroy UNKNOWN %x", (uint) obj); + } + } +#endif + if (ejsDestroyObj(ep, obj) < 0) { + prev = obj; + obj->gcMarked = 0; + continue; + } + + if (prev) { + prev->gc.next = (EjsGCLink*) next; + } else { + slab->allocList[gen].next = (EjsGCLink*) next; + } + count++; + + } else { + prev = obj; + /* Reset for next time */ + obj->gcMarked = 0; + } + } + + if (gen == (EJS_GEN_OLD - 1)) { + slab->lastRecentBlock = prev; + } +#if BLD_FEATURE_ALLOC_STATS + slab->totalSweeps++; +#endif +#if BLD_DEBUG + if (ep->gc.debugLevel > 0) { + mprLog(ep, 0, "GC: Sweep freed %d objects", count); + } +#endif + return count; +} + +/******************************************************************************/ +/* + * Sweep all variables + */ + +void ejsSweepAll(Ejs *ep) +{ + EjsSlab *slab; + EjsObj *obj, *next, *prev; + int gen; + + slab = &ep->slabs[EJS_SLAB_OBJ]; + + for (gen = EJS_GEN_NEW; gen < EJS_GEN_MAX; gen++) { + prev = 0; + for (obj = (EjsObj*) slab->allocList[gen].next; obj; obj = next) { + next = (EjsObj*) obj->gc.next; + ejsDestroyObj(ep, obj); + } + break; + } +} + +/******************************************************************************/ + +bool ejsObjIsCollectable(EjsVar *vp) +{ + if (vp == 0 || !ejsVarIsObject(vp)) { + return 0; + } + return (vp->objectState->alive && !vp->objectState->permanent); +} + +/******************************************************************************/ +#if FUTURE + +static void ageGenerations(Ejs *ep) +{ + EjsSlab *slab; + EjsGCLink *oldList; + int gen; + + slab = &ep->slabs[EJS_SLAB_OBJ]; + + /* + * Age all blocks. First append all (old - 1) blocks onto the old + * alloc list + */ + oldList = &slab->allocList[EJS_GEN_OLD]; + + if (slab->lastRecentBlock) { + slab->lastRecentBlock->gc.next = oldList->next; + oldList->next = (EjsGCLink*) slab->lastRecentBlock; + } + + /* + * Now simply copy all allocation lists up one generation + */ + for (gen = EJS_GEN_OLD - 1; gen > 0; gen--) { + slab->allocList[gen] = slab->allocList[gen - 1]; + } + slab->allocList[0].next = 0; +} + +#endif +/******************************************************************************/ +/* + * Collect the garbage. This is a mark and sweep over all possible objects. + * If an object is not referenced, it and all contained properties will be + * freed. If a slabIndex is provided, the collection halts when a block is + * available for allocation on that slab. + * + * Return 0 if memory is now available after collecting garbage. Otherwise, + * return MPR_ERR_MEMORY. + */ + +int ejsCollectGarbage(Ejs *ep, int slabIndex) +{ + EjsGeneration gen; + + if (ep->flags & EJS_FLAGS_DONT_GC) { + return -1; + } + + /* + * Prevent destructors invoking the garbage collector + */ + if (ep->gc.collecting) { + return 0; + } + ep->gc.collecting = 1; + + resetMarks(ep, &ep->slabs[EJS_SLAB_OBJ]); + + /* + * Examine each generation of objects starting with the most recent + * generation. Stop scanning when we have a free block to use. + */ + for (gen = EJS_GEN_NEW; gen < EJS_GEN_MAX; gen++) { + + if (slabIndex >= 0 && ep->slabs[slabIndex].freeList.next) { + break; + } + + /* + * FUTURE OPT. Should mark objects in new generation and those + * with a dirty bit set in older generations. Don't need to mark + * entire heap. But how to keep list of dirty objects. + */ + mark(ep); + markPerm(ep, gen); + sweep(ep, gen); + + /* FUTURE - not using generations yet */ + break; + } + + /* + * FUTURE -- not using generations yet. + * + * ageGenerations(ep); + */ + + ep->gc.workDone = 0; + ep->gc.collecting = 0; + + return (gen < EJS_GEN_MAX) ? 0 : MPR_ERR_MEMORY; +} + + +/******************************************************************************/ +/* + * Should be called when the app has been idle for a little while and when it + * is likely to be idle a bit longer. Call ejsIsTimeForGC to see if this is + * true. Return the count of objects collected . + */ + +int ejsIncrementalCollectGarbage(Ejs *ep) +{ + int count; + + if (ep->gc.collecting) { + return 0; + } + + ep->gc.collecting = 1; + + resetMarks(ep, &ep->slabs[EJS_SLAB_OBJ]); + mark(ep); + + /* Not generational yet */ + count = sweep(ep, EJS_GEN_NEW); + + ep->gc.collecting = 0; + ep->gc.workDone = 0; + + return count; +} + +/******************************************************************************/ +#if BLD_DEBUG + +void ejsDumpObjects(Ejs *ep) +{ + int oldDebugLevel; + + mprLog(ep, 0, "Dump of objects in use\n"); + + oldDebugLevel = ep->gc.debugLevel; + + ep->gc.debugLevel = 3; + ep->gc.objectsInUse = 0; + ep->gc.propertiesInUse = 0; + ep->gc.collecting = 1; + + resetMarks(ep, &ep->slabs[EJS_SLAB_OBJ]); + mark(ep); + + ep->gc.collecting = 0; + ep->gc.debugLevel = oldDebugLevel; + + mprLog(ep, 0, "%d objects and %d properties in use", + ep->gc.objectsInUse, ep->gc.propertiesInUse); + mprLog(ep, 0, "%d object bytes, %d property bytes and %d total", + (int) (ep->gc.objectsInUse * sizeof(EjsObj)), + (int) (ep->gc.propertiesInUse * sizeof(EjsProperty)), + (int) ((ep->gc.objectsInUse * sizeof(EjsObj) + + ep->gc.propertiesInUse * sizeof(EjsProperty)))); +} + +#endif +/******************************************************************************/ +/* + * Return true if there is time to do a garbage collection and if we will + * benefit from it. + */ + +int ejsIsTimeForGC(Ejs *ep, int timeTillNextEvent) +{ + EjsGC *gc; + + if (timeTillNextEvent < EJS_MIN_TIME_FOR_GC) { + /* + * Not enough time to complete a collection + */ + return 0; + } + + gc = &ep->gc; + + /* + * Return if we haven't done enough work to warrant a collection + * Trigger a little short of the work quota to try to run GC before + * a demand allocation requires it. + */ + if (!gc->enable || !gc->enableIdleCollect || + (gc->workDone < (gc->workQuota - EJS_GC_MIN_WORK_QUOTA))) { + return 0; + } + +#if UNUSED + mprLog(ep, 0, "Time for GC. Work done %d, time till next event %d", + gc->workDone, timeTillNextEvent); +#endif + return 1; +} + +/******************************************************************************/ +/* + * Return the amount of memory in use by EJS + */ + +uint ejsGetUsedMemory(Ejs *ep) +{ +#if BLD_FEATURE_ALLOC_STATS + EjsSlab *slab; + int i, totalMemory, slabMemory; + + totalMemory = 0; + for (i = 0; i < EJS_SLAB_MAX; i++) { + slab = &ep->slabs[i]; + slabMemory = slab->allocCount * slab->size; + totalMemory += slabMemory; + } + return totalMemory; +#else + return 0; +#endif +} + +/******************************************************************************/ +/* + * Return the amount of memory allocated by EJS + */ + +uint ejsGetAllocatedMemory(Ejs *ep) +{ +#if BLD_FEATURE_ALLOC_STATS + EjsSlab *slab; + int i, totalMemory, slabMemory; + + totalMemory = 0; + for (i = 0; i < EJS_SLAB_MAX; i++) { + slab = &ep->slabs[i]; + slabMemory = (slab->allocCount + slab->freeCount) * slab->size; + totalMemory += slabMemory; + } + return totalMemory; +#else + return 0; +#endif +} + +/******************************************************************************/ +/* + * On a memory allocation failure, go into graceful degrade mode. Set all + * slab allocation chunk increments to 1 so we can create an exception block + * to throw. + */ + +static void ejsGracefulDegrade(Ejs *ep) +{ + EjsSlab *slab; + int i; + + mprLog(ep, 1, "WARNING: Memory almost depleted. In graceful degrade mode"); + for (i = 0; i < EJS_SLAB_MAX; i++) { + slab = &ep->slabs[i]; + slab->allocIncrement = 8; + } + ep->gc.degraded = 1; +} + +/******************************************************************************/ + +int ejsSetGCDebugLevel(Ejs *ep, int debugLevel) +{ + int old; + + old = ep->gc.debugLevel; + ep->gc.debugLevel = debugLevel; + return old; +} + +/******************************************************************************/ + +int ejsSetGCMaxMemory(Ejs *ep, uint maxMemory) +{ + int old; + + old = ep->gc.maxMemory; + ep->gc.maxMemory = maxMemory; + + return old; +} + +/******************************************************************************/ + +bool ejsBlockInUseInt(EjsVar *vp) +{ + if (vp) { +#if BLD_DEBUG + if (vp->gc.magic != EJS_MAGIC) { + return 0; + } + if (vp->type == EJS_TYPE_OBJECT && vp->objectState && + vp->objectState->gc.magic != EJS_MAGIC) { + return 0; + } +#endif + return 1; + } + return 1; +} + +/******************************************************************************/ +#else +void ejsGarbageDummy() {} + +#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 + */ diff --git a/source4/lib/appweb/ejs-2.0/ejs/ejsLex.c b/source4/lib/appweb/ejs-2.0/ejs/ejsLex.c new file mode 100644 index 0000000000..fbfee6e4d5 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/ejsLex.c @@ -0,0 +1,1033 @@ +/* + * @file ejsLex.c + * @brief EJS Lexical Analyser + * @overview EJS lexical analyser. This implementes a lexical analyser + * for a subset of the JavaScript language. + */ +/********************************* Copyright **********************************/ +/* + * @copy default.g + * + * Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved. + * Portions Copyright (c) GoAhead Software, 1995-2000. 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 **************************/ + +static int getLexicalToken(Ejs *ep, int state); +static int tokenAddChar(Ejs *ep, int c); +static int inputGetc(Ejs *ep); +static void inputPutback(Ejs *ep, int c); +static int charConvert(Ejs *ep, int base, int maxDig); +static void parseNumber(Ejs *ep, EjsType type); + +/************************************* Code ***********************************/ +/* + * Open a new input script + */ + +int ejsLexOpenScript(Ejs *ep, const char *script) +{ + EjsInput *ip; + + mprAssert(ep); + mprAssert(script); + + if ((ip = mprAllocTypeZeroed(ep, EjsInput)) == NULL) { + return MPR_ERR_MEMORY; + } + ip->next = ep->input; + ep->input = ip; + ip->procName = ep->proc ? ep->proc->procName : NULL; + ip->fileName = ep->fileName ? ep->fileName : NULL; + +/* + * Create the parse token buffer and script buffer + */ + ip->tokServp = ip->tokbuf; + ip->tokEndp = ip->tokbuf; + + ip->script = script; + ip->scriptSize = strlen(script); + ip->scriptServp = (char*) ip->script; + + ip->lineNumber = 1; + ip->lineColumn = 0; + + ip->putBackIndex = -1; + + return 0; +} + +/******************************************************************************/ +/* + * Close the input script + */ + +void ejsLexCloseScript(Ejs *ep) +{ + EjsInput *ip; + + mprAssert(ep); + + ip = ep->input; + mprAssert(ip); + ep->input = ip->next; + + mprFree(ip); +} + +/******************************************************************************/ +/* + * Initialize an input state structure + */ + +int ejsInitInputState(EjsInput *ip) +{ + mprAssert(ip); + + memset(ip, 0, sizeof(*ip)); + ip->putBackIndex = -1; + + return 0; +} +/******************************************************************************/ +/* + * Save the input state + */ + +void ejsLexSaveInputState(Ejs *ep, EjsInput *state) +{ + EjsInput *ip; + int i; + + mprAssert(ep); + + ip = ep->input; + mprAssert(ip); + + *state = *ip; + + for (i = 0; i <= ip->putBackIndex; i++) { + mprStrcpy(state->putBack[i].tokbuf, EJS_MAX_TOKEN, + ip->putBack[i].tokbuf); + state->putBack[i].tid = ip->putBack[i].tid; + } + + mprStrcpy(state->line, sizeof(state->line), ip->line); + + state->lineColumn = ip->lineColumn; + state->lineNumber = ip->lineNumber; +} + +/******************************************************************************/ +/* + * Restore the input state + */ + +void ejsLexRestoreInputState(Ejs *ep, EjsInput *state) +{ + EjsInput *ip; + EjsToken *tp; + int i; + + mprAssert(ep); + mprAssert(state); + + ip = ep->input; + mprAssert(ip); + + mprStrcpy(ip->tokbuf, sizeof(ip->tokbuf), state->tokbuf); + ip->tokServp = state->tokServp; + ip->tokEndp = state->tokEndp; + + ip->script = state->script; + ip->scriptServp = state->scriptServp; + ip->scriptSize = state->scriptSize; + + ip->putBackIndex = state->putBackIndex; + for (i = 0; i <= ip->putBackIndex; i++) { + tp = &ip->putBack[i]; + tp->tid = state->putBack[i].tid; + mprStrcpy(tp->tokbuf, sizeof(tp->tokbuf), state->putBack[i].tokbuf); + } + + mprStrcpy(ip->line, sizeof(ip->line), state->line); + + ip->lineColumn = state->lineColumn; + ip->lineNumber = state->lineNumber; +} + +/******************************************************************************/ +/* + * Free a saved input state + */ + +void ejsLexFreeInputState(Ejs *ep, EjsInput *state) +{ + mprAssert(ep); + mprAssert(state); + + state->putBackIndex = -1; + state->lineColumn = 0; +} + +/******************************************************************************/ +/* + * Get the next EJS token + */ + +int ejsLexGetToken(Ejs *ep, int state) +{ + mprAssert(ep); + + ep->tid = getLexicalToken(ep, state); + return ep->tid; +} + +/******************************************************************************/ + +/* + * Check for reserved words "if", "else", "var", "for", "delete", "function", + * "class", "extends", "public", "private", "protected", "try", "catch", + * "finally", "throw", "return", "get", "set", "this", "module", "each" + * + * The "new" and "in" reserved words are handled below. The "true", "false", + * "null" "typeof" and "undefined" reserved words are handled as global + * objects. + * + * Other reserved words not supported: + * "break", "case", "continue", "default", "do", + * "instanceof", "switch", "while", "with" + * + * ECMA extensions reserved words (not supported): + * "abstract", "boolean", "byte", "char", "const", + * "debugger", "double", "enum", "export", + * "final", "float", "goto", "implements", "import", "int", + * "interface", "long", "native", "package", + * "short", "static", "super", "synchronized", "transient", "volatile" + * + * FUTURE -- use a hash lookup + */ + +static int checkReservedWord(Ejs *ep, int state, int c, int tid) +{ + /* FUTURE -- probably should return for all tokens != EJS_TOK_ID */ + /* FUTURE -- Should have a hash for this. MUCH faster. */ + + if (!isalpha(ep->token[0]) || tid == EJS_TOK_LITERAL) { + return tid; + } + if (state == EJS_STATE_STMT) { + /* FUTURE OPT -- convert to hash lookup */ + if (strcmp(ep->token, "if") == 0) { + inputPutback(ep, c); + return EJS_TOK_IF; + } else if (strcmp(ep->token, "else") == 0) { + inputPutback(ep, c); + return EJS_TOK_ELSE; + } else if (strcmp(ep->token, "var") == 0) { + inputPutback(ep, c); + return EJS_TOK_VAR; + } else if (strcmp(ep->token, "new") == 0) { + inputPutback(ep, c); + return EJS_TOK_NEW; + } else if (strcmp(ep->token, "for") == 0) { + inputPutback(ep, c); + return EJS_TOK_FOR; + } else if (strcmp(ep->token, "delete") == 0) { + inputPutback(ep, c); + return EJS_TOK_DELETE; + } else if (strcmp(ep->token, "function") == 0) { + inputPutback(ep, c); + return EJS_TOK_FUNCTION; + } else if (strcmp(ep->token, "class") == 0) { + inputPutback(ep, c); + return EJS_TOK_CLASS; + } else if (strcmp(ep->token, "module") == 0) { + inputPutback(ep, c); + return EJS_TOK_MODULE; + } else if (strcmp(ep->token, "extends") == 0) { + inputPutback(ep, c); + return EJS_TOK_EXTENDS; + } else if (strcmp(ep->token, "try") == 0) { + inputPutback(ep, c); + return EJS_TOK_TRY; + } else if (strcmp(ep->token, "catch") == 0) { + inputPutback(ep, c); + return EJS_TOK_CATCH; + } else if (strcmp(ep->token, "finally") == 0) { + inputPutback(ep, c); + return EJS_TOK_FINALLY; + } else if (strcmp(ep->token, "throw") == 0) { + inputPutback(ep, c); + return EJS_TOK_THROW; + } else if (strcmp(ep->token, "public") == 0) { + inputPutback(ep, c); + return EJS_TOK_PUBLIC; + } else if (strcmp(ep->token, "protected") == 0) { + inputPutback(ep, c); + return EJS_TOK_PROTECTED; + } else if (strcmp(ep->token, "private") == 0) { + inputPutback(ep, c); + return EJS_TOK_PRIVATE; + } else if (strcmp(ep->token, "get") == 0) { + inputPutback(ep, c); + return EJS_TOK_GET; + } else if (strcmp(ep->token, "set") == 0) { + inputPutback(ep, c); + return EJS_TOK_SET; + } else if (strcmp(ep->token, "extends") == 0) { + inputPutback(ep, c); + return EJS_TOK_EXTENDS; + } else if (strcmp(ep->token, "try") == 0) { + inputPutback(ep, c); + return EJS_TOK_TRY; + } else if (strcmp(ep->token, "catch") == 0) { + inputPutback(ep, c); + return EJS_TOK_CATCH; + } else if (strcmp(ep->token, "finally") == 0) { + inputPutback(ep, c); + return EJS_TOK_FINALLY; + } else if (strcmp(ep->token, "throw") == 0) { + inputPutback(ep, c); + return EJS_TOK_THROW; + } else if (strcmp(ep->token, "public") == 0) { + inputPutback(ep, c); + return EJS_TOK_PUBLIC; + } else if (strcmp(ep->token, "protected") == 0) { + inputPutback(ep, c); + return EJS_TOK_PROTECTED; + } else if (strcmp(ep->token, "private") == 0) { + inputPutback(ep, c); + return EJS_TOK_PRIVATE; + } else if (strcmp(ep->token, "get") == 0) { + inputPutback(ep, c); + return EJS_TOK_GET; + } else if (strcmp(ep->token, "set") == 0) { + inputPutback(ep, c); + return EJS_TOK_SET; + } else if (strcmp(ep->token, "each") == 0) { + inputPutback(ep, c); + return EJS_TOK_EACH; + } else if (strcmp(ep->token, "return") == 0) { + if ((c == ';') || (c == '(')) { + inputPutback(ep, c); + } + return EJS_TOK_RETURN; + } + + } else if (state == EJS_STATE_EXPR) { + if (strcmp(ep->token, "new") == 0) { + inputPutback(ep, c); + return EJS_TOK_NEW; + } else if (strcmp(ep->token, "in") == 0) { + inputPutback(ep, c); + return EJS_TOK_IN; + } else if (strcmp(ep->token, "function") == 0) { + inputPutback(ep, c); + return EJS_TOK_FUNCTION; + } + + } else if (state == EJS_STATE_DEC) { + if (strcmp(ep->token, "extends") == 0) { + inputPutback(ep, c); + return EJS_TOK_EXTENDS; + } + } + return tid; +} + +/******************************************************************************/ +/* + * Get the next EJS token + */ + +static int getLexicalToken(Ejs *ep, int state) +{ + EjsType type; + EjsInput *ip; + int done, tid, c, quote, style, idx, isHex; + + mprAssert(ep); + ip = ep->input; + mprAssert(ip); + + ep->tid = -1; + tid = -1; + type = BLD_FEATURE_NUM_TYPE_ID; + isHex = 0; + + /* + * Use a putback tokens first. Don't free strings as caller needs access. + */ + if (ip->putBackIndex >= 0) { + idx = ip->putBackIndex; + tid = ip->putBack[idx].tid; + ep->token = (char*) ip->putBack[idx].tokbuf; + tid = checkReservedWord(ep, state, 0, tid); + ip->putBackIndex--; + return tid; + } + ep->token = ip->tokServp = ip->tokEndp = ip->tokbuf; + *ip->tokServp = '\0'; + + if ((c = inputGetc(ep)) < 0) { + return EJS_TOK_EOF; + } + + /* + * Main lexical analyser + */ + for (done = 0; !done; ) { + switch (c) { + case -1: + return EJS_TOK_EOF; + + case ' ': + case '\t': + case '\r': + do { + if ((c = inputGetc(ep)) < 0) + break; + } while (c == ' ' || c == '\t' || c == '\r'); + break; + + case '\n': + return EJS_TOK_NEWLINE; + + case '(': + tokenAddChar(ep, c); + return EJS_TOK_LPAREN; + + case ')': + tokenAddChar(ep, c); + return EJS_TOK_RPAREN; + + case '[': + tokenAddChar(ep, c); + return EJS_TOK_LBRACKET; + + case ']': + tokenAddChar(ep, c); + return EJS_TOK_RBRACKET; + + case '.': + tokenAddChar(ep, c); + return EJS_TOK_PERIOD; + + case '{': + tokenAddChar(ep, c); + return EJS_TOK_LBRACE; + + case '}': + tokenAddChar(ep, c); + return EJS_TOK_RBRACE; + + case '+': + if ((c = inputGetc(ep)) < 0) { + ejsSyntaxError(ep, 0); + return EJS_TOK_ERR; + } + if (c != '+' ) { + inputPutback(ep, c); + tokenAddChar(ep, EJS_EXPR_PLUS); + return EJS_TOK_EXPR; + } + tokenAddChar(ep, EJS_EXPR_INC); + return EJS_TOK_INC_DEC; + + case '-': + if ((c = inputGetc(ep)) < 0) { + ejsSyntaxError(ep, 0); + return EJS_TOK_ERR; + } + if (c != '-' ) { + inputPutback(ep, c); + tokenAddChar(ep, EJS_EXPR_MINUS); + return EJS_TOK_EXPR; + } + tokenAddChar(ep, EJS_EXPR_DEC); + return EJS_TOK_INC_DEC; + + case '*': + tokenAddChar(ep, EJS_EXPR_MUL); + return EJS_TOK_EXPR; + + case '%': + tokenAddChar(ep, EJS_EXPR_MOD); + return EJS_TOK_EXPR; + + case '/': + /* + * Handle the division operator and comments + */ + if ((c = inputGetc(ep)) < 0) { + ejsSyntaxError(ep, 0); + return EJS_TOK_ERR; + } + if (c != '*' && c != '/') { + inputPutback(ep, c); + tokenAddChar(ep, EJS_EXPR_DIV); + return EJS_TOK_EXPR; + } + style = c; + /* + * Eat comments. Both C and C++ comment styles are supported. + */ + while (1) { + if ((c = inputGetc(ep)) < 0) { + if (style == '/') { + return EJS_TOK_EOF; + } + ejsSyntaxError(ep, 0); + return EJS_TOK_ERR; + } + if (c == '\n' && style == '/') { + break; + } else if (c == '*') { + c = inputGetc(ep); + if (style == '/') { + if (c == '\n') { + break; + } + } else { + if (c == '/') { + break; + } + } + } + } + /* + * Continue looking for a token, so get the next character + */ + if ((c = inputGetc(ep)) < 0) { + return EJS_TOK_EOF; + } + break; + + case '<': /* < and <= */ + if ((c = inputGetc(ep)) < 0) { + ejsSyntaxError(ep, 0); + return EJS_TOK_ERR; + } + if (c == '<') { + tokenAddChar(ep, EJS_EXPR_LSHIFT); + return EJS_TOK_EXPR; + } else if (c == '=') { + tokenAddChar(ep, EJS_EXPR_LESSEQ); + return EJS_TOK_EXPR; + } + tokenAddChar(ep, EJS_EXPR_LESS); + inputPutback(ep, c); + return EJS_TOK_EXPR; + + case '>': /* > and >= */ + if ((c = inputGetc(ep)) < 0) { + ejsSyntaxError(ep, 0); + return EJS_TOK_ERR; + } + if (c == '>') { + tokenAddChar(ep, EJS_EXPR_RSHIFT); + return EJS_TOK_EXPR; + } else if (c == '=') { + tokenAddChar(ep, EJS_EXPR_GREATEREQ); + return EJS_TOK_EXPR; + } + tokenAddChar(ep, EJS_EXPR_GREATER); + inputPutback(ep, c); + return EJS_TOK_EXPR; + + case '=': /* "==" */ + if ((c = inputGetc(ep)) < 0) { + ejsSyntaxError(ep, 0); + return EJS_TOK_ERR; + } + if (c == '=') { + tokenAddChar(ep, EJS_EXPR_EQ); + return EJS_TOK_EXPR; + } + inputPutback(ep, c); + return EJS_TOK_ASSIGNMENT; + + case '!': /* "!=" or "!"*/ + if ((c = inputGetc(ep)) < 0) { + ejsSyntaxError(ep, 0); + return EJS_TOK_ERR; + } + if (c == '=') { + tokenAddChar(ep, EJS_EXPR_NOTEQ); + return EJS_TOK_EXPR; + } + inputPutback(ep, c); + tokenAddChar(ep, EJS_EXPR_BOOL_COMP); + return EJS_TOK_EXPR; + + case ';': + tokenAddChar(ep, c); + return EJS_TOK_SEMI; + + case ',': + tokenAddChar(ep, c); + return EJS_TOK_COMMA; + + case ':': + tokenAddChar(ep, c); + return EJS_TOK_COLON; + + case '|': /* "||" */ + if ((c = inputGetc(ep)) < 0 || c != '|') { + ejsSyntaxError(ep, 0); + return EJS_TOK_ERR; + } + tokenAddChar(ep, EJS_COND_OR); + return EJS_TOK_LOGICAL; + + case '&': /* "&&" */ + if ((c = inputGetc(ep)) < 0 || c != '&') { + ejsSyntaxError(ep, 0); + return EJS_TOK_ERR; + } + tokenAddChar(ep, EJS_COND_AND); + return EJS_TOK_LOGICAL; + + case '\"': /* String quote */ + case '\'': + quote = c; + if ((c = inputGetc(ep)) < 0) { + ejsSyntaxError(ep, 0); + return EJS_TOK_ERR; + } + + while (c != quote) { + /* + * Check for escape sequence characters + */ + if (c == '\\') { + c = inputGetc(ep); + + if (isdigit(c)) { + /* + * Octal support, \101 maps to 65 = 'A'. Put first + * char back so converter will work properly. + */ + inputPutback(ep, c); + c = charConvert(ep, 8, 3); + + } else { + switch (c) { + case 'n': + c = '\n'; break; + case 'b': + c = '\b'; break; + case 'f': + c = '\f'; break; + case 'r': + c = '\r'; break; + case 't': + c = '\t'; break; + case 'x': + /* + * Hex support, \x41 maps to 65 = 'A' + */ + c = charConvert(ep, 16, 2); + break; + case 'u': + /* + * Unicode support, \x0401 maps to 65 = 'A' + */ + c = charConvert(ep, 16, 2); + c = c*16 + charConvert(ep, 16, 2); + + break; + case '\'': + case '\"': + case '\\': + break; + default: + if (tokenAddChar(ep, '\\') < 0) { + return EJS_TOK_ERR; + } + } + } + if (tokenAddChar(ep, c) < 0) { + return EJS_TOK_ERR; + } + } else { + if (tokenAddChar(ep, c) < 0) { + return EJS_TOK_ERR; + } + } + if ((c = inputGetc(ep)) < 0) { + ejsSyntaxError(ep, "Unmatched Quote"); + return EJS_TOK_ERR; + } + } + return EJS_TOK_LITERAL; + + case '0': + if (tokenAddChar(ep, c) < 0) { + return EJS_TOK_ERR; + } + if ((c = inputGetc(ep)) < 0) { + break; + } + if (tolower(c) == 'x') { + if (tokenAddChar(ep, c) < 0) { + return EJS_TOK_ERR; + } + if ((c = inputGetc(ep)) < 0) { + break; + } + isHex = 1; + if (! isxdigit(c)) { + parseNumber(ep, type); + inputPutback(ep, c); + return EJS_TOK_NUMBER; + } + } else if (! isdigit(c)) { +#if BLD_FEATURE_FLOATING_POINT + if (c == '.' || tolower(c) == 'e' || c == '+' || c == '-') { + /* Fall through */ + type = EJS_TYPE_FLOAT; + } else +#endif + { + parseNumber(ep, type); + inputPutback(ep, c); + return EJS_TOK_NUMBER; + } + } + /* Fall through to get more digits */ + + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + if (isHex) { + do { + if (tokenAddChar(ep, c) < 0) { + return EJS_TOK_ERR; + } + if ((c = inputGetc(ep)) < 0) { + break; + } + } while (isxdigit(c)); + + } else { +#if BLD_FEATURE_FLOATING_POINT + do { + if (tokenAddChar(ep, c) < 0) { + return EJS_TOK_ERR; + } + if ((c = inputGetc(ep)) < 0) { + break; + } + c = tolower(c); + if (c == '.' || c == 'e' || c == 'f') { + type = EJS_TYPE_FLOAT; + } + } while (isdigit(c) || c == '.' || c == 'e' || + c == 'f' || + ((type == EJS_TYPE_FLOAT) && (c == '+' || c == '-'))); +#else + do { + if (tokenAddChar(ep, c) < 0) { + return EJS_TOK_ERR; + } + if ((c = inputGetc(ep)) < 0) { + break; + } + } while (isdigit(c)); +#endif + } + + parseNumber(ep, type); + inputPutback(ep, c); + return EJS_TOK_NUMBER; + + default: + /* + * Identifiers or a function names + */ + while (1) { + if (c == '\\') { + if ((c = inputGetc(ep)) < 0) { + break; + } + if (c == '\n' || c == '\r') { + break; + } + } else if (tokenAddChar(ep, c) < 0) { + break; + } + if ((c = inputGetc(ep)) < 0) { + break; + } + if (!isalnum(c) && c != '$' && c != '_' && + c != '\\' && c != '@') { + break; + } + } + if (*ep->token == '\0') { + c = inputGetc(ep); + break; + } + + if (! isalpha((int) *ep->token) && *ep->token != '$' && + *ep->token != '_' && *ep->token != '@') { + ejsError(ep, EJS_SYNTAX_ERROR, "Invalid identifier %s", + ep->token); + return EJS_TOK_ERR; + } + + tid = checkReservedWord(ep, state, c, EJS_TOK_ID); + if (tid != EJS_TOK_ID) { + return tid; + } + + /* + * Skip white space after token to find out whether this is + * a function or not. + */ + while (c == ' ' || c == '\t' || c == '\r' || c == '\n') { + if ((c = inputGetc(ep)) < 0) + break; + } + + tid = EJS_TOK_ID; + if ((strlen(ep->token) + 1) >= EJS_MAX_ID) { + ejsError(ep, EJS_SYNTAX_ERROR, + "Identifier too big. Max is %d letters.", EJS_MAX_ID); + return EJS_TOK_ERR; + } + done++; + } + } + + /* + * Putback the last extra character for next time + */ + inputPutback(ep, c); + return tid; +} + +/******************************************************************************/ + +static void parseNumber(Ejs *ep, EjsType type) +{ + switch (type) { + case EJS_TYPE_INT: + ep->tokenNumber.integer = ejsParseInteger(ep->token); + ep->tokenNumber.type = type; + break; + +#if BLD_FEATURE_FLOATING_POINT + case EJS_TYPE_FLOAT: + ep->tokenNumber.floating = atof(ep->token); + ep->tokenNumber.type = type; + break; +#endif + +#if BLD_FEATURE_INT64 + case EJS_TYPE_INT64: + ep->tokenNumber.integer64 = ejsParseInteger64(ep->token); + ep->tokenNumber.type = type; + break; +#endif + } +} + +/******************************************************************************/ +/* + * Convert a hex or octal character back to binary, return original char if + * not a hex digit + */ + +static int charConvert(Ejs *ep, int base, int maxDig) +{ + int i, c, lval, convChar; + + lval = 0; + for (i = 0; i < maxDig; i++) { + if ((c = inputGetc(ep)) < 0) { + break; + } + /* + * Initialize to out of range value + */ + convChar = base; + if (isdigit(c)) { + convChar = c - '0'; + } else if (c >= 'a' && c <= 'f') { + convChar = c - 'a' + 10; + } else if (c >= 'A' && c <= 'F') { + convChar = c - 'A' + 10; + } + /* + * If unexpected character then return it to buffer. + */ + if (convChar >= base) { + inputPutback(ep, c); + break; + } + lval = (lval * base) + convChar; + } + return lval; +} + +/******************************************************************************/ +/* + * Putback the last token read. Accept at most one push back token. + */ + +void ejsLexPutbackToken(Ejs *ep, int tid, char *string) +{ + EjsInput *ip; + EjsToken *tp; + int idx; + + mprAssert(ep); + ip = ep->input; + mprAssert(ip); + + ip->putBackIndex += 1; + + mprAssert(ip->putBackIndex < EJS_TOKEN_STACK); + idx = ip->putBackIndex; + + tp = &ip->putBack[idx]; + tp->tid = tid; + + mprStrcpy(tp->tokbuf, sizeof(tp->tokbuf), string); +} + +/******************************************************************************/ +/* + * Add a character to the token buffer + */ + +static int tokenAddChar(Ejs *ep, int c) +{ + EjsInput *ip; + + mprAssert(ep); + ip = ep->input; + mprAssert(ip); + + if (ip->tokEndp >= &ip->tokbuf[sizeof(ip->tokbuf) - 1]) { + ejsSyntaxError(ep, "Token too big"); + return -1; + } + *ip->tokEndp++ = c; + *ip->tokEndp = '\0'; + + return 0; +} + +/******************************************************************************/ +/* + * Get another input character + */ + +static int inputGetc(Ejs *ep) +{ + EjsInput *ip; + int c; + + mprAssert(ep); + ip = ep->input; + + if (ip->scriptSize <= 0) { + return -1; + } + + c = (uchar) (*ip->scriptServp++); + ip->scriptSize--; + + /* + * For debugging, accumulate the line number and the currenly parsed line + */ + if (c == '\n') { +#if 0 && BLD_DEBUG + if (ip->lineColumn > 0) { + printf("PARSED: %s\n", ip->line); + } +#endif + ip->lineNumber++; + ip->lineColumn = 0; + } else if ((ip->lineColumn + 2) < sizeof(ip->line)) { + ip->line[ip->lineColumn++] = c; + ip->line[ip->lineColumn] = '\0'; + } + return c; +} + +/******************************************************************************/ +/* + * Putback a character onto the input queue + */ + +static void inputPutback(Ejs *ep, int c) +{ + EjsInput *ip; + + mprAssert(ep); + + if (c > 0) { + ip = ep->input; + *--ip->scriptServp = c; + ip->scriptSize++; + if (--(ip->lineColumn) < 0) { + ip->lineColumn = 0; + } + mprAssert(ip->line); + mprAssert(ip->lineColumn >= 0); + mprAssert(ip->lineColumn < sizeof(ip->line)); + ip->line[ip->lineColumn] = '\0'; + } +} + +/******************************************************************************/ + +#else +void ejsLexDummy() {} + +/******************************************************************************/ +#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 + */ diff --git a/source4/lib/appweb/ejs-2.0/ejs/ejsParser.c b/source4/lib/appweb/ejs-2.0/ejs/ejsParser.c new file mode 100644 index 0000000000..9fce6d27ee --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/ejsParser.c @@ -0,0 +1,4514 @@ +/* + * @file ejsParser.c + * @brief EJS Parser and Execution + */ +/********************************* Copyright **********************************/ +/* + * @copy default.g + * + * Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved. + * Portions Copyright (c) GoAhead Software, 1995-2000. 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 **************************/ + +static int createClass(Ejs *ep, EjsVar *parentClass, + const char *className, EjsVar *baseClass); +static int createProperty(Ejs *ep, EjsVar **obj, const char *id, + int state); +static int evalCond(Ejs *ep, EjsVar *lhs, int rel, EjsVar *rhs); +static int evalExpr(Ejs *ep, EjsVar *lhs, int rel, EjsVar *rhs); +#if BLD_FEATURE_FLOATING_POINT +static int evalFloatExpr(Ejs *ep, double l, int rel, double r); +#endif +static int evalBoolExpr(Ejs *ep, int l, int rel, int r); +static int evalNumericExpr(Ejs *ep, EjsNum l, int rel, EjsNum r); +static int evalObjExpr(Ejs *ep, EjsVar *lhs, int rel, EjsVar *rhs) ; +static int evalStringExpr(Ejs *ep, EjsVar *lhs, int rel, EjsVar *rhs); +static int evalMethod(Ejs *ep, EjsVar *obj, EjsProc *proc, int flags); +static EjsProperty *findProperty(Ejs *ep, EjsVar *op, const char *property, + int flags); +static EjsVar *pickSpace(Ejs *ep, int state, const char *property, int flags); +static void freeProc(Ejs *ep, EjsProc *proc); +static int parseArgs(Ejs *ep, int state, int flags); +static int parseArrayLiteral(Ejs *ep, int state, int flags, char *id); +static int parseAssignment(Ejs *ep, int state, int flags, char *id); +static int parseClass(Ejs *ep, int state, int flags); +static int parseForInner(Ejs *ep, int state, int flags, + EjsInput *condScript, EjsInput *incrScript, + EjsInput *bodyScript, EjsInput *endScript); +static int parseCond(Ejs *ep, int state, int flags); +static int parseDeclaration(Ejs *ep, int state, int flags); +static int parseExpr(Ejs *ep, int state, int flags); +static int parseFor(Ejs *ep, int state, int flags); +static int parseRegFor(Ejs *ep, int state, int flags); +static int parseForIn(Ejs *ep, int state, int flags, int each); +static int parseId(Ejs *ep, int state, int flags, char **id, int *done); +static int parseInc(Ejs *ep, int state, int flags); +static int parseIf(Ejs *ep, int state, int flags, int *done); +static int parseFunction(Ejs *ep, int state, int flags); +static int parseMethod(Ejs *ep, int state, int flags, char *id); +static int parseObjectLiteral(Ejs *ep, int state, int flags, char *id); +static int parseStmt(Ejs *ep, int state, int flags); +static int parseThrow(Ejs *ep, int state, int flags); +static int parseTry(Ejs *ep, int state, int flags); +static void removeNewlines(Ejs *ep, int state); +static EjsProperty *searchSpacesForProperty(Ejs *ep, int state, EjsVar *obj, + char *property, int flags); +static int assignPropertyValue(Ejs *ep, char *id, int state, EjsVar *value, + int flags); +static int updateProperty(Ejs *ep, EjsVar *obj, const char *id, int state, + EjsVar *value); +static void updateResult(Ejs *ep, int state, int flags, EjsVar *vp); +static int getNextNonSpaceToken(Ejs *ep, int state); + +static int callConstructor(Ejs *ep, EjsVar *thisObj, EjsVar *baseClass, + MprArray *args); +static int callCMethod(Ejs *ep, EjsVar *obj, EjsProc *proc, + EjsVar *prototype); +static int callStringCMethod(Ejs *ep, EjsVar *obj, EjsProc *proc, + EjsVar *prototype); +static int callMethod(Ejs *ep, EjsVar *obj, EjsProc *proc, + EjsVar *prototype); +static int runMethod(Ejs *ep, EjsVar *thisObj, EjsVar *method, + const char *methodName, MprArray *args); + +static EjsInput *getInputStruct(Ejs *ep); +static void freeInputStruct(Ejs *ep, EjsInput *input); + +static void *pushFrame(Ejs *ep, int size); +static void *popFrame(Ejs *ep, int size); + +/************************************* Code ***********************************/ +/* + * Recursive descent parser for EJS + */ + +int ejsParse(Ejs *ep, int state, int flags) +{ + mprAssert(ep); + +#if MOB + if (mprStackCheck(ep)) { + char *stack; + stack = ejsFormatStack(ep); + mprLog(ep, 0, "\nStack grew : MAX %d\n", mprStackSize(ep)); + mprLog(ep, 0, "Stack\n %s\n", stack); + mprFree(stack); + } +#endif + + if (ep->flags & EJS_FLAGS_EXIT) { + return EJS_STATE_RET; + } + + ep->inputMarker = ep->input->scriptServp; + + switch (state) { + /* + * Any statement, method arguments or conditional expressions + */ + case EJS_STATE_STMT: + state = parseStmt(ep, state, flags); + if (state != EJS_STATE_STMT_BLOCK_DONE && state != EJS_STATE_STMT_DONE){ + goto err; + } + break; + + case EJS_STATE_DEC: + state = parseStmt(ep, state, flags); + if (state != EJS_STATE_DEC_DONE) { + goto err; + } + break; + + case EJS_STATE_EXPR: + state = parseStmt(ep, state, flags); + if (state != EJS_STATE_EXPR_DONE) { + goto err; + } + break; + + /* + * Variable declaration list + */ + case EJS_STATE_DEC_LIST: + state = parseDeclaration(ep, state, flags); + if (state != EJS_STATE_DEC_LIST_DONE) { + goto err; + } + break; + + /* + * Method argument string + */ + case EJS_STATE_ARG_LIST: + state = parseArgs(ep, state, flags); + if (state != EJS_STATE_ARG_LIST_DONE) { + goto err; + } + break; + + /* + * Logical condition list (relational operations separated by &&, ||) + */ + case EJS_STATE_COND: + state = parseCond(ep, state, flags); + if (state != EJS_STATE_COND_DONE) { + goto err; + } + break; + + /* + * Expression list + */ + case EJS_STATE_RELEXP: + state = parseExpr(ep, state, flags); + if (state != EJS_STATE_RELEXP_DONE) { + goto err; + } + break; + } + + /* + * Recursion protection + */ + if (ep->input->scriptServp == ep->inputMarker) { + if (ep->recurseCount++ > 20) { + ejsSyntaxError(ep, "Input syntax error"); + state = EJS_STATE_ERR; + } + } else { + ep->recurseCount = 0; + } + + if (state == EJS_STATE_RET || state == EJS_STATE_EOF) { + return state; + } + +done: + return state; + +err: + if (state == EJS_STATE_RET || state == EJS_STATE_EOF) { + goto done; + } + if (state != EJS_STATE_ERR) { + ejsSyntaxError(ep, 0); + } + state = EJS_STATE_ERR; + goto done; +} + +/******************************************************************************/ +/* + * Local vars + */ + +typedef struct ParseStmt { + EjsProc *saveProc; + EjsProperty *pp; + EjsVar *saveObj, *exception; + char *str, *id; + int done, tid, rs, saveObjPerm, expectEndOfStmt; +} ParseStmt; + +/* + * Parse expression (leftHandSide operator rightHandSide) + */ + + +static int parseStmt(Ejs *ep, int state, int flags) +{ + ParseStmt *sp; + + mprAssert(ep); + + if ((sp = pushFrame(ep, sizeof(ParseStmt))) == 0) { + return EJS_STATE_ERR; + } + + sp->id = 0; + sp->expectEndOfStmt = 0; + sp->saveProc = NULL; + + ep->currentObj = 0; + ep->currentProperty = 0; + + for (sp->done = 0; !sp->done && state != EJS_STATE_ERR; ) { + sp->tid = ejsLexGetToken(ep, state); + +#if (WIN || BREW_SIMULATOR) && BLD_DEBUG && DISABLED + /* MOB -- make cross platform */ + _CrtCheckMemory(); +#endif + + switch (sp->tid) { + default: + ejsLexPutbackToken(ep, sp->tid, ep->token); + goto done; + + case EJS_TOK_EXPR: + if (state == EJS_STATE_EXPR) { + ejsLexPutbackToken(ep, EJS_TOK_EXPR, ep->token); + } + goto done; + + case EJS_TOK_LOGICAL: + ejsLexPutbackToken(ep, sp->tid, ep->token); + goto done; + + case EJS_TOK_ERR: + if (state != EJS_STATE_ERR && !ep->gotException) { + ejsSyntaxError(ep, 0); + } + state = EJS_STATE_ERR; + goto done; + + case EJS_TOK_EOF: + state = EJS_STATE_EOF; + goto done; + + case EJS_TOK_NEWLINE: + break; + + case EJS_TOK_SEMI: + /* + * This case is when we discover no statement and just a lone ';' + */ + if (state != EJS_STATE_STMT) { + ejsLexPutbackToken(ep, sp->tid, ep->token); + } + goto done; + + case EJS_TOK_LBRACKET: + if (flags & EJS_FLAGS_EXE) { + ep->currentObj = &ep->currentProperty->var; + if (ep->currentObj != 0 && ep->currentObj->type != + EJS_TYPE_OBJECT) { + ejsError(ep, EJS_REFERENCE_ERROR, + "Property reference to a non-object type \"%s\"\n", + sp->id); + goto err; + } + } + + sp->saveObj = ep->currentObj; + sp->saveObjPerm = ejsMakeObjPermanent(sp->saveObj, 1); + + sp->rs = ejsParse(ep, EJS_STATE_RELEXP, flags); + + ejsMakeObjPermanent(sp->saveObj, sp->saveObjPerm); + ep->currentObj = sp->saveObj; + + if (sp->rs < 0) { + state = sp->rs; + goto done; + } + + mprFree(sp->id); + /* MOB rc */ + sp->str = ejsVarToString(ep, ep->result); + sp->id = mprStrdup(ep, sp->str); + + if (sp->id[0] == '\0') { + if (flags & EJS_FLAGS_EXE) { + ejsError(ep, EJS_RANGE_ERROR, + "[] expression evaluates to the empty string\n"); + goto err; + } + } else { + sp->pp = searchSpacesForProperty(ep, state, ep->currentObj, + sp->id, flags); + ep->currentProperty = sp->pp; + updateResult(ep, state, flags, ejsGetVarPtr(sp->pp)); + } + + if ((sp->tid = ejsLexGetToken(ep, state)) != EJS_TOK_RBRACKET) { + ejsSyntaxError(ep, "Missing ']'"); + goto err; + } + break; + + case EJS_TOK_PERIOD: + if (flags & EJS_FLAGS_EXE) { + if (ep->currentProperty == 0) { + ejsError(ep, EJS_REFERENCE_ERROR, + "Undefined object \"%s\"", sp->id); + goto err; + } + } + ep->currentObj = &ep->currentProperty->var; + if (flags & EJS_FLAGS_EXE) { + if (ep->currentObj != 0 && ep->currentObj->type != + EJS_TYPE_OBJECT) { + ejsError(ep, EJS_REFERENCE_ERROR, + "Property reference to a non-object type \"%s\"\n", + sp->id); + goto err; + } + } + if ((sp->tid = ejsLexGetToken(ep, state)) != EJS_TOK_ID) { + ejsError(ep, EJS_REFERENCE_ERROR, "Bad property after '.': %s", + ep->token); + goto err; + } + /* Fall through */ + + case EJS_TOK_ID: + state = parseId(ep, state, flags, &sp->id, &sp->done); + if (sp->done && state == EJS_STATE_STMT) { + sp->expectEndOfStmt = 1; + } + break; + + case EJS_TOK_ASSIGNMENT: + sp->tid = ejsLexGetToken(ep, state); + if (sp->tid == EJS_TOK_LBRACE) { + /* + * var = { name: value, name: value, ... } + */ + if (parseObjectLiteral(ep, state, flags, sp->id) < 0) { + ejsSyntaxError(ep, "Bad object literal"); + goto err; + } + + } else if (sp->tid == EJS_TOK_LBRACKET) { + /* + * var = [ array elements ] + */ + if (parseArrayLiteral(ep, state, flags, sp->id) < 0) { + ejsSyntaxError(ep, "Bad array literal"); + goto err; + } + + } else if (sp->tid == EJS_TOK_EXPR && + (int) *ep->token == EJS_EXPR_LESS) { + /* + * var = <xmlTag> .../</xmlTag> + */ + ejsSyntaxError(ep, "XML literals are not yet supported"); + goto err; + + } else { + /* + * var = expression + */ + ejsLexPutbackToken(ep, sp->tid, ep->token); + state = parseAssignment(ep, state, flags, sp->id); + if (state == EJS_STATE_ERR) { + if (ep->flags & EJS_FLAGS_EXIT) { + state = EJS_STATE_RET; + goto done; + } + if (!ep->gotException) { + ejsSyntaxError(ep, 0); + } + goto err; + } + } + + if (flags & EJS_FLAGS_EXE) { + if (assignPropertyValue(ep, sp->id, state, ep->result, + flags) < 0) { + if (ep->gotException == 0) { + ejsError(ep, EJS_EVAL_ERROR, "Can't set property %s", + sp->id); + } + goto err; + } + } + + if (state == EJS_STATE_STMT) { + sp->expectEndOfStmt = 1; + goto done; + } + break; + + case EJS_TOK_INC_DEC: + state = parseInc(ep, state, flags); + if (state == EJS_STATE_STMT) { + sp->expectEndOfStmt = 1; + } + break; + + case EJS_TOK_NEW: + /* MOB -- could we remove rs and just use state */ + sp->rs = ejsParse(ep, EJS_STATE_EXPR, flags | EJS_FLAGS_NEW); + if (sp->rs < 0) { + state = sp->rs; + goto done; + } + break; + + case EJS_TOK_DELETE: + sp->rs = ejsParse(ep, EJS_STATE_EXPR, flags | EJS_FLAGS_DELETE); + if (sp->rs < 0) { + state = sp->rs; + goto done; + } + if (flags & EJS_FLAGS_EXE) { + /* Single place where properties are deleted */ + if (ep->currentObj == 0 || ep->currentProperty == 0) { + ejsError(ep, EJS_EVAL_ERROR, + "Can't find property to delete"); + goto err; + } + if (ep->currentObj->isArray) { + ejsSetArrayLength(ep, ep->currentObj, 0, + ep->currentProperty->name, 0); + } + ejsDeleteProperty(ep, ep->currentObj, + ep->currentProperty->name); + ep->currentProperty = 0; + } + goto done; + + case EJS_TOK_FUNCTION: + /* + * Parse a function declaration + */ + state = parseFunction(ep, state, flags); + goto done; + + case EJS_TOK_THROW: + state = parseThrow(ep, state, flags); + goto done; + + case EJS_TOK_TRY: + state = parseTry(ep, state, flags); + goto done; + + case EJS_TOK_CLASS: + case EJS_TOK_MODULE: + state = parseClass(ep, state, flags); + goto done; + + case EJS_TOK_LITERAL: + /* + * Set the result to the string literal + */ + if (flags & EJS_FLAGS_EXE) { + ejsWriteVarAsString(ep, ep->result, ep->token); + ejsSetVarName(ep, ep->result, ""); + } + if (state == EJS_STATE_STMT) { + sp->expectEndOfStmt = 1; + } + goto done; + + case EJS_TOK_NUMBER: + /* + * Set the result to the parsed number + */ + if (flags & EJS_FLAGS_EXE) { + ejsWriteVar(ep, ep->result, &ep->tokenNumber, 0); + } + if (state == EJS_STATE_STMT) { + sp->expectEndOfStmt = 1; + } + goto done; + + case EJS_TOK_METHOD_NAME: + /* + * parse a method() invocation + */ + mprAssert(ep->currentObj); + state = parseMethod(ep, state, flags, sp->id); + if (state == EJS_STATE_STMT) { + sp->expectEndOfStmt = 1; + } + if (ep->flags & EJS_FLAGS_EXIT) { + state = EJS_STATE_RET; + } + goto done; + + case EJS_TOK_IF: + state = parseIf(ep, state, flags, &sp->done); + if (state < 0) { + goto done; + } + break; + + case EJS_TOK_FOR: + state = parseFor(ep, state, flags); + goto done; + + case EJS_TOK_VAR: + if ((sp->rs = ejsParse(ep, EJS_STATE_DEC_LIST, flags)) < 0) { + state = sp->rs; + goto done; + } + goto done; + + case EJS_TOK_COMMA: + ejsLexPutbackToken(ep, sp->tid, ep->token); + goto done; + + case EJS_TOK_LPAREN: + if (state == EJS_STATE_EXPR) { + if ((sp->rs = ejsParse(ep, EJS_STATE_RELEXP, flags)) < 0) { + state = sp->rs; + goto done; + } + if (ejsLexGetToken(ep, state) != EJS_TOK_RPAREN) { + ejsSyntaxError(ep, 0); + goto err; + } + goto done; + + } else if (state == EJS_STATE_STMT) { + ejsLexPutbackToken(ep, EJS_TOK_METHOD_NAME, ep->token); + } + break; + + case EJS_TOK_RPAREN: + ejsLexPutbackToken(ep, sp->tid, ep->token); + goto done; + + case EJS_TOK_EXTENDS: + if (! (flags & EJS_FLAGS_CLASS_DEC)) { + ejsSyntaxError(ep, 0); + goto err; + } + sp->saveObj = ep->currentObj; + sp->saveObjPerm = ejsMakeObjPermanent(sp->saveObj, 1); + + sp->rs = ejsParse(ep, EJS_STATE_STMT, flags); + ejsMakeObjPermanent(sp->saveObj, sp->saveObjPerm); + + if (sp->rs < 0) { + state = sp->rs; + goto done; + } + + if (flags & EJS_FLAGS_EXE) { + if (createClass(ep, sp->saveObj, sp->id, + ejsGetVarPtr(ep->currentProperty)) < 0) { + goto err; + } + } + if (ejsLexGetToken(ep, state) != EJS_TOK_LBRACE) { + ejsSyntaxError(ep, 0); + goto err; + } + ejsLexPutbackToken(ep, ep->tid, ep->token); + goto done; + + case EJS_TOK_LBRACE: + if (flags & EJS_FLAGS_CLASS_DEC) { + if (state == EJS_STATE_DEC) { + if (flags & EJS_FLAGS_EXE) { + if (createClass(ep, ep->currentObj, sp->id, 0) < 0) { + goto err; + } + } + ejsLexPutbackToken(ep, sp->tid, ep->token); + + } else if (state == EJS_STATE_STMT) { + ejsLexPutbackToken(ep, sp->tid, ep->token); + } + goto done; + } + + /* + * This handles any code in braces except "if () {} else {}" + */ + if (state != EJS_STATE_STMT) { + ejsSyntaxError(ep, 0); + goto err; + } + + /* + * Parse will return EJS_STATE_STMT_BLOCK_DONE when the RBRACE + * is seen. + */ + sp->exception = 0; + do { + state = ejsParse(ep, EJS_STATE_STMT, flags); + if (state == EJS_STATE_ERR) { + /* + * We need to keep parsing to get to the end of the block + */ + if (sp->exception == 0) { + sp->exception = ejsDupVar(ep, ep->result, + EJS_SHALLOW_COPY); + if (sp->exception == 0) { + ejsMemoryError(ep); + goto err; + } + if (sp->exception->type == EJS_TYPE_OBJECT) { + ejsMakeObjLive(sp->exception, 0); + mprAssert(sp->exception->objectState->alive == 0); + } + + /* + * If we're in a try block, we need to keep parsing + * so we can find the end of the block and the start + * of the catch block. Otherwise, we are done. + */ + if (!(flags & EJS_FLAGS_TRY)) { + break; + } + } + flags &= ~EJS_FLAGS_EXE; + if (ep->recurseCount > 20) { + break; + } + state = EJS_STATE_STMT_DONE; + ep->gotException = 0; + } + + } while (state == EJS_STATE_STMT_DONE); + + if (sp->exception) { + ep->gotException = 1; + ejsWriteVar(ep, ep->result, sp->exception, EJS_SHALLOW_COPY); + + /* Eat the closing brace */ + ejsLexGetToken(ep, state); + ejsFreeVar(ep, sp->exception); + + goto err; + } + ejsFreeVar(ep, sp->exception); + + if (state < 0) { + goto done; + } + + if (ejsLexGetToken(ep, state) != EJS_TOK_RBRACE) { + ejsSyntaxError(ep, 0); + goto err; + } + state = EJS_STATE_STMT_DONE; + goto done; + + case EJS_TOK_RBRACE: + if (state == EJS_STATE_STMT) { + ejsLexPutbackToken(ep, sp->tid, ep->token); + state = EJS_STATE_STMT_BLOCK_DONE; + + } else if (state == EJS_STATE_EXPR) { + ejsLexPutbackToken(ep, sp->tid, ep->token); + state = EJS_STATE_EXPR; + + } else { + ejsSyntaxError(ep, 0); + state = EJS_STATE_ERR; + } + goto done; + + case EJS_TOK_RETURN: + if ((sp->rs = ejsParse(ep, EJS_STATE_RELEXP, flags)) < 0) { + state = sp->rs; + goto done; + } + if (flags & EJS_FLAGS_EXE) { + state = EJS_STATE_RET; + goto done; + } + break; + } + } +done: + mprFree(sp->id); + + if (sp->expectEndOfStmt && state >= 0) { + sp->tid = ejsLexGetToken(ep, state); + if (sp->tid == EJS_TOK_RBRACE) { + ejsLexPutbackToken(ep, EJS_TOK_RBRACE, ep->token); + + } else if (sp->tid != EJS_TOK_SEMI && sp->tid != EJS_TOK_NEWLINE && + sp->tid != EJS_TOK_EOF) { + ejsSyntaxError(ep, 0); + state = EJS_STATE_ERR; + + } else { + /* + * Skip newlines after semi-colon + */ + removeNewlines(ep, state); + } + } + + /* + * Advance the state + */ + switch (state) { + case EJS_STATE_STMT: + case EJS_STATE_STMT_DONE: + state = EJS_STATE_STMT_DONE; + break; + + case EJS_STATE_DEC: + case EJS_STATE_DEC_DONE: + state = EJS_STATE_DEC_DONE; + break; + + case EJS_STATE_EXPR: + case EJS_STATE_EXPR_DONE: + state = EJS_STATE_EXPR_DONE; + break; + + case EJS_STATE_STMT_BLOCK_DONE: + case EJS_STATE_EOF: + case EJS_STATE_RET: + break; + + default: + if (state != EJS_STATE_ERR) { + ejsSyntaxError(ep, 0); + } + state = EJS_STATE_ERR; + } + popFrame(ep, sizeof(ParseStmt)); + return state; + +err: + state = EJS_STATE_ERR; + goto done; +} + +/******************************************************************************/ +/* + * Local vars + */ + +typedef struct ParseFor { + char *initToken; + int tid, foundVar, initId, each; +} ParseFor; + +/* + * Parse method arguments + */ + +static int parseFor(Ejs *ep, int state, int flags) +{ + ParseFor *sp; + + if ((sp = pushFrame(ep, sizeof(ParseFor))) == 0) { + return EJS_STATE_ERR; + } + + mprAssert(ep); + + if (state != EJS_STATE_STMT) { + ejsSyntaxError(ep, 0); + goto err; + } + + if ((sp->tid = ejsLexGetToken(ep, state)) == EJS_TOK_EACH) { + sp->each = 1; + sp->tid = ejsLexGetToken(ep, state); + + } else { + sp->each = 0; + } + + if (sp->tid != EJS_TOK_LPAREN) { + ejsSyntaxError(ep, 0); + goto err; + } + + /* + * Need to peek 2-3 tokens ahead and see if this is a + * for [each] ([var] x in set) + * or + * for (init ; whileCond; incr) + */ + sp->initId = ejsLexGetToken(ep, EJS_STATE_EXPR); + sp->foundVar = 0; + if (sp->initId == EJS_TOK_ID && strcmp(ep->token, "var") == 0) { + sp->foundVar = 1; + sp->initId = ejsLexGetToken(ep, EJS_STATE_EXPR); + } + sp->initToken = mprStrdup(ep, ep->token); + + sp->tid = ejsLexGetToken(ep, EJS_STATE_EXPR); + + ejsLexPutbackToken(ep, sp->tid, ep->token); + ejsLexPutbackToken(ep, sp->initId, sp->initToken); + mprFree(sp->initToken); + + if (sp->foundVar) { + ejsLexPutbackToken(ep, EJS_TOK_ID, "var"); + } + + if (sp->tid == EJS_TOK_IN) { + state = parseForIn(ep, state, flags, sp->each); + + } else { + state = parseRegFor(ep, state, flags); + } + +done: + popFrame(ep, sizeof(ParseFor)); + return state; + +err: + state = EJS_STATE_ERR; + goto done; +} + +/******************************************************************************/ +/* + * Parse method arguments + */ + +static int parseArgs(Ejs *ep, int state, int flags) +{ + EjsVar *vp; + int tid; + + mprAssert(ep); + + do { + /* + * Peek and see if there are no args + */ + tid = ejsLexGetToken(ep, state); + ejsLexPutbackToken(ep, tid, ep->token); + if (tid == EJS_TOK_RPAREN) { + break; + } + + /* + * If this is part of a constructor, must run methods in args normally + */ + flags &= ~EJS_FLAGS_NEW; + + state = ejsParse(ep, EJS_STATE_RELEXP, flags); + if (state < 0) { + return state; + } + if (flags & EJS_FLAGS_EXE) { + mprAssert(ep->proc->args); + vp = ejsDupVar(ep, ep->result, EJS_SHALLOW_COPY); + if (vp == 0) { + ejsMemoryError(ep); + return EJS_STATE_ERR; + } + /* MOB */ + if (vp->type == EJS_TYPE_OBJECT) { + ejsMakeObjLive(vp, 0); + mprAssert(vp->objectState->alive == 0); + } + + /* + * Propagate the name + */ + ejsSetVarName(ep, vp, ep->result->propertyName); + + mprAddItem(ep->proc->args, vp); + + } + /* + * Peek at the next token, continue if more args (ie. comma seen) + */ + tid = ejsLexGetToken(ep, state); + if (tid != EJS_TOK_COMMA) { + ejsLexPutbackToken(ep, tid, ep->token); + } + } while (tid == EJS_TOK_COMMA); + + if (tid != EJS_TOK_RPAREN && state != EJS_STATE_RELEXP_DONE) { + ejsSyntaxError(ep, 0); + return EJS_STATE_ERR; + } + return EJS_STATE_ARG_LIST_DONE; +} + +/******************************************************************************/ +/* + * Local vars + */ + +typedef struct ParseAssign { + EjsProperty *saveProperty; + EjsVar *saveObj; + int saveObjPerm, savePropPerm, rc; +} ParseAssign; + +/* + * Parse an assignment statement + */ + +static int parseAssignment(Ejs *ep, int state, int flags, char *id) +{ + ParseAssign *sp; + + + if (id == 0) { + if (!ep->gotException) { + ejsSyntaxError(ep, 0); + } + return EJS_STATE_ERR; + } + + if ((sp = pushFrame(ep, sizeof(ParseAssign))) == 0) { + return EJS_STATE_ERR; + } + + mprAssert(ep->currentObj); + + /* + * Parse the right hand side of the "=" + */ + sp->saveObj = ep->currentObj; + sp->saveProperty = ep->currentProperty; + + sp->saveObjPerm = ejsMakeObjPermanent(sp->saveObj, 1); + sp->savePropPerm = ejsMakeObjPermanent(ejsGetVarPtr(sp->saveProperty), 1); + + sp->rc = ejsParse(ep, EJS_STATE_RELEXP, flags | EJS_FLAGS_ASSIGNMENT); + + ejsMakeObjPermanent(sp->saveObj, sp->saveObjPerm); + ejsMakeObjPermanent(ejsGetVarPtr(sp->saveProperty), sp->savePropPerm); + + if (sp->rc < 0) { + state = EJS_STATE_ERR; + } + + ep->currentObj = sp->saveObj; + ep->currentProperty = sp->saveProperty; + + popFrame(ep, sizeof(ParseAssign)); + + if (! (flags & EJS_FLAGS_EXE)) { + return state; + } + + return state; +} + +/******************************************************************************/ + +static int assignPropertyValue(Ejs *ep, char *id, int state, EjsVar *value, + int flags) +{ + EjsProperty *saveProperty; + EjsVar *saveObj, *obj, *vp; + char *procName; + int saveObjPerm, savePropPerm, rc; + + mprAssert(flags & EJS_FLAGS_EXE); + + if (ep->currentProperty && + !ep->currentProperty->var.flags & EJS_GET_ACCESSOR) { + obj = ep->currentObj; + + } else { + /* + * Handle any set accessors. + * FUTURE OPT -- could be faster + * FUTURE OPT -- coming here even when doing just a set "x = value"; + */ + procName = 0; + if (mprAllocStrcat(MPR_LOC_ARGS(ep), &procName, EJS_MAX_ID + 5, 0, + "-set-", id, 0) > 0) { + + MprArray *args; + + ep->currentProperty = searchSpacesForProperty(ep, state, + ep->currentObj, procName, flags); + + if (ep->currentProperty) { + args = mprCreateItemArray(ep, EJS_INC_ARGS, EJS_MAX_ARGS); + + vp = ejsDupVar(ep, value, EJS_SHALLOW_COPY); + mprAddItem(args, vp); + mprAssert(! ejsObjIsCollectable(vp)); + + saveObj = ep->currentObj; + saveProperty = ep->currentProperty; + + saveObjPerm = ejsMakeObjPermanent(saveObj, 1); + savePropPerm = ejsMakeObjPermanent(ejsGetVarPtr(saveProperty), + 1); + + /* + * Invoke the set accessor + */ + rc = ejsRunMethod(ep, ep->currentObj, procName, args); + mprFree(procName); + ejsFreeMethodArgs(ep, args); + + ejsMakeObjPermanent(saveObj, saveObjPerm); + ejsMakeObjPermanent(ejsGetVarPtr(saveProperty), savePropPerm); + + ep->currentObj = saveObj; + ep->currentProperty = saveProperty; + + if (rc < 0) { + return EJS_STATE_ERR; + } + return state; + } + mprFree(procName); + } + + if (ep->currentProperty == 0) { + /* + * MOB -- can we omit this as updateProperty below will create + */ + if (createProperty(ep, &obj, id, state) < 0) { + return EJS_STATE_ERR; + } + } + } + + if (updateProperty(ep, obj, id, state, value) < 0) { + return EJS_STATE_ERR; + } + + vp = ejsGetVarPtr(ep->currentProperty); + if (vp->type == EJS_TYPE_OBJECT) { + ejsMakeObjLive(vp, 1); + } + + return state; +} + +/******************************************************************************/ + +static int parseObjectLiteral(Ejs *ep, int state, int flags, char *id) +{ + EjsProperty *saveProperty; + EjsVar *saveObj; + EjsVar *obj; + char *name; + int saveObjPerm, savePropPerm, tid; + + name = 0; + + saveObj = ep->currentObj; + saveProperty = ep->currentProperty; + + saveObjPerm = ejsMakeObjPermanent(saveObj, 1); + savePropPerm = ejsMakeObjPermanent(ejsGetVarPtr(saveProperty), 1); + + if (flags & EJS_FLAGS_EXE) { + obj = ejsCreateSimpleObj(ep, "Object"); + if (obj == 0) { + ejsMemoryError(ep); + goto err; + } + mprAssert(! ejsObjIsCollectable(obj)); + + } else { + obj = 0; + } + + do { + tid = getNextNonSpaceToken(ep, state); + if (tid != EJS_TOK_ID) { + ejsSyntaxError(ep, 0); + goto err; + } + name = mprStrdup(ep, ep->token); + + tid = getNextNonSpaceToken(ep, state); + if (tid != EJS_TOK_COLON) { + ejsSyntaxError(ep, 0); + goto err; + } + + if (flags & EJS_FLAGS_EXE) { + /* FUTURE OPT -- can we optimize this. We are double accessing id + with the Put below. Should we be using this or ejsSetProperty + */ + if (ejsCreatePropertyMethod(ep, obj, name) == 0) { + ejsMemoryError(ep); + goto err; + } + } + + if (ejsParse(ep, EJS_STATE_RELEXP, flags) < 0) { + goto err; + } + if (flags & EJS_FLAGS_EXE) { + if (ejsSetPropertyMethod(ep, obj, name, ep->result) == 0) { + ejsMemoryError(ep); + goto err; + } + } + mprFree(name); + name = 0; + + tid = getNextNonSpaceToken(ep, state); + + } while (tid == EJS_TOK_COMMA); + + if (tid != EJS_TOK_RBRACE) { + ejsSyntaxError(ep, 0); + goto err; + } + + if (flags & EJS_FLAGS_EXE) { + ejsMakeObjLive(obj, 1); + ejsWriteVar(ep, ep->result, obj, EJS_SHALLOW_COPY); + } + +done: + ejsMakeObjPermanent(saveObj, saveObjPerm); + ejsMakeObjPermanent(ejsGetVarPtr(saveProperty), savePropPerm); + + ep->currentObj = saveObj; + ep->currentProperty = saveProperty; + + if (obj) { + ejsFreeVar(ep, obj); + } + return state; + +err: + mprFree(name); + state = EJS_STATE_ERR; + goto done; +} + +/******************************************************************************/ + +static int parseArrayLiteral(Ejs *ep, int state, int flags, char *id) +{ + EjsProperty *saveProperty; + EjsVar *saveObj; + EjsVar *obj; + int saveObjPerm, savePropPerm, tid; + + saveObj = ep->currentObj; + saveProperty = ep->currentProperty; + + saveObjPerm = ejsMakeObjPermanent(saveObj, 1); + savePropPerm = ejsMakeObjPermanent(ejsGetVarPtr(saveProperty), 1); + + if (flags & EJS_FLAGS_EXE) { + obj = ejsCreateArray(ep, 0); + if (obj == 0) { + ejsMemoryError(ep); + goto err; + } + mprAssert(! ejsObjIsCollectable(obj)); + + } else { + obj = 0; + } + + do { + if (ejsParse(ep, EJS_STATE_RELEXP, flags) < 0) { + goto err; + } + if (flags & EJS_FLAGS_EXE) { + /* MOB _- should this be put[array.length] */ + if (ejsAddArrayElt(ep, obj, ep->result, EJS_SHALLOW_COPY) == 0) { + goto err; + } + } + + tid = getNextNonSpaceToken(ep, state); + + } while (tid == EJS_TOK_COMMA); + + if (tid != EJS_TOK_RBRACKET) { + ejsSyntaxError(ep, "Missing right bracket"); + goto err; + } + + if (flags & EJS_FLAGS_EXE) { + ejsMakeObjLive(obj, 1); + ejsWriteVar(ep, ep->result, obj, EJS_SHALLOW_COPY); + } + +done: + ejsMakeObjPermanent(saveObj, saveObjPerm); + ejsMakeObjPermanent(ejsGetVarPtr(saveProperty), savePropPerm); + + ep->currentObj = saveObj; + ep->currentProperty = saveProperty; + + ejsFreeVar(ep, obj); + return state; + +err: + state = EJS_STATE_ERR; + goto done; +} + +/******************************************************************************/ +/* + * Create a property. + */ + +/* +MOB -- simplify this. Enforce ep->currentObj to be always set. +Then we can delete this and just call + + ejsCreatePropertyMethod(ep->currentObj, id); +*/ +//XX +static int createProperty(Ejs *ep, EjsVar **objp, const char *id, int state) +{ + EjsVar *obj, *vp; + + mprAssert(id && *id); + mprAssert(objp); + + /* + * Determine the variable scope to use for the property. + * Standard says: "var x" means declare locally. + * "x = 2" means declare globally if x is undefined. + */ + if (ep->currentObj) { + if (ep->currentObj->type != EJS_TYPE_OBJECT) { + ejsSyntaxError(ep, "Reference is not an object"); + return EJS_STATE_ERR; + } + obj = ep->currentObj; + + } else { + /* MOB -- we should never be doing this here. ep->currentObj should + always be set already */ + obj = (state == EJS_STATE_DEC) ? ep->local : ep->global; + } + mprAssert(obj); + + vp = ejsCreatePropertyMethod(ep, obj, id); + if (vp == 0) { + if (!ep->gotException) { + ejsMemoryError(ep); + } + return EJS_STATE_ERR; + } + + *objp = obj; + return state; +} + +/******************************************************************************/ +/* + * Update a property. + * + * Return with ep->currentProperty updated to point to the property. + */ + +static int updateProperty(Ejs *ep, EjsVar *obj, const char *id, int state, + EjsVar *value) +{ + EjsVar *vp; + + /* + * MOB -- do ready-only check here + */ + vp = ejsSetPropertyMethod(ep, obj, id, value); + if (vp == 0) { + ejsMemoryError(ep); + return EJS_STATE_ERR; + } + ep->currentProperty = ejsGetPropertyPtr(vp); + + obj->objectState->dirty = 1; + + return state; +} + +/******************************************************************************/ +/* + * Local vars + */ + +typedef struct ParseCond { + EjsVar lhs, rhs; + int tid, operator; +} ParseCond; + +/* + * Parse conditional expression (relational ops separated by ||, &&) + */ + +static int parseCond(Ejs *ep, int state, int flags) +{ + ParseCond *sp; + + if ((sp = pushFrame(ep, sizeof(ParseCond))) == 0) { + return EJS_STATE_ERR; + } + + mprAssert(ep); + + if (flags & EJS_FLAGS_EXE) { + ejsClearVar(ep, ep->result); + } + + sp->lhs.type = sp->rhs.type = EJS_TYPE_UNDEFINED; + sp->lhs.objectState = sp->rhs.objectState = 0; + sp->lhs.allocatedData = sp->rhs.allocatedData = 0; + + ejsSetVarName(ep, &sp->lhs, "lhs"); + ejsSetVarName(ep, &sp->rhs, "rhs"); + + sp->operator = 0; + + do { + /* + * Recurse to handle one side of a conditional. Accumulate the + * left hand side and the final result in ep->result. + */ + state = ejsParse(ep, EJS_STATE_RELEXP, flags); + if (state < 0) { + break; + } + + if (flags & EJS_FLAGS_EXE) { + if (sp->operator > 0) { + /* + * FUTURE -- does not do precedence + */ + ejsWriteVar(ep, &sp->rhs, ep->result, EJS_SHALLOW_COPY); + if (evalCond(ep, &sp->lhs, sp->operator, &sp->rhs) < 0) { + state = EJS_STATE_ERR; + break; + } + /* Result left in ep->result */ + /* MOB */ + if (sp->lhs.type == EJS_TYPE_OBJECT) { + mprAssert(sp->lhs.objectState->alive == 0); + } + if (sp->rhs.type == EJS_TYPE_OBJECT) { + mprAssert(sp->rhs.objectState->alive == 0); + } + } + } + + sp->tid = ejsLexGetToken(ep, state); + if (sp->tid == EJS_TOK_LOGICAL) { + sp->operator = (int) *ep->token; + + } else if (sp->tid == EJS_TOK_RPAREN || sp->tid == EJS_TOK_SEMI) { + ejsLexPutbackToken(ep, sp->tid, ep->token); + state = EJS_STATE_COND_DONE; + break; + + } else { + ejsLexPutbackToken(ep, sp->tid, ep->token); + } + + if (flags & EJS_FLAGS_EXE) { + ejsWriteVar(ep, &sp->lhs, ep->result, EJS_SHALLOW_COPY); + } + + } while (state == EJS_STATE_RELEXP_DONE); + + ejsClearVar(ep, &sp->lhs); + ejsClearVar(ep, &sp->rhs); + + popFrame(ep, sizeof(ParseCond)); + + return state; +} + +/******************************************************************************/ +/* + * Parse variable declaration list. Declarations can be of the following forms: + * var x; + * var x, y, z; + * var x = 1 + 2 / 3, y = 2 + 4; + * var x = { property: value, property: value ... }; + * var x = [ property: value, property: value ... ]; + * + * We set the variable to NULL if there is no associated assignment. + */ + +static int parseDeclaration(Ejs *ep, int state, int flags) +{ + int tid; + + mprAssert(ep); + + do { + if ((tid = ejsLexGetToken(ep, state)) != EJS_TOK_ID) { + ejsSyntaxError(ep, 0); + return EJS_STATE_ERR; + } + ejsLexPutbackToken(ep, tid, ep->token); + + /* + * Parse the entire assignment or simple identifier declaration + */ + if (ejsParse(ep, EJS_STATE_DEC, flags) != EJS_STATE_DEC_DONE) { + return EJS_STATE_ERR; + } + + /* + * Peek at the next token, continue if comma seen + * Stop on ";" or "in" which is used in a "for (var x in ..." + */ + tid = ejsLexGetToken(ep, state); + + if (tid == EJS_TOK_SEMI) { + return EJS_STATE_DEC_LIST_DONE; + + } else if (tid == EJS_TOK_IN) { + ejsLexPutbackToken(ep, tid, ep->token); + return EJS_STATE_DEC_LIST_DONE; + + } else if (flags & EJS_FLAGS_CLASS_DEC && + (tid == EJS_TOK_LBRACE || tid == EJS_TOK_EXTENDS)) { + ejsLexPutbackToken(ep, tid, ep->token); + return EJS_STATE_DEC_LIST_DONE; + + } else if (tid == EJS_TOK_RPAREN && flags & EJS_FLAGS_CATCH) { + ejsLexPutbackToken(ep, tid, ep->token); + return EJS_STATE_DEC_LIST_DONE; + + } else if (tid != EJS_TOK_COMMA) { + ejsSyntaxError(ep, 0); + return EJS_STATE_ERR; + } + + } while (tid == EJS_TOK_COMMA); + + if (tid != EJS_TOK_SEMI) { + ejsSyntaxError(ep, 0); + return EJS_STATE_ERR; + } + return EJS_STATE_DEC_LIST_DONE; +} + +/******************************************************************************/ +/* + * Local vars + */ + +typedef struct ParseExpr { + EjsVar lhs, rhs; + int rel, tid, unaryMinus; +} ParseExpr; + +/* + * Parse expression (leftHandSide operator rightHandSide) + */ + +static int parseExpr(Ejs *ep, int state, int flags) +{ + ParseExpr *sp; + + mprAssert(ep); + + if ((sp = pushFrame(ep, sizeof(ParseExpr))) == 0) { + return EJS_STATE_ERR; + } + + if (flags & EJS_FLAGS_EXE) { + ejsClearVar(ep, ep->result); + } + + sp->lhs.type = sp->rhs.type = EJS_TYPE_UNDEFINED; + sp->lhs.objectState = sp->rhs.objectState = 0; + sp->lhs.allocatedData = sp->rhs.allocatedData = 0; + + ejsSetVarName(ep, &sp->lhs, "lhs"); + ejsSetVarName(ep, &sp->rhs, "rhs"); + + sp->rel = 0; + sp->tid = 0; + sp->unaryMinus = 0; + + do { + /* + * This loop will handle an entire expression list. We call parse + * to evalutate each term which returns the result in ep->result. + */ + if (sp->tid == EJS_TOK_LOGICAL) { + state = ejsParse(ep, EJS_STATE_RELEXP, flags); + if (state < 0) { + break; + } + } else { + sp->tid = ejsLexGetToken(ep, state); + if (sp->tid == EJS_TOK_EXPR && (int) *ep->token == EJS_EXPR_MINUS) { + sp->unaryMinus = 1; + + } else { + ejsLexPutbackToken(ep, sp->tid, ep->token); + } + + state = ejsParse(ep, EJS_STATE_EXPR, flags); + if (state < 0) { + break; + } + } + + if (flags & EJS_FLAGS_EXE) { + if (sp->unaryMinus) { + switch (ep->result->type) { + default: + case EJS_TYPE_UNDEFINED: + case EJS_TYPE_NULL: + case EJS_TYPE_STRING_CMETHOD: + case EJS_TYPE_CMETHOD: + case EJS_TYPE_METHOD: + case EJS_TYPE_PTR: + case EJS_TYPE_OBJECT: + case EJS_TYPE_STRING: + case EJS_TYPE_BOOL: + ejsError(ep, EJS_SYNTAX_ERROR, "Invalid unary minus"); + state = EJS_STATE_ERR; + break; + +#if BLD_FEATURE_FLOATING_POINT + case EJS_TYPE_FLOAT: + ep->result->floating = - ep->result->floating; + break; +#endif + + case EJS_TYPE_INT: + ep->result->integer = - ep->result->integer; + break; + +#if BLD_FEATURE_INT64 + case EJS_TYPE_INT64: + ep->result->integer64 = - ep->result->integer64; + break; +#endif + } + } + sp->unaryMinus = 0; + + if (sp->rel > 0) { + ejsWriteVar(ep, &sp->rhs, ep->result, EJS_SHALLOW_COPY); + if (sp->tid == EJS_TOK_LOGICAL) { + if (evalCond(ep, &sp->lhs, sp->rel, &sp->rhs) < 0) { + state = EJS_STATE_ERR; + break; + } + } else { + if (evalExpr(ep, &sp->lhs, sp->rel, &sp->rhs) < 0) { + state = EJS_STATE_ERR; + break; + } + } + } + /* MOB */ + if (sp->lhs.type == EJS_TYPE_OBJECT) { + ejsMakeObjLive(&sp->lhs, 0); + mprAssert(sp->lhs.objectState->alive == 0); + } + if (sp->rhs.type == EJS_TYPE_OBJECT) { + ejsMakeObjLive(&sp->rhs, 0); + mprAssert(sp->rhs.objectState->alive == 0); + } + } + + if ((sp->tid = ejsLexGetToken(ep, state)) == EJS_TOK_EXPR || + sp->tid == EJS_TOK_INC_DEC || sp->tid == EJS_TOK_LOGICAL) { + sp->rel = (int) *ep->token; + ejsWriteVar(ep, &sp->lhs, ep->result, EJS_SHALLOW_COPY); + + } else { + ejsLexPutbackToken(ep, sp->tid, ep->token); + state = EJS_STATE_RELEXP_DONE; + } + + } while (state == EJS_STATE_EXPR_DONE); + + ejsClearVar(ep, &sp->lhs); + ejsClearVar(ep, &sp->rhs); + + popFrame(ep, sizeof(ParseExpr)); + + return state; +} + +/******************************************************************************/ +/* + * Local vars + */ + +typedef struct ParseForIn { + EjsInput *endScript, *bodyScript; + EjsProperty *pp, *nextp; + EjsVar *iteratorVar, *setVar, *vp; + int forFlags, tid; +} ParseForIn; + +/* + * Parse the "for ... in" statement. Format for the statement is: + * + * for [each] (var varName in expression) { + * body; + * } + */ + +static int parseForIn(Ejs *ep, int state, int flags, int each) +{ + ParseForIn *sp; + + mprAssert(ep); + + if ((sp = pushFrame(ep, sizeof(ParseForIn))) == 0) { + return EJS_STATE_ERR; + } + + sp->setVar = 0; + sp->iteratorVar = 0; + sp->bodyScript = 0; + sp->endScript = 0; + + sp->tid = ejsLexGetToken(ep, state); + if (sp->tid != EJS_TOK_ID && sp->tid != EJS_TOK_VAR) { + ejsSyntaxError(ep, 0); + goto err; + } + ejsLexPutbackToken(ep, sp->tid, ep->token); + + state = ejsParse(ep, EJS_STATE_EXPR, EJS_FLAGS_FORIN | flags); + if (state < 0) { + goto done; + } + if (flags & EJS_FLAGS_EXE) { + if (ep->currentProperty == 0) { + ejsSyntaxError(ep, 0); + goto err; + } + sp->iteratorVar = &ep->currentProperty->var; + } else { + sp->iteratorVar = 0; + } + + if (ejsLexGetToken(ep, state) != EJS_TOK_IN) { + ejsSyntaxError(ep, 0); + goto err; + } + + /* + * Get the set + */ + sp->tid = ejsLexGetToken(ep, state); + if (sp->tid != EJS_TOK_ID) { + ejsSyntaxError(ep, 0); + goto err; + } + ejsLexPutbackToken(ep, sp->tid, ep->token); + + state = ejsParse(ep, EJS_STATE_EXPR, flags); + if (state < 0) { + goto done; + } + + if ((flags & EJS_FLAGS_EXE) && + (ep->result == 0 || ep->result->type == EJS_TYPE_UNDEFINED)) { + ejsError(ep, EJS_REFERENCE_ERROR, "Can't access array or object"); + goto err; + } + + if (ejsLexGetToken(ep, state) != EJS_TOK_RPAREN) { + ejsSyntaxError(ep, 0); + goto err; + } + + sp->setVar = ejsDupVar(ep, ep->result, EJS_SHALLOW_COPY); + + sp->bodyScript = getInputStruct(ep); + + /* + * Parse the body and remember the end of the body script + */ + sp->forFlags = flags & ~EJS_FLAGS_EXE; + ejsLexSaveInputState(ep, sp->bodyScript); + + state = ejsParse(ep, EJS_STATE_STMT, sp->forFlags); + if (state < 0) { + goto done; + } + + sp->endScript = getInputStruct(ep); + ejsInitInputState(sp->endScript); + ejsLexSaveInputState(ep, sp->endScript); + + /* + * Enumerate the properties + */ + if (flags & EJS_FLAGS_EXE) { + if (sp->setVar->type == EJS_TYPE_OBJECT) { + + sp->setVar->objectState->preventDeleteProp = 1; + + sp->pp = ejsGetFirstProperty(sp->setVar, 0); + while (sp->pp) { + sp->nextp = ejsGetNextProperty(sp->pp, 0); + if (! sp->pp->dontEnumerate && !sp->pp->delayedDelete) { + if (each) { + sp->vp = ejsWriteVar(ep, sp->iteratorVar, + ejsGetVarPtr(sp->pp), EJS_SHALLOW_COPY); + } else { + sp->vp = ejsWriteVarAsString(ep, sp->iteratorVar, + sp->pp->name); + } + if (sp->vp == 0) { + ejsError(ep, EJS_MEMORY_ERROR, + "Can't write to variable"); + goto err; + } + + ejsLexRestoreInputState(ep, sp->bodyScript); + + state = ejsParse(ep, EJS_STATE_STMT, flags); + + if (state < 0) { + if (sp->setVar->objectState) { + sp->setVar->objectState->preventDeleteProp = 0; + } + goto done; + } + } + sp->pp = sp->nextp; + } + + /* + * Process delayed deletes + */ + if (sp->setVar->objectState) { + sp->setVar->objectState->preventDeleteProp = 0; + if (sp->setVar->objectState->delayedDeleteProp) { + sp->pp = ejsGetFirstProperty(sp->setVar, 0); + while (sp->pp) { + sp->nextp = ejsGetNextProperty(sp->pp, 0); + if (sp->pp->delayedDelete) { + ejsDeleteProperty(ep, sp->setVar, sp->pp->name); + } + sp->pp = sp->nextp; + } + sp->setVar->objectState->delayedDeleteProp = 0; + } + } + + } else { + ejsError(ep, EJS_REFERENCE_ERROR, + "Variable to iterate over is not an array or object"); + goto err; + } + } + + ejsLexRestoreInputState(ep, sp->endScript); + +done: + if (sp->endScript) { + ejsLexFreeInputState(ep, sp->endScript); + ejsLexFreeInputState(ep, sp->bodyScript); + } + + if (sp->bodyScript) { + freeInputStruct(ep, sp->bodyScript); + } + if (sp->endScript) { + freeInputStruct(ep, sp->endScript); + } + + if (sp->setVar) { + ejsFreeVar(ep, sp->setVar); + } + + popFrame(ep, sizeof(ParseForIn)); + + return state; + +err: + state = EJS_STATE_ERR; + goto done; +} + +/******************************************************************************/ +/* + * Parse the for statement. Format for the expression is: + * + * for (initial; condition; incr) { + * body; + * } + */ + +static int parseRegFor(Ejs *ep, int state, int flags) +{ + EjsInput *condScript, *endScript, *bodyScript, *incrScript; + + endScript = getInputStruct(ep); + bodyScript = getInputStruct(ep); + incrScript = getInputStruct(ep); + condScript = getInputStruct(ep); + + ejsInitInputState(endScript); + ejsInitInputState(bodyScript); + ejsInitInputState(incrScript); + ejsInitInputState(condScript); + + state = parseForInner(ep, state, flags, + condScript, incrScript, bodyScript, endScript); + + ejsLexFreeInputState(ep, condScript); + ejsLexFreeInputState(ep, incrScript); + ejsLexFreeInputState(ep, endScript); + ejsLexFreeInputState(ep, bodyScript); + + freeInputStruct(ep, condScript); + freeInputStruct(ep, incrScript); + freeInputStruct(ep, endScript); + freeInputStruct(ep, bodyScript); + + return state; +} + +/******************************************************************************/ + +static int parseForInner(Ejs *ep, int state, int flags, EjsInput *condScript, + EjsInput *incrScript, EjsInput *bodyScript, EjsInput *endScript) +{ + int forFlags, cond, rs; + + mprAssert(ep); + + /* + * Evaluate the for loop initialization statement + */ + if ((state = ejsParse(ep, EJS_STATE_STMT, flags)) < 0) { + return state; + } + + /* + * The first time through, we save the current input context just prior + * to each step: prior to the conditional, the loop increment and + * the loop body. + */ + ejsLexSaveInputState(ep, condScript); + if ((rs = ejsParse(ep, EJS_STATE_COND, flags)) < 0) { + return rs; + } + + cond = (ep->result->boolean != 0); + + if (ejsLexGetToken(ep, state) != EJS_TOK_SEMI) { + ejsSyntaxError(ep, 0); + return EJS_STATE_ERR; + } + + /* + * Don't execute the loop increment statement or the body + * first time. + */ + forFlags = flags & ~EJS_FLAGS_EXE; + ejsLexSaveInputState(ep, incrScript); + if ((rs = ejsParse(ep, EJS_STATE_EXPR, forFlags)) < 0) { + return rs; + } + + if (ejsLexGetToken(ep, state) != EJS_TOK_RPAREN) { + ejsSyntaxError(ep, 0); + return EJS_STATE_ERR; + } + + /* + * Parse the body and remember the end of the body script + */ + ejsLexSaveInputState(ep, bodyScript); + if ((rs = ejsParse(ep, EJS_STATE_STMT, forFlags)) < 0) { + return rs; + } + ejsLexSaveInputState(ep, endScript); + + /* + * Now actually do the for loop. Note loop has been rotated + */ + while (cond && (flags & EJS_FLAGS_EXE)) { + /* + * Evaluate the body + */ + ejsLexRestoreInputState(ep, bodyScript); + + if ((rs = ejsParse(ep, EJS_STATE_STMT, flags)) < 0) { + return rs; + } + + /* + * Evaluate the increment script + */ + ejsLexRestoreInputState(ep, incrScript); + if ((rs = ejsParse(ep, EJS_STATE_EXPR, flags)) < 0) { + return rs; + } + /* + * Evaluate the condition + */ + ejsLexRestoreInputState(ep, condScript); + if ((rs = ejsParse(ep, EJS_STATE_COND, flags)) < 0) { + return 0; + } + mprAssert(ep->result->type == EJS_TYPE_BOOL); + cond = (ep->result->boolean != 0); + } + + ejsLexRestoreInputState(ep, endScript); + + return state; +} + +/******************************************************************************/ +/* + * Create the bare class object + */ + +static int createClass(Ejs *ep, EjsVar *obj, const char *className, + EjsVar *baseClass) +{ + EjsVar *classObj, *existingClass; + + existingClass = ejsGetClass(ep, obj, className); + if (existingClass) { + /* + * We allow partial clases and method redefinition + * FUTURE -- should prevent this if the class is sealed. + * DISABLED Error message and return OK. + */ + /* ejsError(ep, EJS_EVAL_ERROR, "Can't create class %s", className); */ + return 0; + } + + if (baseClass == 0) { + baseClass = ejsGetClass(ep, ep->service->globalClass, "Object"); + mprAssert(baseClass); + } + + classObj = ejsCreateSimpleClass(ep, baseClass, className); + if (classObj == 0) { + ejsMemoryError(ep); + return -1; + } + mprAssert(! ejsObjIsCollectable(classObj)); + + ep->currentProperty = ejsSetPropertyAndFree(ep, obj, className, classObj); + mprAssert(ep->currentProperty); + + if (ep->currentProperty == 0) { + return -1; + } + + return 0; +} + +/******************************************************************************/ +/* + * Local vars for parseTry + */ + +typedef struct ParseTry { + EjsVar *exception; + int tid, caught, rs, catchFlags; +} ParseTry; + +/* + * Parse try block + * + * try {} + */ + +static int parseTry(Ejs *ep, int state, int flags) +{ + ParseTry *sp; + + if ((sp = pushFrame(ep, sizeof(ParseTry))) == 0) { + return EJS_STATE_ERR; + } + + mprAssert(ep); + + sp->caught = 0; + sp->exception = 0; + sp->catchFlags = flags; + + /* + * Execute the code in the try block + */ + sp->rs = ejsParse(ep, EJS_STATE_STMT, flags | EJS_FLAGS_TRY); + if (sp->rs < 0) { + if (sp->rs == EJS_STATE_ERR) { + sp->exception = ejsDupVar(ep, ep->result, EJS_SHALLOW_COPY); + if (sp->exception == 0) { + ejsMemoryError(ep); + goto err; + } + } else { + state = sp->rs; + goto done; + } + + } else { + sp->catchFlags = flags & ~EJS_FLAGS_EXE; + } + + /* + * On success path or when an exception is caught, we must parse all + * catch and finally blocks. + */ + sp->tid = getNextNonSpaceToken(ep, state); + + if (sp->tid == EJS_TOK_CATCH) { + + ep->gotException = 0; + + sp->tid = getNextNonSpaceToken(ep, state); + + if (sp->tid == EJS_TOK_LBRACE) { + /* + * Unqualified "catch " + */ + ejsLexPutbackToken(ep, sp->tid, ep->token); + if (ejsParse(ep, EJS_STATE_STMT, sp->catchFlags) >= 0) { + sp->caught++; + } + + } else if (sp->tid == EJS_TOK_LPAREN) { + + /* + * Qualified "catch (variable) " + */ + if ((sp->rs = ejsParse(ep, EJS_STATE_DEC_LIST, + sp->catchFlags | EJS_FLAGS_CATCH)) < 0) { + ejsSyntaxError(ep, "Bad catch statement"); + state = sp->rs; + goto done; + } + + sp->tid = getNextNonSpaceToken(ep, state); + if (sp->tid != EJS_TOK_RPAREN) { + ejsSyntaxError(ep, 0); + goto err; + } + + if (sp->catchFlags & EJS_FLAGS_EXE) { + if (ep->currentProperty == 0) { + ejsError(ep, EJS_EVAL_ERROR, "Can't define catch variable"); + goto err; + } + + /* + * Set the catch variable + */ + if (ejsWriteVar(ep, + ejsGetVarPtr(ep->currentProperty), sp->exception, + EJS_SHALLOW_COPY) == 0) { + ejsError(ep, EJS_EVAL_ERROR, "Can't update catch variable"); + goto err; + } + } + + /* + * Parse the catch block + */ + if ((sp->rs = ejsParse(ep, EJS_STATE_STMT, sp->catchFlags)) < 0) { + state = sp->rs; + goto done; + } + sp->caught++; + ep->gotException = 0; + } + sp->tid = getNextNonSpaceToken(ep, state); + } + + /* + * Parse the finally block + */ + if (sp->tid == EJS_TOK_FINALLY) { + if (ejsParse(ep, EJS_STATE_STMT, flags) < 0) { + goto err; + } + } else { + ejsLexPutbackToken(ep, sp->tid, ep->token); + } + + /* + * Set the exception value + */ + if (sp->exception && !sp->caught) { + ejsWriteVar(ep, ep->result, sp->exception, EJS_SHALLOW_COPY); + goto err; + } + + state = EJS_STATE_STMT_DONE; + +done: + if (sp->exception) { + ejsFreeVar(ep, sp->exception); + } + + popFrame(ep, sizeof(ParseTry)); + return state; + + +err: + state = EJS_STATE_ERR; + goto done; +} + +/******************************************************************************/ +/* + * Parse throw statement + * + * throw expression + */ + +static int parseThrow(Ejs *ep, int state, int flags) +{ + int rc; + + mprAssert(ep); + + if ((rc = ejsParse(ep, EJS_STATE_EXPR, flags)) < 0) { + return rc; + } + + + if (flags & EJS_FLAGS_EXE) { + /* + * We have thrown the exception so set the state to ERR + */ + ep->gotException = 1; + return EJS_STATE_ERR; + } + return state; +} + +/******************************************************************************/ +/* + * Parse a class and module declaration + * + * class <name> [extends baseClass] { + * [public | private | ... ] var declarations ... + * [constructor] function declarations ... + * } + * + * Modules are identical except declared with a "module" instead of + * "class". Modules cannot be instantiated and are used for mixins. + * + */ + +static int parseClass(Ejs *ep, int state, int flags) +{ + int originalToken, tid, fid; + + mprAssert(ep); + + originalToken = ep->tid; + + /* + * Parse "class Name [extends BaseClass]" + */ + if (ejsParse(ep, EJS_STATE_DEC_LIST, flags | EJS_FLAGS_CLASS_DEC) < 0) { + return EJS_STATE_ERR; + } + + tid = getNextNonSpaceToken(ep, state); + + if (tid != EJS_TOK_LBRACE) { + return EJS_STATE_ERR; + } + + /* + * After parsing the class body, ep->local will contain the actual + * class/module object. So, we save ep->local by creating a new block. + */ + if (flags & EJS_FLAGS_EXE) { + fid = ejsSetBlock(ep, ejsGetVarPtr(ep->currentProperty)); + ejsSetVarName(ep, ep->local, ep->currentProperty->name); + + } else { + fid = -1; + } + + /* FUTURE -- should prevent modules from being instantiated */ + + /* + * Parse class body + */ + do { + state = ejsParse(ep, EJS_STATE_STMT, flags); + if (state < 0) { + if (fid >= 0) { + ejsCloseBlock(ep, fid); + } + return state; + } + tid = getNextNonSpaceToken(ep, state); + if (tid == EJS_TOK_RBRACE) { + break; + } + ejsLexPutbackToken(ep, tid, ep->token); + + } while (state >= 0); + + if (fid >= 0) { + ejsCloseBlock(ep, fid); + } + + if (tid != EJS_TOK_RBRACE) { + ejsSyntaxError(ep, 0); + state = EJS_STATE_ERR; + } + + return state; +} + +/******************************************************************************/ +/* + * Parse a function declaration + */ + +static int parseFunction(Ejs *ep, int state, int flags) +{ + EjsInput *endScript, *bodyScript; + EjsProperty *pp; + EjsVar *func, *funcProp, *currentObj, *vp, *baseClass; + char *procName; + int varFlags, len, tid, bodyFlags, innerState; + + mprAssert(ep); + + func = 0; + varFlags = 0; + + /* + * method <name>(arg, arg, arg) { body }; + * method name(arg, arg, arg) { body }; + */ + + tid = ejsLexGetToken(ep, state); + + if (tid == EJS_TOK_GET) { + varFlags |= EJS_GET_ACCESSOR; + tid = ejsLexGetToken(ep, state); + + } else if (tid == EJS_TOK_SET) { + varFlags |= EJS_SET_ACCESSOR; + tid = ejsLexGetToken(ep, state); + } + + if (tid == EJS_TOK_ID) { + if (varFlags & EJS_SET_ACCESSOR) { + + if (mprAllocStrcat(MPR_LOC_ARGS(ep), &procName, EJS_MAX_ID + 5, + 0, "-set-", ep->token, 0) < 0) { + ejsError(ep, EJS_SYNTAX_ERROR, + "Name %s is too long", ep->token); + return EJS_STATE_ERR; + } + + } else { + procName = mprStrdup(ep, ep->token); + } + + tid = ejsLexGetToken(ep, state); + + } else { + procName = 0; + } + + if (tid != EJS_TOK_LPAREN) { + mprFree(procName); + ejsSyntaxError(ep, 0); + return EJS_STATE_ERR; + } + + /* + * Hand craft the method value structure. + */ + if (flags & EJS_FLAGS_EXE) { + func = ejsCreateMethodVar(ep, 0, 0, 0); + if (func == 0) { + mprFree(procName); + ejsMemoryError(ep); + return EJS_STATE_ERR; + } + func->flags = varFlags; + } + + tid = ejsLexGetToken(ep, state); + while (tid == EJS_TOK_ID) { + if (flags & EJS_FLAGS_EXE) { + mprAddItem(func->method.args, + mprStrdup(func->method.args, ep->token)); + } + tid = ejsLexGetToken(ep, state); + if (tid == EJS_TOK_RPAREN || tid != EJS_TOK_COMMA) { + break; + } + tid = ejsLexGetToken(ep, state); + } + if (tid != EJS_TOK_RPAREN) { + mprFree(procName); + ejsFreeVar(ep, func); + ejsSyntaxError(ep, 0); + return EJS_STATE_ERR; + } + + /* Allow new lines before opening brace */ + do { + tid = ejsLexGetToken(ep, state); + } while (tid == EJS_TOK_NEWLINE); + + if (tid != EJS_TOK_LBRACE) { + mprFree(procName); + ejsFreeVar(ep, func); + ejsSyntaxError(ep, 0); + return EJS_STATE_ERR; + } + + /* + * Register the method name early to allow for recursive + * method calls (see note in ECMA standard, page 71) + */ + funcProp = 0; + if (flags & EJS_FLAGS_EXE && procName) { + currentObj = pickSpace(ep, 0, procName, flags | EJS_FLAGS_LOCAL); + pp = ejsSetProperty(ep, currentObj, procName, func); + if (pp == 0) { + ejsFreeVar(ep, func); + ejsMemoryError(ep); + return EJS_STATE_ERR; + } + funcProp = ejsGetVarPtr(pp); + } + + + bodyScript = getInputStruct(ep); + + /* + * Parse the method body. Turn execute off. + */ + bodyFlags = flags & ~EJS_FLAGS_EXE; + ejsLexSaveInputState(ep, bodyScript); + + do { + innerState = ejsParse(ep, EJS_STATE_STMT, bodyFlags); + } while (innerState == EJS_STATE_STMT_DONE); + + tid = ejsLexGetToken(ep, state); + + if (innerState != EJS_STATE_STMT_BLOCK_DONE || tid != EJS_TOK_RBRACE) { + mprFree(procName); + ejsFreeVar(ep, func); + ejsLexFreeInputState(ep, bodyScript); + if (innerState != EJS_STATE_ERR) { + ejsSyntaxError(ep, 0); + } + freeInputStruct(ep, bodyScript); + return EJS_STATE_ERR; + } + + if (flags & EJS_FLAGS_EXE) { + endScript = getInputStruct(ep); + ejsLexSaveInputState(ep, endScript); + + /* + * Save the method body between the starting and ending parse + * positions. Overwrite the trailing '}' with a null. + */ + len = endScript->scriptServp - bodyScript->scriptServp; + func->method.body = mprAlloc(ep, len + 1); + memcpy(func->method.body, bodyScript->scriptServp, len); + + if (len <= 0) { + func->method.body[0] = '\0'; + } else { + func->method.body[len - 1] = '\0'; + } + ejsLexFreeInputState(ep, bodyScript); + ejsLexFreeInputState(ep, endScript); + freeInputStruct(ep, endScript); + + /* + * If we are in an assignment, don't register the method name, rather + * return the method structure in the parser result. + */ + if (procName) { + currentObj = pickSpace(ep, 0, procName, flags | EJS_FLAGS_LOCAL); + pp = ejsSetProperty(ep, currentObj, procName, func); + if (pp == 0) { + ejsFreeVar(ep, func); + ejsMemoryError(ep); + return EJS_STATE_ERR; + } + + if (currentObj->objectState->className && + strcmp(currentObj->objectState->className, procName) == 0) { + baseClass = currentObj->objectState->baseClass; + if (baseClass) { + if (strstr(func->method.body, "super(") != 0) { + funcProp->callsSuper = 1; + /* + * Define super() to point to the baseClass constructor + */ + vp = ejsGetPropertyAsVar(ep, baseClass, + baseClass->objectState->className); + if (vp) { + mprAssert(vp); + if (ejsSetProperty(ep, currentObj, "super", + vp) == 0) { + ejsFreeVar(ep, func); + ejsMemoryError(ep); + return EJS_STATE_ERR; + } + } + } + } + } + } + /* + * Always return the function. Try for all stmts to be expressions. + */ + /* MOB - rc */ + ejsWriteVar(ep, ep->result, func, EJS_SHALLOW_COPY); + } + freeInputStruct(ep, bodyScript); + + mprFree(procName); + ejsFreeVar(ep, func); + + return state; +} + +/******************************************************************************/ +/* + * Local vars + */ + +typedef struct ParseMethod { + EjsProc proc, *saveProc; + EjsVar *saveObj, *newObj; + int saveObjPerm, rc; + +} ParseMethod; + +/* + * Parse a method name and invoke the method. See parseFunction for + * function declarations. + */ + +static int parseMethod(Ejs *ep, int state, int flags, char *id) +{ + ParseMethod *sp; + + if ((sp = pushFrame(ep, sizeof(ParseMethod))) == 0) { + return EJS_STATE_ERR; + } + + /* + * Must save any current ep->proc value for the current stack frame + * to allow for recursive method calls. + */ + sp->saveProc = (ep->proc) ? ep->proc: 0; + + memset(&sp->proc, 0, sizeof(EjsProc)); + sp->proc.procName = mprStrdup(ep, id); + sp->proc.fn = &ep->currentProperty->var; + sp->proc.args = mprCreateItemArray(ep, EJS_INC_ARGS, EJS_MAX_ARGS); + ep->proc = &sp->proc; + +#if BLD_DEBUG + if (strcmp(sp->proc.procName, "printv") == 0) { + flags |= EJS_FLAGS_TRACE_ARGS; + } +#endif + + if (flags & EJS_FLAGS_EXE) { + ejsClearVar(ep, ep->result); + } + + if (! (flags & EJS_FLAGS_NO_ARGS)) { + sp->saveObj = ep->currentObj; + sp->saveObjPerm = ejsMakeObjPermanent(sp->saveObj, 1); + sp->rc = ejsParse(ep, EJS_STATE_ARG_LIST, flags); + ejsMakeObjPermanent(sp->saveObj, sp->saveObjPerm); + if (sp->rc < 0) { + goto err; + } + ep->currentObj = sp->saveObj; + } + +#if BLD_DEBUG + flags &= ~EJS_FLAGS_TRACE_ARGS; +#endif + + /* + * Evaluate the method if required + */ + if (flags & EJS_FLAGS_EXE) { + if (flags & EJS_FLAGS_NEW) { + sp->newObj = ejsCreateObjUsingArgv(ep, ep->currentObj, + sp->proc.procName, sp->proc.args); + + if (sp->newObj == 0) { + state = EJS_STATE_ERR; + + } else { + mprAssert(! ejsObjIsCollectable(sp->newObj)); + + /* + * Return the newly created object as the result of the + * command. NOTE: newObj may not be an object! + */ + /* MOB - rc */ + ejsWriteVar(ep, ep->result, sp->newObj, EJS_SHALLOW_COPY); + if (ejsVarIsObject(sp->newObj)) { + ejsMakeObjLive(sp->newObj, 1); + mprAssert(ejsObjIsCollectable(sp->newObj)); + mprAssert(ejsBlockInUse(sp->newObj)); + } + ejsFreeVar(ep, sp->newObj); + } + + } else { + + if (evalMethod(ep, ep->currentObj, &sp->proc, flags) < 0) { + /* Methods must call ejsError to set exceptions */ + state = EJS_STATE_ERR; + } + } + } + + if (! (flags & EJS_FLAGS_NO_ARGS)) { + if (ejsLexGetToken(ep, state) != EJS_TOK_RPAREN) { + if (state != EJS_STATE_ERR) { + ejsSyntaxError(ep, 0); + } + state = EJS_STATE_ERR; + } + } + +done: + freeProc(ep, &sp->proc); + ep->proc = sp->saveProc; + + popFrame(ep, sizeof(ParseMethod)); + return state; + +err: + state = EJS_STATE_ERR; + goto done; +} + +/******************************************************************************/ +/* + * Parse an identifier. This is a segment of a fully qualified variable. + * May come here for an initial identifier or for property names + * after a "." or "[...]". + */ + +static int parseId(Ejs *ep, int state, int flags, char **id, int *done) +{ + EjsVar *null; + int tid; + + mprFree(*id); + *id = mprStrdup(ep, ep->token); + + if (ep->currentObj == 0) { + /* First identifier segement */ + ep->currentObj = pickSpace(ep, state, *id, flags); + } + + tid = ejsLexGetToken(ep, state); + if (tid == EJS_TOK_ASSIGNMENT) { + flags |= EJS_FLAGS_LHS; + } + + /* + * Find the referenced variable and store it in currentProperty. + */ + if (flags & EJS_FLAGS_EXE) { + ep->currentProperty = searchSpacesForProperty(ep, state, + ep->currentObj, *id, flags); + + /* + * Handle properties that have been deleted inside an enumeration + */ + if (ep->currentProperty && ep->currentProperty->delayedDelete) { + ep->currentProperty = 0; + } + + if (ep->currentProperty && + ejsVarIsMethod(&ep->currentProperty->var) && + tid != EJS_TOK_LPAREN) { + if (ep->currentProperty->var.flags & EJS_GET_ACCESSOR) { + ejsLexPutbackToken(ep, tid, ep->token); + state = parseMethod(ep, state, flags | EJS_FLAGS_NO_ARGS, *id); + if (ep->flags & EJS_FLAGS_EXIT) { + state = EJS_STATE_RET; + } + if (state >= 0) { + ejsSetVarName(ep, ep->result, ep->currentProperty->name); + } + return state; + } + } + /* + * OPT. We should not have to do this always + */ + updateResult(ep, state, flags, ejsGetVarPtr(ep->currentProperty)); + } + + flags &= ~EJS_FLAGS_LHS; + + if (tid == EJS_TOK_LPAREN) { + if (ep->currentProperty == 0 && (flags & EJS_FLAGS_EXE)) { + ejsError(ep, EJS_REFERENCE_ERROR, + "Method name not defined \"%s\"", *id); + return EJS_STATE_ERR; + } + ejsLexPutbackToken(ep, EJS_TOK_METHOD_NAME, ep->token); + return state; + } + + if (tid == EJS_TOK_PERIOD || tid == EJS_TOK_LBRACKET || + tid == EJS_TOK_ASSIGNMENT || tid == EJS_TOK_INC_DEC) { + ejsLexPutbackToken(ep, tid, ep->token); + return state; + } + + if (flags & EJS_FLAGS_CLASS_DEC) { + if (tid == EJS_TOK_LBRACE || tid == EJS_TOK_EXTENDS) { + ejsLexPutbackToken(ep, tid, ep->token); + return state; + } + } + + if (flags & EJS_FLAGS_DELETE) { + if (tid == EJS_TOK_RBRACE) { + ejsLexPutbackToken(ep, tid, ep->token); + } + } + + /* + * Only come here for variable access and declarations. + * Assignment handled elsewhere. + */ + if (flags & EJS_FLAGS_EXE) { + if (state == EJS_STATE_DEC) { + /* + * Declare a variable. Standard allows: var x ; var x ; + */ +#if DISABLED + if (ep->currentProperty != 0) { + ejsError(ep, EJS_REFERENCE_ERROR, + "Variable already defined \"%s\"", *id); + return EJS_STATE_ERR; + } +#endif + /* + * Create or overwrite if it already exists + * Set newly declared variables to the null value. + */ + null = ejsCreateNullVar(ep); + ep->currentProperty = ejsSetPropertyAndFree(ep, ep->currentObj, + *id, null); + ejsClearVar(ep, ep->result); + + } else if (flags & EJS_FLAGS_FORIN) { + /* + * This allows "for (x" when x has not yet been defined + */ + if (ep->currentProperty == 0) { + /* MOB -- return code */ + ep->currentProperty = ejsCreateProperty(ep, + ep->currentObj, *id); + } + + } else if (ep->currentProperty == 0) { + + if (ep->currentObj && ((ep->currentObj == ep->global || + (ep->currentObj == ep->local)))) { + /* + * Test against currentObj and not currentObj->objectState + * as we must allow "i = global.x" and not allow + * "i = x" where x does not exist. + */ + ejsError(ep, EJS_REFERENCE_ERROR, + "Undefined variable \"%s\"", *id); + return EJS_STATE_ERR; + } + + if (flags & EJS_FLAGS_DELETE) { + ejsError(ep, EJS_REFERENCE_ERROR, + "Undefined variable \"%s\"", *id); + return EJS_STATE_ERR; + } + } + } + ejsLexPutbackToken(ep, tid, ep->token); + if (tid == EJS_TOK_RBRACKET || tid == EJS_TOK_COMMA || + tid == EJS_TOK_IN) { + *done = 1; + } + return state; +} + +/******************************************************************************/ +/* + * Local vars + */ + +typedef struct ParseIf { + int ifResult, thenFlags, elseFlags, tid, rs; +} ParseIf; + +/* + * Parse an "if" statement + */ + +static int parseIf(Ejs *ep, int state, int flags, int *done) +{ + ParseIf *sp; + + if ((sp = pushFrame(ep, sizeof(ParseIf))) == 0) { + return EJS_STATE_ERR; + } + + if (state != EJS_STATE_STMT) { + goto err; + } + if (ejsLexGetToken(ep, state) != EJS_TOK_LPAREN) { + goto err; + } + + /* + * Evaluate the entire condition list "(condition)" + */ + if (ejsParse(ep, EJS_STATE_COND, flags) < 0) { + goto err; + } + if (ejsLexGetToken(ep, state) != EJS_TOK_RPAREN) { + goto err; + } + + /* + * This is the "then" case. We need to always parse both cases and + * execute only the relevant case. + */ + sp->ifResult = ejsVarToBoolean(ep->result); + if (sp->ifResult) { + sp->thenFlags = flags; + sp->elseFlags = flags & ~EJS_FLAGS_EXE; + } else { + sp->thenFlags = flags & ~EJS_FLAGS_EXE; + sp->elseFlags = flags; + } + + /* + * Process the "then" case. + */ + if ((sp->rs = ejsParse(ep, EJS_STATE_STMT, sp->thenFlags)) < 0) { + if (! ep->gotException) { + state = sp->rs; + goto done; + } + } + + /* + * Check to see if there is an "else" case + */ + removeNewlines(ep, state); + sp->tid = ejsLexGetToken(ep, state); + if (sp->tid != EJS_TOK_ELSE) { + ejsLexPutbackToken(ep, sp->tid, ep->token); + *done = 1; + if (ep->gotException) { + goto err; + } + goto done; + } + + /* + * Process the "else" case. + */ + state = ejsParse(ep, EJS_STATE_STMT, sp->elseFlags); + +done: + *done = 1; + if (ep->gotException) { + state = EJS_STATE_ERR; + } + popFrame(ep, sizeof(ParseIf)); + return state; + + +err: + state = EJS_STATE_ERR; + goto done; +} + +/******************************************************************************/ +/* + * Parse a postix "++" or "--" statement + */ + +static int parseInc(Ejs *ep, int state, int flags) +{ + EjsVar *one; + + if (! (flags & EJS_FLAGS_EXE)) { + return state; + } + + if (ep->currentProperty == 0) { + ejsError(ep, EJS_REFERENCE_ERROR, + "Undefined variable \"%s\"", ep->token); + return EJS_STATE_ERR; + } + one = ejsCreateIntegerVar(ep, 1); + if (evalExpr(ep, &ep->currentProperty->var, (int) *ep->token, one) < 0) { + ejsFreeVar(ep, one); + return EJS_STATE_ERR; + } + if (ejsWriteVar(ep, &ep->currentProperty->var, ep->result, + EJS_SHALLOW_COPY) < 0) { + ejsError(ep, EJS_IO_ERROR, "Can't write to variable"); + ejsFreeVar(ep, one); + return EJS_STATE_ERR; + } + ejsFreeVar(ep, one); + return state; +} + +/******************************************************************************/ +/* + * Evaluate a condition. Implements &&, ||, !. Returns with a boolean result + * in ep->result. Returns EJS_STATE_ERR on errors, zero if successful. + */ + +static int evalCond(Ejs *ep, EjsVar *lhs, int rel, EjsVar *rhs) +{ + int l, r, lval; + + mprAssert(rel > 0); + + l = ejsVarToBoolean(lhs); + r = ejsVarToBoolean(rhs); + + switch (rel) { + case EJS_COND_AND: + lval = l && r; + break; + case EJS_COND_OR: + lval = l || r; + break; + default: + ejsError(ep, EJS_SYNTAX_ERROR, "Bad operator %d", rel); + return -1; + } + + /* MOB - rc */ + ejsWriteVarAsBoolean(ep, ep->result, lval); + return 0; +} + + +/******************************************************************************/ +/* + * return true if this string is a valid number + */ + +static int stringIsNumber(const char *s) +{ + char *endptr = NULL; + + if (s == NULL || *s == 0) { + return 0; + } + /* MOB -- not ideal */ +#if BREW + /* MOB this should check all digits and not just the first. */ + /* Does not support floating point - easy */ + + if (isdigit(*s) || (*s == '-' && isdigit(s[1]))) { + return 1; + } +#else + strtod(s, &endptr); +#endif + if (endptr != NULL && *endptr == 0) { + return 1; + } + return 0; +} + +/******************************************************************************/ +/* + * Evaluate an operation. Returns with the result in ep->result. Returns -1 + * on errors, otherwise zero is returned. + */ + +static int evalExpr(Ejs *ep, EjsVar *lhs, int rel, EjsVar *rhs) +{ + EjsNum lval; + char *str; + int rc; + + mprAssert(rel > 0); + str = 0; + lval = 0; + + /* + * Type conversion. This is tricky and must be according to the standard. + * Only numbers (including floats) and strings can be compared. All other + * types are first converted to numbers by preference and if that fails, + * to strings. + * + * MOB -- should we do "valueOf" here also. + */ + if (lhs->type == EJS_TYPE_OBJECT && + (rhs->type != EJS_TYPE_OBJECT && + (rhs->type != EJS_TYPE_UNDEFINED && rhs->type != EJS_TYPE_NULL))) { + if (ejsVarIsNumber(rhs)) { + if (ejsRunMethod(ep, lhs, "toValue", 0) == 0) { + /* MOB - rc */ + ejsWriteVar(ep, lhs, ep->result, EJS_SHALLOW_COPY); + } else { + if (ejsRunMethod(ep, lhs, "toString", 0) == 0) { + /* MOB - rc */ + ejsWriteVar(ep, lhs, ep->result, EJS_SHALLOW_COPY); + } + } + + } else { + if (ejsRunMethod(ep, lhs, "toString", 0) == 0) { + /* MOB - rc */ + ejsWriteVar(ep, lhs, ep->result, EJS_SHALLOW_COPY); + } else { + if (ejsRunMethod(ep, lhs, "toValue", 0) == 0) { + /* MOB - rc */ + ejsWriteVar(ep, lhs, ep->result, EJS_SHALLOW_COPY); + } + } + } + /* Nothing more can be done */ + } + + if (rhs->type == EJS_TYPE_OBJECT && + (lhs->type != EJS_TYPE_OBJECT && + (lhs->type != EJS_TYPE_UNDEFINED && lhs->type != EJS_TYPE_NULL))) { + if (ejsVarIsNumber(lhs)) { + /* If LHS is number, then convert to a value first */ + if (ejsRunMethod(ep, rhs, "toValue", 0) == 0) { + /* MOB - rc */ + ejsWriteVar(ep, rhs, ep->result, EJS_SHALLOW_COPY); + } else { + if (ejsRunMethod(ep, rhs, "toString", 0) == 0) { + /* MOB - rc */ + ejsWriteVar(ep, rhs, ep->result, EJS_SHALLOW_COPY); + } + } + + } else { + /* If LHS is not a number, then convert to a string first */ + if (ejsRunMethod(ep, rhs, "toString", 0) == 0) { + /* MOB - rc */ + ejsWriteVar(ep, rhs, ep->result, EJS_SHALLOW_COPY); + + } else { + if (ejsRunMethod(ep, rhs, "toValue", 0) == 0) { + /* MOB - rc */ + ejsWriteVar(ep, rhs, ep->result, EJS_SHALLOW_COPY); + } + } + } + /* Nothing more can be done */ + } + + /* + * undefined and null are special, in that they don't get promoted when + * comparing. + */ + if (rel == EJS_EXPR_EQ || rel == EJS_EXPR_NOTEQ) { + if (lhs->type == EJS_TYPE_UNDEFINED || + rhs->type == EJS_TYPE_UNDEFINED) { + return evalBoolExpr(ep, + lhs->type == EJS_TYPE_UNDEFINED, + rel, + rhs->type == EJS_TYPE_UNDEFINED); + } + + if (lhs->type == EJS_TYPE_NULL || rhs->type == EJS_TYPE_NULL) { + return evalBoolExpr(ep, + lhs->type == EJS_TYPE_NULL, + rel, + rhs->type == EJS_TYPE_NULL); + } + } + + /* + * From here on, lhs and rhs may contain allocated data (strings), so + * we must always destroy before overwriting. + */ + + /* + * Only allow a few bool operations. Otherwise convert to number. + */ + if (lhs->type == EJS_TYPE_BOOL && rhs->type == EJS_TYPE_BOOL && + (rel != EJS_EXPR_EQ && rel != EJS_EXPR_NOTEQ && + rel != EJS_EXPR_BOOL_COMP)) { + ejsWriteVarAsNumber(ep, lhs, ejsVarToNumber(lhs)); + } + + /* + * Types do not match, so try to coerce the right operand to match the left + * But first, try to convert a left operand that is a numeric stored as a + * string, into a numeric. + */ + if (lhs->type != rhs->type) { + if (lhs->type == EJS_TYPE_STRING) { + if (stringIsNumber(lhs->string)) { + ejsWriteVarAsNumber(ep, lhs, ejsVarToNumber(lhs)); + + /* Examine further below */ + + } else { + /* + * Convert the RHS to a string + * MOB rc + */ + str = ejsVarToString(ep, rhs); + ejsWriteVarAsString(ep, rhs, str); + } + +#if BLD_FEATURE_FLOATING_POINT + } else if (lhs->type == EJS_TYPE_FLOAT) { + /* + * Convert rhs to floating + */ + ejsWriteVarAsFloat(ep, rhs, ejsVarToFloat(rhs)); + +#endif +#if BLD_FEATURE_INT64 + } else if (lhs->type == EJS_TYPE_INT64) { + /* + * Convert the rhs to 64 bit + */ + ejsWriteVarAsInteger64(ep, rhs, ejsVarToInteger64(rhs)); +#endif + } else if (lhs->type == EJS_TYPE_BOOL || lhs->type == EJS_TYPE_INT) { + + if (rhs->type == EJS_TYPE_STRING) { + if (stringIsNumber(rhs->string)) { + ejsWriteVarAsNumber(ep, rhs, ejsVarToNumber(rhs)); + } else { + /* + * Convert to lhs to a string + */ + str = ejsVarToString(ep, lhs); + /* MOB -- rc */ + if (str) { + ejsWriteVarAsString(ep, lhs, str); + } + } + +#if BLD_FEATURE_FLOATING_POINT + } else if (rhs->type == EJS_TYPE_FLOAT) { + /* + * Convert lhs to floating + */ + ejsWriteVarAsFloat(ep, lhs, ejsVarToFloat(lhs)); +#endif + + } else { + /* + * Forcibly convert both operands to numbers + */ + ejsWriteVarAsNumber(ep, lhs, ejsVarToNumber(lhs)); + ejsWriteVarAsNumber(ep, rhs, ejsVarToNumber(rhs)); + } + } + } + + /* + * We have failed to coerce the types to be the same. Special case here + * for undefined and null. We need to allow comparisions against these + * special values. + */ + if (lhs->type == EJS_TYPE_UNDEFINED || lhs->type == EJS_TYPE_NULL) { + switch (rel) { + case EJS_EXPR_EQ: + lval = lhs->type == rhs->type; + break; + case EJS_EXPR_NOTEQ: + lval = lhs->type != rhs->type; + break; + case EJS_EXPR_BOOL_COMP: + lval = ! ejsVarToBoolean(rhs); + break; + default: + ejsWriteVar(ep, ep->result, rhs, EJS_SHALLOW_COPY); + return 0; + } + ejsWriteVarAsBoolean(ep, ep->result, lval); + return 0; + } + + /* + * Types are the same here + */ + switch (lhs->type) { + default: + case EJS_TYPE_UNDEFINED: + case EJS_TYPE_NULL: + /* Should be handled above */ + mprAssert(0); + return 0; + + case EJS_TYPE_STRING_CMETHOD: + case EJS_TYPE_CMETHOD: + case EJS_TYPE_METHOD: + case EJS_TYPE_PTR: + ejsWriteVarAsBoolean(ep, ep->result, 0); + return 0; + + case EJS_TYPE_OBJECT: + rc = evalObjExpr(ep, lhs, rel, rhs); + break; + + case EJS_TYPE_BOOL: + rc = evalBoolExpr(ep, lhs->boolean, rel, rhs->boolean); + break; + +#if BLD_FEATURE_FLOATING_POINT + case EJS_TYPE_FLOAT: + rc = evalFloatExpr(ep, lhs->floating, rel, rhs->floating); + break; +#endif + + case EJS_TYPE_INT: + rc = evalNumericExpr(ep, (EjsNum) lhs->integer, rel, + (EjsNum) rhs->integer); + break; + +#if BLD_FEATURE_INT64 + case EJS_TYPE_INT64: + rc = evalNumericExpr(ep, (EjsNum) lhs->integer64, rel, + (EjsNum) rhs->integer64); + break; +#endif + + case EJS_TYPE_STRING: + rc = evalStringExpr(ep, lhs, rel, rhs); + } + + /* MOB */ + if (lhs->type == EJS_TYPE_OBJECT) { + ejsMakeObjLive(lhs, 0); + mprAssert(lhs->objectState->alive == 0); + } + if (rhs->type == EJS_TYPE_OBJECT) { + ejsMakeObjLive(rhs, 0); + mprAssert(rhs->objectState->alive == 0); + } + + return rc; +} + +/******************************************************************************/ +#if BLD_FEATURE_FLOATING_POINT +/* + * Expressions with floating operands + */ + +static int evalFloatExpr(Ejs *ep, double l, int rel, double r) +{ + double lval; + int logical; + + lval = 0; + logical = 0; + + switch (rel) { + case EJS_EXPR_PLUS: + lval = l + r; + break; + case EJS_EXPR_INC: + lval = l + 1; + break; + case EJS_EXPR_MINUS: + lval = l - r; + break; + case EJS_EXPR_DEC: + lval = l - 1; + break; + case EJS_EXPR_MUL: + lval = l * r; + break; + case EJS_EXPR_DIV: + lval = l / r; + break; + default: + logical++; + break; + } + + /* + * Logical operators + */ + if (logical) { + + switch (rel) { + case EJS_EXPR_EQ: + lval = l == r; + break; + case EJS_EXPR_NOTEQ: + lval = l != r; + break; + case EJS_EXPR_LESS: + lval = (l < r) ? 1 : 0; + break; + case EJS_EXPR_LESSEQ: + lval = (l <= r) ? 1 : 0; + break; + case EJS_EXPR_GREATER: + lval = (l > r) ? 1 : 0; + break; + case EJS_EXPR_GREATEREQ: + lval = (l >= r) ? 1 : 0; + break; + case EJS_EXPR_BOOL_COMP: + lval = (r == 0) ? 1 : 0; + break; + default: + ejsError(ep, EJS_SYNTAX_ERROR, "Bad operator %d", rel); + return -1; + } + ejsWriteVarAsBoolean(ep, ep->result, lval != 0); + + } else { + ejsWriteVarAsFloat(ep, ep->result, lval); + } + return 0; +} + +#endif /* BLD_FEATURE_FLOATING_POINT */ +/******************************************************************************/ +/* + * Expressions with object operands + */ + +static int evalObjExpr(Ejs *ep, EjsVar *lhs, int rel, EjsVar *rhs) +{ + int lval; + + switch (rel) { + case EJS_EXPR_EQ: + lval = lhs->objectState == rhs->objectState; + break; + case EJS_EXPR_NOTEQ: + lval = lhs->objectState != rhs->objectState; + break; + default: + ejsError(ep, EJS_SYNTAX_ERROR, "Bad operator %d", rel); + return -1; + } + ejsWriteVarAsBoolean(ep, ep->result, lval); + return 0; +} + +/******************************************************************************/ +/* + * Expressions with boolean operands + */ + +static int evalBoolExpr(Ejs *ep, int l, int rel, int r) +{ + int lval; + + switch (rel) { + case EJS_EXPR_EQ: + lval = l == r; + break; + case EJS_EXPR_NOTEQ: + lval = l != r; + break; + case EJS_EXPR_BOOL_COMP: + lval = (r == 0) ? 1 : 0; + break; + default: + ejsError(ep, EJS_SYNTAX_ERROR, "Bad operator %d", rel); + return -1; + } + ejsWriteVarAsBoolean(ep, ep->result, lval); + return 0; +} + +/******************************************************************************/ +/* + * Expressions with numeric operands + */ + +static int evalNumericExpr(Ejs *ep, EjsNum l, int rel, EjsNum r) +{ + EjsNum lval; + int logical; + + lval = 0; + logical = 0; + + switch (rel) { + case EJS_EXPR_PLUS: + lval = l + r; + break; + case EJS_EXPR_INC: + lval = l + 1; + break; + case EJS_EXPR_MINUS: + lval = l - r; + break; + case EJS_EXPR_DEC: + lval = l - 1; + break; + case EJS_EXPR_MUL: + lval = l * r; + break; + case EJS_EXPR_DIV: + if (r != 0) { + lval = l / r; + } else { + ejsError(ep, EJS_RANGE_ERROR, "Divide by zero"); + return -1; + } + break; + case EJS_EXPR_MOD: + if (r != 0) { + lval = l % r; + } else { + ejsError(ep, EJS_RANGE_ERROR, "Modulo zero"); + return -1; + } + break; + case EJS_EXPR_LSHIFT: + lval = l << r; + break; + case EJS_EXPR_RSHIFT: + lval = l >> r; + break; + + default: + logical++; + break; + } + + /* + * Logical operators + */ + if (logical) { + + switch (rel) { + case EJS_EXPR_EQ: + lval = l == r; + break; + case EJS_EXPR_NOTEQ: + lval = l != r; + break; + case EJS_EXPR_LESS: + lval = (l < r) ? 1 : 0; + break; + case EJS_EXPR_LESSEQ: + lval = (l <= r) ? 1 : 0; + break; + case EJS_EXPR_GREATER: + lval = (l > r) ? 1 : 0; + break; + case EJS_EXPR_GREATEREQ: + lval = (l >= r) ? 1 : 0; + break; + case EJS_EXPR_BOOL_COMP: + lval = (r == 0) ? 1 : 0; + break; + default: + ejsError(ep, EJS_SYNTAX_ERROR, "Bad operator %d", rel); + return -1; + } + ejsWriteVarAsBoolean(ep, ep->result, lval != 0); + + } else { + ejsWriteVarAsNumber(ep, ep->result, lval); + } + return 0; +} + +/******************************************************************************/ +/* + * Expressions with string operands + */ + +static int evalStringExpr(Ejs *ep, EjsVar *lhs, int rel, EjsVar *rhs) +{ + int lval; + + mprAssert(ep); + mprAssert(lhs); + mprAssert(rhs); + + switch (rel) { + case EJS_EXPR_LESS: + lval = strcmp(lhs->string, rhs->string) < 0; + break; + case EJS_EXPR_LESSEQ: + lval = strcmp(lhs->string, rhs->string) <= 0; + break; + case EJS_EXPR_GREATER: + lval = strcmp(lhs->string, rhs->string) > 0; + break; + case EJS_EXPR_GREATEREQ: + lval = strcmp(lhs->string, rhs->string) >= 0; + break; + case EJS_EXPR_EQ: + lval = strcmp(lhs->string, rhs->string) == 0; + break; + case EJS_EXPR_NOTEQ: + lval = strcmp(lhs->string, rhs->string) != 0; + break; + case EJS_EXPR_PLUS: + /* + * This differs from all the above operations. We append rhs to lhs. + */ + ejsClearVar(ep, ep->result); + ejsStrcat(ep, ep->result, lhs); + ejsStrcat(ep, ep->result, rhs); + return 0; + + case EJS_EXPR_INC: + case EJS_EXPR_DEC: + case EJS_EXPR_MINUS: + case EJS_EXPR_DIV: + case EJS_EXPR_MOD: + case EJS_EXPR_LSHIFT: + case EJS_EXPR_RSHIFT: + default: + ejsSyntaxError(ep, "Bad operator"); + return -1; + } + + ejsWriteVarAsBoolean(ep, ep->result, lval); + return 0; +} + +/******************************************************************************/ +/* + * Evaluate a method. obj is set to the current object if a method is being + * run. + */ + +static int evalMethod(Ejs *ep, EjsVar *obj, EjsProc *proc, int flags) +{ + EjsProperty *pp; + EjsVar *saveThis, *prototype; + int saveThisPerm, rc, fid; + + mprAssert(ep); + + rc = 0; + fid = -1; + saveThis = 0; + saveThisPerm = 0; + prototype = proc->fn; + + if (prototype == 0) { + ejsError(ep, EJS_EVAL_ERROR, "Undefined method"); + return EJS_STATE_ERR; + } + + if (prototype->type == EJS_TYPE_OBJECT) { + prototype = ejsGetPropertyAsVar(ep, prototype, proc->procName); + } + + if (prototype) { + /* + * Create a new variable stack frame. ie. new local variables. + * Some C methods (eg. include) don't create a new local context. + */ + if (! (prototype->flags & EJS_NO_LOCAL)) { + fid = ejsOpenBlock(ep); + if (fid < 0) { + return EJS_STATE_ERR; + } + mprAssert(ejsBlockInUse(ep->local)); + + pp = ejsSetProperty(ep, ep->local, "this", obj); + ejsMakePropertyEnumerable(pp, 0); + + /* + * Optimization. Save "this" during this block. + */ + saveThis = ep->thisObject; + ep->thisObject = ejsGetVarPtr(pp); + saveThisPerm = ejsMakeObjPermanent(saveThis, 1); + } + + switch (prototype->type) { + default: + mprAssert(0); + break; + + case EJS_TYPE_STRING_CMETHOD: + rc = callStringCMethod(ep, obj, proc, prototype); + break; + + case EJS_TYPE_CMETHOD: + rc = callCMethod(ep, obj, proc, prototype); + break; + + case EJS_TYPE_METHOD: + rc = callMethod(ep, obj, proc, prototype); + break; + } + + if (fid >= 0) { + ejsMakeObjPermanent(saveThis, saveThisPerm); + ep->thisObject = saveThis; + mprAssert(ejsBlockInUse(ep->local)); + mprAssert(ejsBlockInUse(ep->thisObject)); + ejsCloseBlock(ep, fid); + } + } + + return rc; +} + +/******************************************************************************/ +/* + * Create a new object and call all required constructors. + * obj may be null in which case we look globally for className. + */ + +EjsVar *ejsCreateObjUsingArgvInternal(EJS_LOC_DEC(ep, loc), EjsVar *obj, + const char *className, MprArray *args) +{ + EjsVar *baseClass, *objectClass, *thisObj; + int rc; + + mprAssert(className && *className); + + /* + * Create a new object of the required class and pass it into the + * constructor as the "this" local variable. + */ + baseClass = ejsGetClass(ep, obj, className); + if (baseClass == 0) { + + if (obj && obj->objectState->className && + strcmp(obj->objectState->className, className) == 0) { + /* + * Handle case where we are calling the constructor inside + * the class. In this case, obj == baseClass. + */ + thisObj = ejsCreateSimpleObjUsingClassInt(EJS_LOC_PASS(ep, loc), + obj); + + } else { + + /* + * If the baseClass does not exist, try to create an Object + * We do this for compatibility with JS 1.5 style new Function. + * MOB -- but this masks an error if we really need className. + */ + objectClass = ejsGetClass(ep, 0, "Object"); + thisObj = ejsCreateSimpleObjUsingClassInt(EJS_LOC_PASS(ep, loc), + objectClass); + } + + } else { + thisObj = ejsCreateSimpleObjUsingClassInt(EJS_LOC_PASS(ep, loc), + baseClass); + } + + if (thisObj == 0) { + ejsMemoryError(ep); + return 0; + } + + /* + * Make the object permanent. While currently not alive, the constructor + * below may make the object alive. + */ + ejsMakeObjPermanent(thisObj, 1); + mprAssert(! ejsObjIsCollectable(thisObj)); + + rc = 0; + if (baseClass) { + if (! baseClass->objectState->noConstructor) { + rc = callConstructor(ep, thisObj, baseClass, args); + } + } else { + /* + * className is the function name when calling new on functions + */ + rc = ejsRunMethod(ep, thisObj, className, args); + } + + /* + * Constructor may change the type to a non-object. + * Function() does this. Ensure object is not collectable yet. + */ + if (ejsVarIsObject(thisObj)) { + ejsMakeObjPermanent(thisObj, 0); + ejsMakeObjLive(thisObj, 0); + } + + if (rc < 0) { + if (rc == MPR_ERR_NOT_FOUND) { + /* No constructor (default) */ + return thisObj; + } + if (! (ep->flags & EJS_FLAGS_EXIT)) { + if (! ep->gotException) { + ejsMemoryError(ep); + } + } + ejsFreeVar(ep, thisObj); + return 0; + } + + mprAssert(ejsBlockInUse(thisObj)); + + return thisObj; +} + +/******************************************************************************/ +/* + * Local vars + */ + +typedef struct CallCons { + EjsVar *subClassConstructor, *subClass, *method; +} CallCons; + +/* + * Create a new object and call all required constructors. + */ + +static int callConstructor(Ejs *ep, EjsVar *thisObj, EjsVar *baseClass, + MprArray *args) +{ + CallCons *sp; + int state; + + if ((sp = pushFrame(ep, sizeof(CallCons))) == 0) { + return EJS_STATE_ERR; + } + + mprAssert(baseClass); + mprAssert(baseClass->objectState); + + state = 0; + + /* + * method will be null if there is no constructor for this class + */ + sp->method = ejsGetPropertyAsVar(ep, baseClass, + baseClass->objectState->className); + + if (sp->method == 0 || !ejsVarIsMethod(sp->method) || + !sp->method->callsSuper) { + /* + * Invoke base class constructors in reverse order (RECURSIVE) + */ + sp->subClass = baseClass->objectState->baseClass; + if (sp->subClass) { + + /* + * Note that the Object class does not have a constructor for + * speed. Construction for the base Object is done via + * ejsCreateObj above. The code below will invoke constructors + * in the right order (bottom up) via recursion. MOB -- need to + * scan for super() MOB -- Bug. Fails poorly if no constructor. + * Should allows this and invoke a default constructor. + */ + sp->subClassConstructor = ejsGetPropertyAsVar(ep, sp->subClass, + sp->subClass->objectState->className); + + if (sp->subClassConstructor) { + + if (callConstructor(ep, thisObj, sp->subClass, 0) < 0) { + if (! ep->gotException) { + ejsMemoryError(ep); + } + goto err; + } + } + } + } + + if (sp->method) { + /* + * Finally, invoke the constructor for this class itself. + */ + state = runMethod(ep, thisObj, sp->method, + baseClass->objectState->className, args); + } + +done: + popFrame(ep, sizeof(CallCons)); + return state; + +err: + state = EJS_STATE_ERR; + goto done; +} + +/******************************************************************************/ +/* + * Create a new object and call all required constructors using string args. + * MOB -- would be good to parse constructorArgs for "," and break into + * separate args. + * Returned object is not yet collectable. Will have alive bit cleared. + */ + +EjsVar *ejsCreateObj(Ejs *ep, EjsVar *obj, const char *className, + const char *constructorArgs) +{ + MprArray *args; + EjsVar *newp, *vp; + + args = mprCreateItemArray(ep, 0, 0); + if (args == 0) { + return 0; + } + + if (constructorArgs && *constructorArgs) { + vp = ejsCreateStringVarInternal(EJS_LOC_ARGS(ep), constructorArgs); + + if (mprAddItem(args, vp) < 0) { + mprFree(args); + return 0; + } + } + + newp = ejsCreateObjUsingArgv(ep, obj, className, args); + + ejsFreeMethodArgs(ep, args); + + mprAssert(! ejsObjIsCollectable(newp)); + mprAssert(ejsBlockInUse(newp)); + + return newp; +} + +/******************************************************************************/ + +static int callStringCMethod(Ejs *ep, EjsVar *obj, EjsProc *proc, + EjsVar *prototype) +{ + EjsVar **argValues; + MprArray *actualArgs; + char **argBuf, *str; + int i, rc; + + actualArgs = proc->args; + argValues = (EjsVar**) actualArgs->items; + + if (actualArgs->length > 0) { + argBuf = mprAlloc(ep, actualArgs->length * sizeof(char*)); + for (i = 0; i < actualArgs->length; i++) { + str = ejsVarToString(ep, argValues[i]); + /* MOB rc */ + argBuf[i] = mprStrdup(ep, str); + } + } else { + argBuf = 0; + } + + /* + * Call the method depending on the various handle flags + */ + ep->userData = prototype->cMethodWithStrings.userData; + if (prototype->flags & EJS_ALT_HANDLE) { + /* + * Used by the AppWeb GaCompat module. The alt handle is set to the + * web server request struct + */ + rc = ((EjsAltStringCMethod) + prototype->cMethodWithStrings.fn) + (ep, ep->altHandle, obj, actualArgs->length, argBuf); + + } else if (prototype->flags & EJS_PRIMARY_HANDLE) { + /* + * Used by ESP. The primary handle is set to the esp struct + */ + rc = (prototype->cMethodWithStrings.fn)(ep->primaryHandle, + obj, actualArgs->length, argBuf); + + } else { + /* + * Used EJS for the standard procs + */ + rc = (prototype->cMethodWithStrings.fn)(ep, obj, actualArgs->length, + argBuf); + } + + if (actualArgs->length > 0) { + for (i = 0; i < actualArgs->length; i++) { + mprFree(argBuf[i]); + } + mprFree(argBuf); + } + ep->userData = 0; + + return rc; +} + +/******************************************************************************/ + +static int callCMethod(Ejs *ep, EjsVar *obj, EjsProc *proc, EjsVar *prototype) +{ + EjsVar **argValues; + MprArray *actualArgs; + int rc; + + actualArgs = proc->args; + argValues = (EjsVar**) actualArgs->items; + + ep->userData = prototype->cMethod.userData; + + /* + * Call the method depending on the various handle flags + * Sometimes cMethod.fn is NULL if there is no constructor for + * an object. + */ + if (prototype->flags & EJS_ALT_HANDLE) { + /* + * Use by the GaCompat module. The alt handle is set to the + * web server request struct + */ + rc = ((EjsAltCMethod) prototype->cMethod.fn) + (ep, ep->altHandle, obj, actualArgs->length, argValues); + + } else if (prototype->flags & EJS_PRIMARY_HANDLE) { + /* + * Used by ESP. The primary handle is set to the esp struct + */ + rc = (prototype->cMethod.fn) + (ep->primaryHandle, obj, actualArgs->length, argValues); + + } else { + /* + * Used EJS for the standard procs + */ + rc = (prototype->cMethod.fn)(ep, obj, actualArgs->length, argValues); + } + + ep->userData = 0; + + return rc; +} + +/******************************************************************************/ +/* + * Local vars + */ + +typedef struct CallMethod { + MprArray *formalArgs, *actualArgs; + EjsVar *arguments, *callee, **argValues; + char **argNames, buf[16]; + int i, argumentsObj; +} CallMethod; + + +static int callMethod(Ejs *ep, EjsVar *obj, EjsProc *proc, EjsVar *prototype) +{ + CallMethod *sp; + int i; + + if ((sp = pushFrame(ep, sizeof(CallMethod))) == 0) { + return EJS_STATE_ERR; + } + + sp->arguments = 0; + sp->callee = 0; + + sp->actualArgs = proc->args; + sp->argValues = (EjsVar**) sp->actualArgs->items; + sp->formalArgs = prototype->method.args; + sp->argNames = (char**) sp->formalArgs->items; + + /* + * Only create arguments and callee if the function actually uses them + */ + sp->argumentsObj = 0; + if (strstr(prototype->method.body, "arguments") != 0) { + sp->argumentsObj++; + + /* + * Create the arguments and callee variables + * MOB -- should we make real arrays here ? YES + */ + sp->arguments = ejsCreateSimpleObj(ep, "Object"); + ejsSetVarName(ep, sp->arguments, "arguments"); + mprAssert(! ejsObjIsCollectable(sp->arguments)); + + sp->callee = ejsCreateSimpleObj(ep, "Object"); + ejsSetVarName(ep, sp->callee, "callee"); + mprAssert(! ejsObjIsCollectable(sp->callee)); + + /* + * Overwrite the length property + */ + ejsSetPropertyToInteger(ep, sp->arguments, "length", + sp->actualArgs->length); + ejsSetPropertyToInteger(ep, sp->callee, "length", + sp->formalArgs->length); + } + + /* + * Define all the agruments to be set to the actual parameters + */ + for (i = 0; i < sp->formalArgs->length; i++) { + if (i >= sp->actualArgs->length) { + /* MOB -- return code */ + ejsCreateProperty(ep, ep->local, sp->argNames[i]); + + } else { + /* MOB -- return code */ + ejsSetProperty(ep, ep->local, sp->argNames[i], sp->argValues[i]); + } + } + + if (sp->argumentsObj) { + for (i = 0; i < sp->actualArgs->length; i++) { + mprItoa(sp->buf, sizeof(sp->buf), i); + ejsSetProperty(ep, sp->arguments, sp->buf, sp->argValues[i]); + } + + ejsSetPropertyAndFree(ep, sp->arguments, "callee", sp->callee); + ejsSetPropertyAndFree(ep, ep->local, "arguments", sp->arguments); + } + + /* + * Actually run the method + */ + + i = ejsEvalScript(ep, prototype->method.body, 0); + + popFrame(ep, sizeof(CallMethod)); + return i; +} + +/******************************************************************************/ +/* + * Run a method. Obj is set to "this" object. MethodName must exist in it + * or in a sub class. + */ + +int ejsRunMethod(Ejs *ep, EjsVar *obj, const char *methodName, MprArray *args) +{ + EjsProperty *pp; + EjsProc proc, *saveProc; + int rc; + + mprAssert(obj); + mprAssert(methodName && *methodName); + + pp = ejsGetProperty(ep, obj, methodName); + if (pp == 0) { + /* MOB -- this should be all in some common accessor routine */ + pp = ejsGetProperty(ep, ep->local, methodName); + if (pp == 0) { + pp = ejsGetProperty(ep, ep->global, methodName); + if (pp == 0) { + ejsError(ep, EJS_REFERENCE_ERROR, + "Undefined method \"%s\"", methodName); + return MPR_ERR_NOT_FOUND; + } + } + } + + saveProc = ep->proc; + ep->proc = &proc; + + memset(&proc, 0, sizeof(EjsProc)); + + ejsClearVar(ep, ep->result); + + /* MOB -- if closures are going to work, we need to have proc be an + Object and let the GC look after it */ + + proc.fn = &pp->var; + if (proc.fn == 0 || proc.fn->type == EJS_TYPE_UNDEFINED) { + ep->proc = saveProc; + return MPR_ERR_NOT_FOUND; + } + + proc.procName = mprStrdup(ep, methodName); + if (args == 0) { + proc.args = mprCreateItemArray(ep, EJS_INC_ARGS, EJS_MAX_ARGS); + } else { + proc.args = args; + } + + rc = evalMethod(ep, obj, &proc, 0); + + if (args) { + proc.args = 0; + } + freeProc(ep, &proc); + + ep->proc = saveProc; + + return rc; +} + +/******************************************************************************/ +/* + * Run a method. Obj is set to "this" object. MethodName must exist in it + * or in a sub class. + */ + +int ejsRunMethodCmd(Ejs *ep, EjsVar *obj, const char *methodName, + const char *cmdFmt, ...) +{ + MprArray *args; + va_list cmdArgs; + char *buf, *arg, *cp; + int rc; + + mprAssert(methodName && *methodName); + mprAssert(cmdFmt && *cmdFmt); + + va_start(cmdArgs, cmdFmt); + mprAllocVsprintf(MPR_LOC_ARGS(ep), &buf, 0, cmdFmt, cmdArgs); + va_end(cmdArgs); + + args = mprCreateItemArray(ep, EJS_INC_ARGS, EJS_MAX_ARGS); + + for (arg = cp = buf; cp && *cp; cp++) { + if (*cp == ',') { + *cp = 0; + mprAddItem(args, ejsParseVar(ep, arg, 0)); + arg = cp + 1; + } + } + if (cp > arg) { + mprAddItem(args, ejsParseVar(ep, arg, 0)); + } + + rc = ejsRunMethod(ep, obj, methodName, args); + + ejsFreeMethodArgs(ep, args); + mprFree(buf); + + return rc; +} + +/******************************************************************************/ +/* + * Run a method. Obj is set to "this" object. + */ + +static int runMethod(Ejs *ep, EjsVar *thisObj, EjsVar *method, + const char *methodName, MprArray *args) +{ + EjsProc proc, *saveProc; + int rc; + + mprAssert(thisObj); + mprAssert(method); + + saveProc = ep->proc; + ep->proc = &proc; + + memset(&proc, 0, sizeof(EjsProc)); + + ejsClearVar(ep, ep->result); + + /* MOB -- if closures are going to work, we need to have proc be an + Object and let the GC look after it */ + + proc.fn = method; + if (proc.fn == 0 || proc.fn->type == EJS_TYPE_UNDEFINED) { + ep->proc = saveProc; + return MPR_ERR_NOT_FOUND; + } + + proc.procName = mprStrdup(ep, methodName); + if (args == 0) { + proc.args = mprCreateItemArray(ep, EJS_INC_ARGS, EJS_MAX_ARGS); + } else { + proc.args = args; + } + + rc = evalMethod(ep, thisObj, &proc, 0); + + if (args) { + proc.args = 0; + } + freeProc(ep, &proc); + + ep->proc = saveProc; + + return rc; +} + +/******************************************************************************/ +/* + * Find which object contains the property given the current context. + * We call this when there is no explicit object and the object must be + * determined by the context. + */ + +static EjsVar *pickSpace(Ejs *ep, int state, const char *property, int flags) +{ + EjsVar *obj; + + mprAssert(ep); + mprAssert(property && *property); + + /* MOB - this is ugly and the logic is confused */ + + if (flags & EJS_FLAGS_GLOBAL) { + obj = ep->global; + + } else if (state == EJS_STATE_DEC || flags & EJS_FLAGS_LOCAL) { + obj = ep->local; + + } else { + /* First look local, then this and finally global */ + + if (ejsGetSimpleProperty(ep, ep->local, property)) { + obj = ep->local; + + } else if (ep->thisObject && + findProperty(ep, ep->thisObject, property, flags)) { + obj = ep->thisObject; + + } else { +#if EJS_ECMA_STND + obj = ep->global; +#else + if (flags & EJS_FLAGS_EXE && + !findProperty(ep, ep->global, property, flags)) { + obj = ep->local; + } else { + obj = ep->global; + } +#endif + } + } + return obj; +} + +/******************************************************************************/ +/* + * Find an object property given a object and a property name. We + * intelligently look in the local and global namespaces depending on + * our state. If not found in local or global, try base classes for method + * names only. Returns the property or NULL. + * MOB -- need to rework this API. + */ + +static EjsProperty *searchSpacesForProperty(Ejs *ep, int state, EjsVar *obj, + char *property, int flags) +{ + EjsProperty *pp; + + if (obj) { + return findProperty(ep, obj, property, flags); + } + + /* MOB -- really should have a search stack */ + + pp = findProperty(ep, ep->local, property, flags); + if (pp == 0 && state != EJS_STATE_DEC) { + + if (ep->thisObject) { + pp = findProperty(ep, ep->thisObject, property, flags); + } + if (pp == 0) { + pp = findProperty(ep, ep->global, property, flags); + } + } + return pp; +} + +/******************************************************************************/ +/* + * Search an object and its base classes to find an object given an object + * an a property name. If not an assignment (LHS), then follow base classes. + * Otherwise, just look in the specified object. + */ + +static EjsProperty *findProperty(Ejs *ep, EjsVar *op, const char *property, + int flags) +{ + /* MOB -- NEW. Remove when EXE fixes are in. */ + if (! (flags & EJS_FLAGS_EXE) && op->type == EJS_TYPE_UNDEFINED) { + return 0; + } + + if (flags & EJS_FLAGS_LHS) { + return ejsGetPropertyPtr(ejsGetSimpleProperty(ep, op, property)); + + } else { + /* + * Follow base classes + */ + return ejsGetPropertyPtr(ejsGetProperty(ep, op, property)); + } +} + +/******************************************************************************/ +/* + * Update result + */ + +static void updateResult(Ejs *ep, int state, int flags, EjsVar *vp) +{ + if (flags & EJS_FLAGS_EXE && state != EJS_STATE_DEC) { + ejsClearVar(ep, ep->result); + if (vp) { + ejsWriteVar(ep, ep->result, vp, EJS_SHALLOW_COPY); + ejsSetVarName(ep, ep->result, vp->propertyName); + } + } +} + +/******************************************************************************/ +/* + * Append to the pointer value + */ + +int ejsStrcat(Ejs *ep, EjsVar *dest, EjsVar *src) +{ + char *oldBuf, *buf, *str; + int oldLen, newLen, len; + + mprAssert(dest); + mprAssert(ejsVarIsString(src)); + + if (ejsVarIsValid(dest)) { + + if (! ejsVarIsString(dest)) { + /* Bad type for dest */ + return -1; + } + + if (! ejsVarIsString(src)) { + str = ejsVarToString(ep, src); + if (str == 0) { + return -1; + } + len = strlen(str); + + } else { + str = src->string; + len = src->length; + } + + oldBuf = dest->string; + oldLen = dest->length; + newLen = oldLen + len + 1; + + if (newLen < MPR_SLAB_STR_MAX) { + buf = oldBuf; + } else { + buf = mprRealloc(ep, oldBuf, newLen); + if (buf == 0) { + return -1; + } + dest->string = buf; + } + memcpy(&buf[oldLen], str, len); + dest->length += len; + + } else { + ejsWriteVarAsString(ep, dest, src->string); + } + return 0; +} + +/******************************************************************************/ +/* + * Exit the script + */ + +void ejsExit(Ejs *ep, int status) +{ + ep->scriptStatus = status; + ep->flags |= EJS_FLAGS_EXIT; +} + +/******************************************************************************/ +/* + * Free an argument list + */ + +static void freeProc(Ejs *ep, EjsProc *proc) +{ + if (proc->args) { + ejsFreeMethodArgs(ep, proc->args); + } + + if (proc->procName) { + mprFree(proc->procName); + proc->procName = NULL; + } +} + +/******************************************************************************/ + +void ejsFreeMethodArgs(Ejs *ep, MprArray *args) +{ + int i; + + for (i = args->length - 1; i >= 0; i--) { + ejsFreeVar(ep, args->items[i]); + mprRemoveItemByIndex(args, i); + } + mprFree(args); +} + +/******************************************************************************/ +/* + * This method removes any new lines. Used for else cases, etc. + */ + +static void removeNewlines(Ejs *ep, int state) +{ + int tid; + + do { + tid = ejsLexGetToken(ep, state); + } while (tid == EJS_TOK_NEWLINE); + + ejsLexPutbackToken(ep, tid, ep->token); +} + +/******************************************************************************/ + +static int getNextNonSpaceToken(Ejs *ep, int state) +{ + int tid; + + do { + tid = ejsLexGetToken(ep, state); + } while (tid == EJS_TOK_NEWLINE); + return tid; +} + +/******************************************************************************/ + +int ejsGetFlags(Ejs *ep) +{ + return ep->flags; +} + +/******************************************************************************/ + +bool ejsIsExiting(Ejs *ep) +{ + return (ep->flags & EJS_FLAGS_EXIT) ? 1: 0; +} + +/******************************************************************************/ + +void ejsClearExiting(Ejs *ep) +{ + ep->flags &= ~EJS_FLAGS_EXIT; +} + +/******************************************************************************/ + +static EjsInput *getInputStruct(Ejs *ep) +{ + EjsInput *input; + + if (ep->inputList) { + input = ep->inputList; + ep->inputList = input->nextInput; + + } else { + input = mprAlloc(ep, sizeof(EjsInput)); + } + return input; +} + +/******************************************************************************/ + +static void freeInputStruct(Ejs *ep, EjsInput *input) +{ + input->nextInput = ep->inputList; + ep->inputList = input; +} + +/******************************************************************************/ + +static void *pushFrame(Ejs *ep, int size) +{ + /* + * Grow down stack + */ + ep->stkPtr -= size; + if (ep->stkPtr < ep->stack) { + mprError(ep, MPR_LOC, "Exceeded parse stack"); + return 0; + } + return ep->stkPtr; +} + +/******************************************************************************/ + +static void *popFrame(Ejs *ep, int size) +{ + ep->stkPtr += size; + if (ep->stkPtr > &ep->stack[EJS_MAX_STACK]) { + mprError(ep, MPR_LOC, "Over poped parse stack"); + return 0; + } + return ep->stkPtr; +} + +/******************************************************************************/ +#else +void ejsParserDummy() {} + +/******************************************************************************/ +#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 + */ diff --git a/source4/lib/appweb/ejs-2.0/ejs/ejsVar.c b/source4/lib/appweb/ejs-2.0/ejs/ejsVar.c new file mode 100644 index 0000000000..5067215f03 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/ejsVar.c @@ -0,0 +1,4033 @@ +/** + * @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, 0) < 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, 0) < 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 + */ diff --git a/source4/lib/appweb/ejs-2.0/ejs/ejsVar.h b/source4/lib/appweb/ejs-2.0/ejs/ejsVar.h new file mode 100644 index 0000000000..071665e00b --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/ejsVar.h @@ -0,0 +1,1091 @@ +/* + * ejsVar.h -- EJS 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 + */ + +/* + * Variables can efficiently store primitive types and can hold references to + * objects. Objects can store properties which are themselves variables. + * Properties can be primitive data types, other objects or methods. + * Properties are indexed by a character name. A variable may store one of + * the following types: + * + * string, integer, integer-64bit, C method, C method with string args, + * Javascript method, Floating point number, boolean value, Undefined + * value and the Null value. + * + * Variables have names while objects may be referenced by multiple variables. + * Objects use reference counting for garbage collection. + * + * This module is not thread safe for performance and compactness. It relies + * on upper modules to provide thread synchronization as required. The API + * provides primitives to get variable/object references or to get copies of + * variables which will help minimize required lock times. + */ + +#ifndef _h_EJS_VAR +#define _h_EJS_VAR 1 + +/********************************* Includes ***********************************/ + +#include "mpr.h" + +/********************************** Defines ***********************************/ +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Defined in ejs.h + */ +typedef struct Ejs Ejs; + +/* + * Constants + */ +#if BLD_FEATURE_SQUEEZE + /** + * Maximum property or variable name size + */ + #define EJS_MAX_ID 64 + + /* + * EJS_VAR_HASH_SIZE must be less than the size of the bit field + * propertyIndex in EjsProperty. + */ + #define EJS_OBJ_HASH_SIZE 13 + + /** + * Maximum number of arguments per function call + */ + #define EJS_MAX_ARGS 32 + #define EJS_INC_ARGS 8 /* Frame stack increment */ + +#else + #define EJS_MAX_ID 256 + #define EJS_OBJ_HASH_SIZE 29 + #define EJS_MAX_ARGS 64 + #define EJS_INC_ARGS 8 +#endif + +#define EJS_VAR_MAX_RECURSE 5 /* Max object loops */ + +#if !DOXYGEN +/* + * Forward declare types + */ +struct Ejs; +struct EjsObj; +struct EjsProperty; +struct EjsVar; +#endif + +/** + * @overview EJ primitive variable type + * @description EJ primitive variable values are stored in EjsVar structures. + * The type of the primitive data is described by an EjsType field. + * EjsVar variable types. + * @stability Prototype. + * @library libejs. + * @see EJS_TYPE_UNDEFINED, EJS_TYPE_NULL, EJS_TYPE_BOOL, EJS_TYPE_CMETHOD, + * EJS_TYPE_FLOAT, EJS_TYPE_INT, EJS_TYPE_INT64, EJS_TYPE_OBJECT, + * EJS_TYPE_METHOD, EJS_TYPE_STRING, EJS_TYPE_STRING_CMETHOD, EJS_TYPE_PTR, + */ +typedef uint EjsType; +#define EJS_TYPE_UNDEFINED 0 /**< Undefined. No value has been set */ +#define EJS_TYPE_NULL 1 /**< Value defined to be null. */ +#define EJS_TYPE_BOOL 2 /**< Boolean type. */ +#define EJS_TYPE_CMETHOD 3 /**< C method */ +#define EJS_TYPE_FLOAT 4 /**< Floating point number */ +#define EJS_TYPE_INT 5 /**< Integer number */ +#define EJS_TYPE_INT64 6 /**< 64-bit Integer number */ +#define EJS_TYPE_OBJECT 7 /**< Object reference */ +#define EJS_TYPE_METHOD 8 /**< JavaScript method */ +#define EJS_TYPE_STRING 9 /**< String (immutable) */ +#define EJS_TYPE_STRING_CMETHOD 10 /**< C method with string args */ +#define EJS_TYPE_PTR 11 /**< Opaque pointer */ + +/* + * Create a type for the default number type + * Config.h will define the default number type. For example: + * + * BLD_FEATURE_NUM_TYPE=int + * BLD_FEATURE_NUM_TYPE_ID=EJS_TYPE_INT + */ + +/** + * Set to the type used for EJS numeric variables. Will equate to int, int64 + * or double. + */ +typedef BLD_FEATURE_NUM_TYPE EjsNum; + +/** + * Set to the EJS_TYPE used for EJS numeric variables. Will equate to + * EJS_TYPE_INT, EJS_TYPE_INT64 or EJS_TYPE_FLOAT. + */ +#define EJS_NUM_VAR BLD_FEATURE_NUM_TYPE_ID +#define EJS_TYPE_NUM BLD_FEATURE_NUM_TYPE_ID + +/* + * Return TRUE if a variable is a method type + */ +#define ejsVarIsMethod(vp) \ + ((vp)->type == EJS_TYPE_METHOD || (vp)->type == EJS_TYPE_STRING_CMETHOD || \ + (vp)->type == EJS_TYPE_CMETHOD) + +/* + * Return TRUE if a variable is a numeric type + */ +#define ejsVarIsNumber(vp) \ + ((vp)->type == EJS_TYPE_INT || (vp)->type == EJS_TYPE_INT64 || \ + (vp)->type == EJS_TYPE_FLOAT) + +/* + * Return TRUE if a variable is a boolean + */ +#define ejsVarIsBoolean(vp) \ + ((vp)->type == EJS_TYPE_BOOL) + +/* + * Return TRUE if a variable is an integer type + */ +#define ejsVarIsInteger(vp) ((vp)->type == EJS_TYPE_INT) + +/* + * Return TRUE if a variable is a string + */ +#define ejsVarIsString(vp) \ + ((vp)->type == EJS_TYPE_STRING) + +/* + * Return TRUE if a variable is an object + */ +#define ejsVarIsObject(vp) \ + ((vp)->type == EJS_TYPE_OBJECT) + +/* + * Return TRUE if a variable is a floating number + */ +#define ejsVarIsFloating(vp) \ + ((vp)->type == EJS_TYPE_FLOAT) + +/* + * Return TRUE if a variable is undefined + */ +#define ejsVarIsUndefined(var) \ + ((var)->type == EJS_TYPE_UNDEFINED) + +/* + * Return TRUE if a variable is null + */ +#define ejsVarIsNull(var) \ + ((var)->type == EJS_TYPE_NULL) + +/* + * Return TRUE if a variable is a valid type (not null or undefined) + */ +#define ejsVarIsValid(var) \ + (((var)->type != EJS_TYPE_NULL) && ((var)->type != EJS_TYPE_UNDEFINED)) + +/* + * Return TRUE if a variable is a ptr type + */ +#define ejsVarIsPtr(vp) \ + ((vp)->type == EJS_TYPE_PTR) + +/* MOB -- convert all ep to ejs */ +/** + * @overview C Method signature + * @description This is the calling signature for C Methods. + * @param ejs Ejs reference returned from ejsCreateInterp + * @param thisObj Reference to the "this" object. (The object containing the + * method). + * @param argc Number of arguments. + * @param argv Array of arguments. Each argument is held in an EjsVar type. + * @stability Prototype. + * @library libejs. + * @see ejsCreateCMethodVar + */ +typedef int (*EjsCMethod)(struct Ejs *ejs, struct EjsVar *thisObj, + int argc, struct EjsVar **argv); + +/** + * C Method with string arguments signature + * @overview C Method with string arguments signature + * @description This is the calling signature for C Methods. + * @param ejs Ejs reference returned from ejsCreateInterp + * @param thisObj Reference to the "this" object (object containing the + * method. + * @param argc Number of arguments. + * @param argv Array of arguments. Each argument is held in an C string + * pointer. + * @stability Prototype. + * @library libejs. + * @see ejsCreateStringCMethodVar + */ +typedef int (*EjsStringCMethod)(struct Ejs *ep, struct EjsVar *thisObj, + int argc, char **argv); + +/** + * Flags for types: EJS_TYPE_CMETHOD, EJS_TYPE_STRING_CMETHOD + * NOTE: flags == 0 means to use the EJS handle on method callbacks + */ +/* Use the primary handle on method callbacks */ +#define EJS_PRIMARY_HANDLE 0x1 + +/* Use the alternate handle on method callbacks */ +#define EJS_ALT_HANDLE 0x2 + +/** Method should not create a new local variable block */ +#define EJS_NO_LOCAL 0x4 + +/* Method is a get accessor */ +#define EJS_GET_ACCESSOR 0x8 + +/* Method is a set accessor */ +#define EJS_SET_ACCESSOR 0x10 + +/* + * Flags for E4X (Xml type) + */ +/* Node is a text node */ +#define EJS_XML_FLAGS_TEXT 0x1 + +/* Node is a processing instruction */ +#define EJS_XML_FLAGS_PI 0x2 + +/* Node is a comment */ +#define EJS_XML_FLAGS_COMMENT 0x4 + +/* Node is an attribute */ +#define EJS_XML_FLAGS_ATTRIBUTE 0x8 + +/* Node is an element */ +#define EJS_XML_FLAGS_ELEMENT 0x10 + +/** + * Copy depth + * @overview Specifies how an object should be copied + * @description The EjsCopyDepth type specifies how an object's properties + * should be copied. Several routines take EjsCopyDepth parameters to + * control how the properties of an object should be copied. It provides + * three copy options: + * @see ejsWriteVar + */ +typedef enum EjsCopyDepth { + /** + * During an object copy, object property references will be copied so + * that the original object and the copy will share the same reference to + * a property object. Properties containing primitive types including + * strings will have their values copied and will not share references. + */ + EJS_SHALLOW_COPY, /** Copy strings. Copy object references. */ + /* + * During an object copy, object properties will be replicated so that + * the original object and the copy will not share references to the same + * object properties. If the original object's properties are themselves + * objects, their properties will not be copied. Only their references + * will be copied. i.e. the deep copy is one level deep. + */ + EJS_DEEP_COPY, /** Copy strings and copy object contents. */ + /* + * During an object copy, all object properties will be replicated so that + * the original object and the copy will not share references to the same + * object properties. If the original object's properties are themselves + * objects, their properties will be copied. i.e. the copy is of infinite + * depth. + */ + EJS_RECURSIVE_DEEP_COPY /** Copy strings and copy object contents + recursively (complete copy). */ +} EjsCopyDepth; + + +/* + * Enumeration flags + */ +/** Enumerate data properties */ +#define EJS_ENUM_DATA 0x0 + +/** Enumerate sub classes */ +#define EJS_ENUM_CLASSES 0x1 + +/** Enumerate non-enumerable properties */ +#define EJS_ENUM_HIDDEN 0x2 + +/** Enumerate all properties */ +#define EJS_ENUM_ALL (0x3) + +/** Magic number when allocated */ +#define EJS_MAGIC 0xe801e2ec +#define EJS_MAGIC_FREE 0xe701e3ea + + +/* + * Garbage Collection Linkage. Free list only uses the next pointers. + */ +typedef struct EjsGCLink { +#if BLD_DEBUG + uint magic; /* Magic number */ +#endif +#if BLD_FEATURE_ALLOC_LEAK_TRACK + const char *allocatedBy; /* Who allocated this */ +#endif + struct EjsGCLink *next; /* Next property */ +} EjsGCLink; + + +/** + * @overview EJS Variable Type + * @description The EJ language supports an extensive set of primitive types. + * These variable types can efficiently store primitive data types such as + * integers, strings, binary string, booleans, floating point numbers, + * pointer references, and objects. EjsVars are the universal type used by + * EJ to hold objects, classes and properties. + * \n\n + * An EjsVar may store one of the following types: + * @li Boolean + * @li Floating point (if supported in this build) + * @li Integer + * @li 64 bit integer (if supported in this build) + * @li String + * @li Binary string + * @li C function or C++ method + * @li C function with string args + * @li Javascript method + * @li Object + * @li Null value. + * @li Undefined value + * \n\n + * Objects can hold object properties which are themselves EJS variables. + * Properties are hash indexed by the property name and are stored in + * an ordered sequence. i.e. Order of properties is maintained. Objects may + * be referenced by multiple variables and they use garbage collection to + * reclaim memory no longer in use by objects and properties. + * + * @warning This module is @e not thread safe for performance and + * compactness. It relies on upper modules to provide thread + * synchronization as required. The API provides primitives to get + * variable/object references or to get copies of variables which should + * help minimize required lock times. + * @stability Prototype. + * @library libejs + * @see Ejs, EjsProperty, ejsCreateStringVar, ejsFreeVar + */ + +typedef struct EjsVar { /* Size 12 bytes */ + /* + * GC must be first + */ +#if BLD_DEBUG || BLD_FEATURE_ALLOC_LEAK_TRACK + EjsGCLink gc; /* Garbage collection links */ +#endif + +#if BLD_DEBUG + const char *propertyName; /* Ptr to property name */ +#endif + + /* + * Union of primitive types. When debugging on Linux, don't use unions + * as the gdb debugger can't display them. + */ +#if (!BLD_DEBUG && !VXWORKS) || WIN || BREW_SIMULATOR + union { +#endif + /* + * For debugging, we order the common types first + */ + struct EjsObj *objectState; /* Object state information */ + int integer; + bool boolean; + +#if BLD_FEATURE_FLOATING_POINT + double floating; +#endif +#if BLD_FEATURE_INT64 + int64 integer64; +#endif + + struct { + int length; /* String length (sans null) */ + /* + * All strings always have a trailing null allocated + */ + union { + char *string; /* String */ + uchar *ustring; /* Binary string */ + }; + }; + + struct { /* Javascript methods */ + MprArray *args; /* Null terminated */ + char *body; + } method; + + struct { /* Method with EjsVar args */ + EjsCMethod fn; /* Method pointer */ + void *userData; /* User data for method */ + } cMethod; + + struct { /* Method with string args */ + EjsStringCMethod fn; /* Method pointer */ + void *userData; /* User data for method */ + } cMethodWithStrings; + + struct { + void *userPtr; /* Opaque pointer */ + int (*destructor)(Ejs *ejs, struct EjsVar *vp); + } ptr; + +#if (!BLD_DEBUG && !VXWORKS) || WIN || BREW_SIMULATOR + }; +#endif + + /* + * Packed bit field (32 bits) + */ + uint flags : 8; /* Type specific flags */ + EjsType type : 4; /* Selector into union */ + uint stringLen : 4; /* Length of string if inline */ + uint allocatedData : 1; /* Node needs freeing */ + uint isArray : 1; /* Var is an array */ + uint isArrayLength : 1; /* Var is array.length */ + uint callsSuper : 1; /* Method calls super() */ + uint isProperty : 1; /* Part of a property */ + uint reserved : 11; /* Unused */ + +} EjsVar; + + +/* + * Linkage for the ordered list of properties + */ +typedef struct EjsPropLink { + struct EjsPropLink *next; /* Next property */ + struct EjsPropLink *prev; /* Previous property */ + + /* + * To make debugging easier + */ +#if BLD_DEBUG + const char *propertyName; /* Pointer to name */ + struct EjsProperty *property; /* Pointer to property */ + struct EjsPropLink *head; /* Dummy head of list */ +#endif +} EjsPropLink; + + +/** + * @overview Object Property Type + * @description The EjsProperty type is used to store all object properties. + * It contains the property name, property linkage, propery attributes + * such as public/private, enumerable and readonly settings. It also + * contains an EjsVar to store the property data value. + * @stability Prototype. + * @library libejs + * @see Ejs, EjsVar + */ +typedef struct EjsProperty { /* Size 96 bytes in squeeze */ + /* + * EjsVar must be first. We often take the address of "var" and take + * advantage of if an EjsProperty is null, then &prop->var will be null + * also. Be WARNED. External users should use ejsGetVarPtr and + * ejsGetPropertyPtr to convert between the two. + */ + EjsVar var; /* Property value */ + + /* OPT change this to a pointer to the base class property */ + char name[EJS_MAX_ID]; /* Name */ + + uint visited : 1; /* Has been traversed */ + uint isPrivate : 1; /* Property is private */ + uint isProtected : 1; /* Property is protected */ + uint dontEnumerate : 1; /* Not enumerable */ + uint dontDelete : 1; /* Prevent delete */ + uint readonly : 1; /* Unmodifiable */ + uint allowNonUnique : 1; /* Multiple of same name ok */ + uint delayedDelete : 1; + uint reserved : 24; + + EjsPropLink link; /* Ordered linked list */ + struct EjsProperty *hashNext; /* Hash table linkage */ + + /* MOB -- is this really required */ + struct EjsObj *parentObj; /* Pointer to parent object */ + +} EjsProperty; + + +#define EJS_OP_DOT 0x1 +#define EJS_OP_INDEX 0x2 +#define EJS_OP_PLUS 0x3 +#define EJS_OP_MINUS 0x4 +#define EJS_OP_MULTIPLY 0x5 +#define EJS_OP_DIVIDE 0x6 +#define EJS_OP_CALL 0x7 + +typedef struct EjsOp { + int opType; + +} EjsOp; + +/* + * Propety Access Methods. Used per class. + * MOB -- rename EjsHelpers + */ +typedef struct EjsMethods { +#if FUTURE + int (*create)(Ejs *ep, EjsVar *thisObj); + int (*deleteProperty)(Ejs *ep, EjsVar *thisObj, const char *prop); + EjsVar *(*getProperty)(Ejs *ep, EjsVar *thisObj, const char *prop); + EjsVar *(*setProperty)(Ejs *ep, EjsVar *thisObj, const char *prop); + int (*hasProperty)(Ejs *ep, EjsVar *thisObj, const char *prop); + int (*hasInstance)(Ejs *ep, EjsVar *thisObj, const char *prop); + int (*operate)(Ejs *ep, EjsVar *thisObj, EjsOp op, EjsVar *result, + EjsVar *lhs, EjsVar *rhs, int *code); +#else + + EjsVar *(*createProperty)(Ejs *ep, EjsVar *obj, const char *property); + int (*deleteProperty)(Ejs *ep, EjsVar *obj, const char *property); + EjsVar *(*getProperty)(Ejs *ep, EjsVar *obj, const char *property); + EjsVar *(*setProperty)(Ejs *ep, EjsVar *obj, const char *property, + const EjsVar *value); + /* + * Other implemented internal properties in ECMA-262 are: + * + * [[Construct]] implemented via EjsVar methods + * [[Prototype]] implemented via EjsObj->baseClass + * [[Class]] implemented via EjsObj->baseClass->name + * [[Value]] Implemented via EjsProperty + EjsVar + EjsObj + */ + + /* + * FUTURE -- not implemented + */ + int (*canPut)(Ejs *ep, EjsVar *obj, const char *property); + int (*defaultValue)(Ejs *ep, EjsVar *obj, const char *property, + const char *hint); + int (*hasProperty)(Ejs *ep, EjsVar *obj, const char *property); + EjsVar *(*call)(Ejs *ep, EjsVar *obj, const char *property, + EjsVar *args); + int (*hasInstance)(Ejs *ep, EjsVar *obj, const char *property); + int (*scope)(Ejs *ep, EjsVar *obj, const char *property); + int (*match)(Ejs *ep, EjsVar *obj, const char *property, + const char *string, int index); +#endif +} EjsMethods; + + +/* + * Ejs Object Type + */ +typedef struct EjsObj { + /* + * GC must be first + */ + EjsGCLink gc; /* Garbage collection links */ + + union { + char *objName; /* Object name */ + char *className; /* Class name */ + }; + + struct EjsVar *baseClass; /* Pointer to base class object */ + + EjsPropLink link; /* Ordered list of properties */ + + /* OPT -- dynamically allocate this only if required */ + EjsProperty *propertyHash[EJS_OBJ_HASH_SIZE]; /* Hash chains */ + + /* OPT -- could save this and store off baseClass only */ + EjsMethods *methods; /* Property access methods */ + void *nativeData; /* Native object data */ + + int (*destructor)(Ejs *ejs, struct EjsVar *vp); + + uint numProperties : 16; /* Total count of items */ + uint visited : 1; /* Has been traversed */ + uint gcMarked : 1; /* Node marked in-use by GC */ + uint permanent : 1; /* Permanent object, dont GC */ + uint alive : 1; /* Only GC if alive */ + uint noConstructor : 1; /* Object has no constructor */ + uint dirty : 1; /* Object has been modified */ + uint hasErrors : 1; /* Update error */ + uint preventDeleteProp : 1; /* Don't allow prop deletion */ + uint delayedDeleteProp : 1; /* Delayed delete of props */ + uint reserved : 7; /* Unused */ + + Ejs *ejs; /* Owning interp */ + +#if BLD_FEATURE_MULTITHREAD + MprLock *mutex; /* Advisory mutex lock */ +#endif +} EjsObj; + + +/* + * Define a field macro so code an use numbers in a "generic" fashion. + */ +#if EJS_NUM_VAR == EJS_TYPE_INT || DOXYGEN +/* + * Default numeric type + */ +#define ejsNumber integer +#endif +#if EJS_NUM_VAR == EJS_TYPE_INT64 +/* Default numeric type */ +#define ejsNumber integer64 +#endif +#if EJS_NUM_VAR == EJS_TYPE_FLOAT +/* Default numeric type */ +#define ejsNumber floating +#endif + +typedef BLD_FEATURE_NUM_TYPE EjsNumber; + +/* + * Memory allocation slabs + */ +#define EJS_SLAB_OBJ 0 +#define EJS_SLAB_PROPERTY 1 +#define EJS_SLAB_VAR 2 +#define EJS_SLAB_MAX 3 + +/** + * Object and pointer property destructory type + */ +typedef int (*EjsDestructor)(Ejs *ejs, EjsVar *vp); + +#if BLD_FEATURE_ALLOC_LEAK_TRACK || DOXYGEN +/* + * Line number information args and declarations for ejsAlloc. + * Use EJS_LOC_ARGS in normal user code. + * Use EJS_LOC_DEC in declarations. + * Use EJS_LOC_PASS in layered APIs to pass original line info down. + */ +#define EJS_LOC_ARGS(ejs) ejs, MPR_LOC +#define EJS_LOC_DEC(ejs, loc) Ejs *ejs, const char *loc +#define EJS_LOC_PASS(ejs, loc) ejs, loc +#else +#define EJS_LOC_ARGS(ejs) ejs +#define EJS_LOC_DEC(ejs, loc) Ejs *ejs +#define EJS_LOC_PASS(ejs, loc) ejs +#endif + +/******************************* Internal Prototypes **************************/ + +#define ejsInitVar(vp, varType) \ + if (1) { \ + (vp)->type = varType; \ + (vp)->isArray = 0; \ + (vp)->flags = 0; \ + } else +extern void ejsClearVar(Ejs *ep, EjsVar *vp); + +extern int ejsDestroyObj(Ejs *ep, EjsObj *obj); +extern EjsVar *ejsCreatePropertyMethod(Ejs *ep, EjsVar *obj, + const char *name); +extern EjsVar *ejsSetPropertyMethod(Ejs *ep, EjsVar *obj, const char *name, + const EjsVar *value); +extern EjsVar *ejsGetPropertyMethod(Ejs *ep, EjsVar *obj, const char *name); +extern int ejsDeletePropertyMethod(Ejs *ep, EjsVar *obj, + const char *name); +extern void ejsSetArrayLength(Ejs *ep, EjsVar *obj, const char *creating, + const char *deleting, const EjsVar *setLength); + +/* + * At the moment, these are the same routine + */ +extern void ejsSetClassName(Ejs *ep, EjsVar *obj, const char *name); +#define ejsSetObjName ejsSetObjName + +extern bool ejsIsObjDirty(EjsVar *vp); +extern void ejsResetObjDirtyBit(EjsVar *vp); + +extern int ejsObjHasErrors(EjsVar *vp); +extern void ejsClearObjErrors(EjsVar *vp); + +extern EjsVar *ejsClearProperty(Ejs *ep, EjsVar *obj, const char *prop); + +typedef int (*EjsSortFn)(Ejs *ep, EjsProperty *p1, EjsProperty *p2, + const char *propertyName, int order); +extern void ejsSortProperties(Ejs *ep, EjsVar *obj, EjsSortFn fn, + const char *propertyName, int order); + +#if BLD_DEBUG +#define ejsSetVarName(ep, vp, varName) \ + if (1) { \ + (vp)->propertyName = varName; \ + if ((vp)->type == EJS_TYPE_OBJECT && \ + (vp)->objectState && \ + ((vp)->objectState->objName == 0)) { \ + (vp)->objectState->objName = \ + mprStrdup(ep, varName); \ + } \ + } else +#else +#define ejsSetVarName(ep, vp, varName) +#endif + +EjsVar *ejsFindProperty(Ejs *ep, EjsVar **obj, char **property, + EjsVar *global, EjsVar *local, const char *fullName, + int create); + +extern EjsVar *ejsCopyProperties(Ejs *ep, EjsVar *dest, + const EjsVar *src, EjsCopyDepth copyDepth); + +#define EJS_LINK_OFFSET ((uint) (&((EjsProperty*) 0)->link)) +#define ejsGetPropertyFromLink(lp) \ + ((EjsProperty*) ((char*) lp - EJS_LINK_OFFSET)) + +#define ejsGetObjPtr(vp) ((EjsObj*) vp->objectState) + +extern void ejsMakePropertyPrivate(EjsProperty *pp, int isPrivate); +extern void ejsMakePropertyReadOnly(EjsProperty *pp, int readonly); +extern void ejsMakePropertyUndeleteable(EjsProperty *pp, int deletable); +extern int ejsMakeObjLive(EjsVar *vp, bool alive); +extern void ejsMakeClassNoConstructor(EjsVar *vp); + +extern bool ejsBlockInUseInt(EjsVar *vp); +#if BLD_DEBUG + #define ejsBlockInUse(vp) ejsBlockInUseInt(vp) +#else + #define ejsBlockInUse(vp) +#endif + +/********************************* Prototypes *********************************/ + +/* + * Variable constructors and destructors + */ +extern EjsVar *ejsCreateBinaryStringVar(Ejs *ep, const uchar *value, + int len); +extern EjsVar *ejsCreateBoolVar(Ejs *ep, int value); +extern EjsVar *ejsCreateCMethodVar(Ejs *ep, EjsCMethod fn, + void *userData, int flags); +#if BLD_FEATURE_FLOATING_POINT +extern EjsVar *ejsCreateFloatVar(Ejs *ep, double value); +#endif +extern EjsVar *ejsCreateIntegerVar(Ejs *ep, int value); +#if BLD_FEATURE_INT64 +extern EjsVar *ejsCreateInteger64Var(Ejs *ep, int64 value); +#endif + +extern EjsVar *ejsCreateMethodVar(Ejs *ep, const char *body, + MprArray *args, int flags); +extern EjsVar *ejsCreateNullVar(Ejs *ep); +extern EjsVar *ejsCreateNumberVar(Ejs *ep, EjsNumber value); + +#define ejsCreateObjVar(ep) \ + ejsCreateObjVarInternal(EJS_LOC_ARGS(ep)) +extern EjsVar *ejsCreateObjVarInternal(EJS_LOC_DEC(ep, loc)); + +extern EjsVar *ejsCreatePtrVar(Ejs *ep, void *ptr, EjsDestructor dest); + +extern EjsVar *ejsCreateStringCMethodVar(Ejs *ep, EjsStringCMethod fn, + void *userData, int flags); + +#define ejsCreateStringVar(ep, value) \ + ejsCreateStringVarInternal(EJS_LOC_ARGS(ep), value) +extern EjsVar *ejsCreateStringVarInternal(EJS_LOC_DEC(ep, loc), + const char *value); + +extern EjsVar *ejsCreateUndefinedVar(Ejs *ep); + +/* MOB -- naming. Should be Create/Destroy */ +extern void ejsFreeVar(Ejs *ep, EjsVar *vp); + +/* + * Var support routines + */ +extern int ejsGetVarFlags(EjsVar *vp); +extern void ejsSetVarFlags(EjsVar *obj, int flags); + +extern EjsType ejsGetVarType(EjsVar *vp); +extern const char *ejsGetVarTypeAsString(EjsVar *vp); + +extern void *ejsGetCMethodUserData(EjsVar *obj); +extern void ejsSetCMethodUserData(EjsVar *obj, void *userData); + +extern void *ejsGetVarUserPtr(EjsVar *vp); +extern void ejsSetVarUserPtr(EjsVar *vp, void *data); + + +/* + * Variable access and manipulation. These work on standalone objects. + */ +#define ejsDupVar(ep, src, copyDepth) \ + ejsDupVarInternal(EJS_LOC_ARGS(ep), src, copyDepth) +extern EjsVar *ejsDupVarInternal(EJS_LOC_DEC(ep, loc), EjsVar *src, + EjsCopyDepth copyDepth); +#define ejsWriteVar(ep, dest, src, copyDepth) \ + ejsWriteVarInternal(EJS_LOC_ARGS(ep), dest, src, copyDepth) +extern EjsVar *ejsWriteVarInternal(EJS_LOC_DEC(ep, loc), EjsVar *dest, + const EjsVar *src, EjsCopyDepth copyDepth); +extern EjsVar *ejsWriteVarAsBinaryString(Ejs *ep, EjsVar *dest, + const uchar *value, int len); +extern EjsVar *ejsWriteVarAsBoolean(Ejs *ep, EjsVar *dest, bool value); +extern EjsVar *ejsWriteVarAsCMethod(Ejs *ep, EjsVar *dest, EjsCMethod fn, + void *userData, int flags); +#if BLD_FEATURE_FLOATING_POINT +extern EjsVar *ejsWriteVarAsFloat(Ejs *ep, EjsVar *dest, double value); +#endif +extern EjsVar *ejsWriteVarAsInteger(Ejs *ep, EjsVar *dest, int value); +#if BLD_FEATURE_INT64 +extern EjsVar *ejsWriteVarAsInteger64(Ejs *ep, EjsVar *dest, int64 value); +#endif +extern EjsVar *ejsWriteVarAsMethod(Ejs *ep, EjsVar *dest, + const char *body, MprArray *args); +extern EjsVar *ejsWriteVarAsNull(Ejs *ep, EjsVar *dest); +extern EjsVar *ejsWriteVarAsNumber(Ejs *ep, EjsVar *dest, EjsNum value); +#define ejsWriteVarAsString(ep, dest, value) \ + ejsWriteVarAsStringInternal(EJS_LOC_ARGS(ep), dest, value) +extern EjsVar *ejsWriteVarAsStringInternal(EJS_LOC_DEC(ep, loc), + EjsVar *dest, const char *value); +extern EjsVar *ejsWriteVarAsStringCMethod(Ejs *ep, EjsVar *dest, + EjsStringCMethod fn, void *userData, int flags); +extern EjsVar *ejsWriteVarAsUndefined(Ejs *ep, EjsVar *dest); + +/* + * These routines do not convert types + */ +/* MOB -- make this a fn and pass back the length as an arg */ +#define ejsReadVarAsBinaryString(vp) ((const uchar*) (vp->ustring)); +#define ejsReadVarAsBoolean(vp) (vp->boolean); +#define ejsReadVarAsCMethod(vp) (vp->cMethod); +#if BLD_FEATURE_FLOATING_POINT +#define ejsReadVarAsFloat(vp) (vp->floating); +#endif +#define ejsReadVarAsInteger(vp) (vp->integer); +#if BLD_FEATURE_INT64 +#define ejsReadVarAsInteger64(vp) (vp->int64); +#endif +#define ejsReadVarAsString(vp) ((const char*) (vp->string)); +#define ejsReadVarAsStringCMethod(vp) (vp->cMethodWithStrings); +/* MOB -- remove this fn */ +#define ejsReadVarStringLength(vp) (vp->length); + +/* + * Object property creation routines + */ +extern EjsProperty *ejsCreateProperty(Ejs *ep, EjsVar *obj, const char *prop); +extern EjsProperty *ejsCreateSimpleProperty(Ejs *ep, EjsVar *obj, + const char *prop); +extern EjsProperty *ejsCreateSimpleNonUniqueProperty(Ejs *ep, EjsVar *obj, + const char *prop); +/* MOB -- should be destroy */ +extern int ejsDeleteProperty(Ejs *ep, EjsVar *obj, const char *prop); + + +/* + * Get property routines + */ +extern EjsProperty *ejsGetProperty(Ejs *ep, EjsVar *obj, const char *prop); +extern EjsProperty *ejsGetSimpleProperty(Ejs *ep, EjsVar *obj, + const char *prop); + +extern EjsVar *ejsGetPropertyAsVar(Ejs *ep, EjsVar *obj, + const char *prop); +extern int ejsGetPropertyCount(EjsVar *obj); + +extern const uchar *ejsGetPropertyAsBinaryString(Ejs *ep, EjsVar *obj, + const char *prop, int *length); +extern bool ejsGetPropertyAsBoolean(Ejs *ep, EjsVar *obj, + const char *prop); +extern int ejsGetPropertyAsInteger(Ejs *ep, EjsVar *obj, + const char *prop); +extern int64 ejsGetPropertyAsInteger64(Ejs *ep, EjsVar *obj, + const char *prop); +extern EjsNum ejsGetPropertyAsNumber(Ejs *ep, EjsVar *obj, + const char *prop); +extern void *ejsGetPropertyAsPtr(Ejs *ep, EjsVar *obj, + const char *prop); +extern const char *ejsGetPropertyAsString(Ejs *ep, EjsVar *obj, + const char *prop); + +/* + * Object property update routines + */ +extern EjsProperty *ejsSetBaseProperty(Ejs *ep, EjsVar *obj, const char *prop, + const EjsVar *value); +extern EjsProperty *ejsSetProperty(Ejs *ep, EjsVar *obj, const char *prop, + const EjsVar *value); +extern EjsProperty *ejsSetPropertyAndFree(Ejs *ep, EjsVar *obj, + const char *prop, EjsVar *value); +extern EjsProperty *ejsSetPropertyToBinaryString(Ejs *ep, EjsVar *obj, + const char *prop, const uchar *value, int len); +extern EjsProperty *ejsSetPropertyToBoolean(Ejs *ep, EjsVar *obj, + const char *prop, bool value); +extern EjsProperty *ejsSetPropertyToCMethod(Ejs *ep, EjsVar *obj, + const char *prop, EjsCMethod fn, void *userData, + int flags); +#if BLD_FEATURE_FLOATING_POINT +extern EjsProperty *ejsSetPropertyToFloat(Ejs *ep, EjsVar *obj, + const char *prop, double value); +#endif +extern EjsProperty *ejsSetPropertyToInteger(Ejs *ep, EjsVar *obj, + const char *prop, int value); +#if BLD_FEATURE_INT64 +extern EjsProperty *ejsSetPropertyToInteger64(Ejs *ep, EjsVar *obj, + const char *prop, int64 value); +#endif +extern EjsProperty *ejsSetPropertyToMethod(Ejs *ep, EjsVar *obj, + const char *prop, const char *body, MprArray *args, + int flags); +extern EjsProperty *ejsSetPropertyToNewObj(Ejs *ep, EjsVar *obj, + const char *prop, const char *className, + MprArray *args); +extern EjsProperty *ejsSetPropertyToNull(Ejs *ep, EjsVar *obj, + const char *prop); +extern EjsProperty *ejsSetPropertyToNumber(Ejs *ep, EjsVar *obj, + const char *prop, EjsNum value); +extern EjsProperty *ejsSetPropertyToObj(Ejs *ep, EjsVar *obj, + const char *prop); +extern EjsProperty *ejsSetPropertyToPtr(Ejs *ep, EjsVar *obj, + const char *prop, void *ptr, EjsDestructor destructor); + +extern EjsProperty *ejsSetPropertyToStringCMethod(Ejs *ep, EjsVar *obj, + const char *prop, EjsStringCMethod fn, + void *userData, int flags); +extern EjsProperty *ejsSetPropertyToString(Ejs *ep, EjsVar *obj, + const char *prop, const char *value); +extern EjsProperty *ejsSetPropertyToUndefined(Ejs *ep, EjsVar *obj, + const char *prop); + + +/* Convenience function */ +extern EjsVar *ejsSetPropertyToObjAsVar(Ejs *ep, EjsVar *obj, + const char *prop); +extern void ejsSetObjDestructor(Ejs *ep, EjsVar *obj, + EjsDestructor destructor); +extern void ejsClearObjDestructor(Ejs *ep, EjsVar *obj); + +/* + * Enumeration of properties + * MOB -- should these take an ejs parameter to be consistent + */ +extern EjsProperty *ejsGetFirstProperty(const EjsVar *obj, int flags); +extern EjsProperty *ejsGetNextProperty(EjsProperty *last, int flags); + +/* + * Method definition and control. + */ +extern EjsProperty *ejsDefineMethod(Ejs *ep, EjsVar *obj, const char *prop, + const char *body, MprArray *args); +extern EjsProperty *ejsDefineCMethod(Ejs *ep, EjsVar *obj, const char *prop, + EjsCMethod fn, int flags); + +extern EjsProperty *ejsDefineStringCMethod(Ejs *ep, EjsVar *obj, + const char *prop, EjsStringCMethod fn, int flags); + +extern EjsProperty *ejsDefineAccessors(Ejs *ep, EjsVar *obj, + const char *prop, const char *getBody, + const char *setBody); +extern EjsProperty *ejsDefineCAccessors(Ejs *ep, EjsVar *obj, + const char *prop, EjsCMethod getFn, EjsCMethod setFn, + int flags); + +/* + * Macro to get the variable value portion of a property + */ +#define ejsGetVarPtr(pp) (&((pp)->var)) +#define ejsGetPropertyPtr(vp) ((EjsProperty*) vp) + +/* MOB -- take ejs to be consistent */ +extern int ejsMakePropertyEnumerable(EjsProperty *pp, bool enumerable); +extern int ejsMakeObjPermanent(EjsVar *vp, bool permanent); + + +/* + * Var conversion routines + * MOB -- should these take an Ejs as first arg for consistency + */ +extern bool ejsVarToBoolean(EjsVar *vp); +#if BLD_FEATURE_FLOATING_POINT +extern double ejsVarToFloat(EjsVar *vp); +#endif +extern int ejsVarToInteger(EjsVar *vp); +#if BLD_FEATURE_INT64 +extern int64 ejsVarToInteger64(EjsVar *vp); +#endif +extern EjsNum ejsVarToNumber(EjsVar *vp); +extern char *ejsVarToString(Ejs *ep, EjsVar *vp); +extern char *ejsVarToStringEx(Ejs *ep, EjsVar *vp, bool *alloc); +extern char *ejsFormatVar(Ejs *ep, const char *fmt, EjsVar *vp); + +#if BLD_FEATURE_FLOATING_POINT +extern double ejsParseFloat(const char *str); +#endif +/* + * Parsing and type range checking routines + */ +extern bool ejsParseBoolean(const char *str); +extern int ejsParseInteger(const char *str); +#if BLD_FEATURE_INT64 +extern int64 ejsParseInteger64(const char *str); +#endif +extern EjsNum ejsParseNumber(const char *str); +extern EjsVar *ejsParseVar(Ejs *ep, const char *str, EjsType prefType); + +#if BLD_FEATURE_FLOATING_POINT +extern bool ejsIsInfinite(double f); +extern bool ejsIsNan(double f); +#endif + +/* + * Advisory locking support + */ +#if BLD_FEATURE_MULTITHREAD +extern void ejsLockObj(EjsVar *vp); +extern void ejsUnlockObj(EjsVar *vp); +#endif + +/* + * Just for debugging + */ +extern bool ejsObjIsCollectable(EjsVar *vp); + +#ifdef __cplusplus +} +#endif + +/*****************************************************************************/ +#endif /* _h_EJS_VAR */ + +/* + * 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 + */ diff --git a/source4/lib/appweb/ejs-2.0/ejs/lib/event.js b/source4/lib/appweb/ejs-2.0/ejs/lib/event.js new file mode 100644 index 0000000000..283a3ec72f --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/lib/event.js @@ -0,0 +1,141 @@ +/* + * @file event.js + * @brief Event class + * @copy Copyright (c) Mbedthis Software LLC, 2005-2006. All Rights Reserved. + * + * Usage: + * listener = new System.Listener(); + * listener.onClick = function() { + * // Any code here + * } + * eventTarget.addListener(eventName, listener); + * or + * listener = new System.Listener(obj, method); + * eventTarget.addListener(eventName, listener); + * + * To fire events: + * eventTarget.fire(eventName, new System.Event("My Event")); + */ + +/******************************************************************************/ +/* + * Base event class + */ +class System.Event +{ + var type; // keyboard + var timeStamp; + var arg; + + /* MOB -- constructor should take a type */ + function Event(arg) + { + timeStamp = time(); + type = "default"; + this.arg = arg; + } +} + +/* MOB -- should not be needed */ +Event = System.Event; + +class System.Listener +{ + var obj; + var method; + + function Listener(obj, method) + { + if (arguments.length >= 1) { + this.obj = obj; + } else { + this.obj = this; + } + if (arguments.length == 2) { + this.method = method; + } else { + this.method = "onEvent"; + } + } +} +/* MOB -- should not be needed */ +Listener = System.Listener; + + +/* + * The Event target class + */ +class System.EventTarget +{ + // Private + var events; /* Hash of a event names */ + + function EventTarget() + { + events = new Object(); + } + + // Public + function addListener(eventName, listener) + { + var listeners = events[eventName]; + if (listeners == undefined) { + listeners = events[eventName] = new Array(); + } + if (arguments.length == 2) { + var method = eventName; + } + /* MOB OPT */ + for (var i = 0; i < listeners.length; i++) { + var l = listeners[i]; + if (l == listener) { + return; + } + } + listeners[listeners.length] = listener; + } + + function removeListener(eventName, listener) + { + var listeners = events[eventName]; + + if (listeners == undefined) { + return; + } + + for (var i = 0; i < listeners.length; i++) { + var l = listeners[i]; + if (l == listener) { + // MOB -- want listeners.splice here + // listeners.splice(i, 1); + for (var j = i; j < (listeners.length - 1); j++) { + listeners[j] = listeners[j + 1]; + } + delete listeners[listeners.length - 1]; + i = listeners.length; + } + } + } + + function fire(eventName, event) + { + var listeners = events[eventName]; + + if (listeners == undefined) { + // println("Event.fire(): unknown eventName " + eventName); + return; + } + + for (var i = listeners.length - 1; i >= 0; i--) { + var listener = listeners[i]; + var method = listener.obj[listener.method]; + if (method == undefined) { + throw new EvalError("Undefined method: " + listener.method); + } + listener.obj[listener.method](listener, event); + } + } +} + +/* MOB -- should not be needed */ +EventTarget = System.EventTarget; diff --git a/source4/lib/appweb/ejs-2.0/ejs/lib/global.js b/source4/lib/appweb/ejs-2.0/ejs/lib/global.js new file mode 100644 index 0000000000..f2daaa57c0 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/lib/global.js @@ -0,0 +1,34 @@ +/* + * @file global.js + * @brief Misc global functions + * @copy Copyright (c) Mbedthis Software LLC, 2005-2006. All Rights Reserved. + */ + +/******************************************************************************/ + +function min(a, b) +{ + if (a < b) { + return a; + } else { + return b; + } +} + + +function max(a, b) +{ + if (a > b) { + return a; + } else { + return b; + } +} + +function abs(a) +{ + if (a < 0) { + return -a; + } + return a; +} diff --git a/source4/lib/appweb/ejs-2.0/ejs/lib/startup.js b/source4/lib/appweb/ejs-2.0/ejs/lib/startup.js new file mode 100644 index 0000000000..e627a96e04 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/lib/startup.js @@ -0,0 +1,15 @@ +/* + * @file startup.js + * @brief Embedded JavaScript Startup Code + * @copy Copyright (c) Mbedthis Software LLC, 2005-2006. All Rights Reserved. + * + * Invoked automatically on startup. + */ + +/******************************************************************************/ + +// println("Loading startup.js ..."); + +include("lib/event.js"); +include("lib/global.js"); +include("lib/timer.js"); diff --git a/source4/lib/appweb/ejs-2.0/ejs/lib/timer.js b/source4/lib/appweb/ejs-2.0/ejs/lib/timer.js new file mode 100644 index 0000000000..f4cb8b12ce --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/lib/timer.js @@ -0,0 +1,158 @@ +/* + * @file timer.js + * @brief Timer class + * @copy Copyright (c) Mbedthis Software LLC, 2005-2006. All Rights Reserved. + * + * Usage: + * timer = new System.Timer("name", period); + * timer.onTick = function(arg) { + * // Anything here + * } + * timer.start(); + * or + * + * timer = new System.Timer("name", period, obj, method); + * timer.start(); + */ + +/******************************************************************************/ + +class System.Timer +{ + var id; + + /* MOB -- really need accessor on period. If user updates period, + then due must be updated. */ + var period; + var due; + var runOnce; // Run timer just once + var method; // Callback method + var obj; // Callback object + + function Timer(id, period, obj, method) + { + this.id = id; + this.period = period; + due = time() + period; + + if (arguments.length >= 3) { + this.obj = obj; + } else { + this.obj = this; + } + if (arguments.length >= 4) { + this.method = method; + } else { + this.method = "onTick"; + } + } + + /* MOB this should be deprecated */ + function reschedule(period) + { + /* MOB -- should update the timer service somehow */ + this.period = period; + } + + function run(now) + { + if (obj[method] == undefined) { + trace("Timer cant find timer method " + method); + due = now + this.period; + return; + } + + /* + * Run the timer + */ + try { + obj[method](this); + } + catch (error) { + trace("Timer exception: " + error); + } + + if (runOnce) { + timerService.removeTimer(this); + + } else { + due = now + this.period; + } + } + + function start() + { + if (obj[method] == undefined) { + throw new Error("Callback method is undefined"); + } else { + timerService.addTimer(this); + } + } + + function stop() + { + timerService.removeTimer(this); + } + +} + +/* MOB -- should not need this */ +Timer = System.Timer; + + +/* + * Timer service + */ +class System.TimerService +{ + var timers; + var nextDue; + + function TimerService() + { + timers = new Object(); + nextDue = 0; + global.timerService = this; + } + + function addTimer(timer) + { + timers[timer.id] = timer; + } + + function removeTimer(timer) + { + try { + delete timers[timer.id]; + } + catch {} + } + + function getIdleTime() + { + return nextDue - time(); + } + + function runTimers() + { + var now = time(); + + nextDue = 2147483647; /* MOB -- MATH.MAX_INT; */ + + for each (var timer in timers) + { + if (timer.due < now) { + timer.run(now); + } + } + for each (var timer in timers) + { + if (timer.due < nextDue) { + nextDue = timer.due; + } + } + // println("runTimers leaving with " + (nextDue - now)); + return nextDue - time(); + } +} +TimerService = System.TimerService; diff --git a/source4/lib/appweb/ejs-2.0/ejs/system/.ignore b/source4/lib/appweb/ejs-2.0/ejs/system/.ignore new file mode 100755 index 0000000000..fb5a29031e --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/system/.ignore @@ -0,0 +1 @@ +.updated diff --git a/source4/lib/appweb/ejs-2.0/ejs/system/Makefile b/source4/lib/appweb/ejs-2.0/ejs/system/Makefile new file mode 100755 index 0000000000..2d83662655 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/system/Makefile @@ -0,0 +1,27 @@ +# +# Makefile to build the EJS Object Model +# +# Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved. +# + +COMPILE := *.c +EXPORT_OBJECTS := yes +MAKE_IFLAGS := -I.. -I../../mpr -I../../exml + +include make.dep + +ifeq ($(BLD_HOST_UNIX),1) +PRE_DIRS = UNIX +else +PRE_DIRS = $(BLD_HOST_OS) +endif + +compileExtra: .updated + +.updated: $(FILES) + @touch .updated + +## Local variables: +## tab-width: 4 +## End: +## vim: tw=78 sw=4 ts=4 diff --git a/source4/lib/appweb/ejs-2.0/ejs/system/README.TXT b/source4/lib/appweb/ejs-2.0/ejs/system/README.TXT new file mode 100644 index 0000000000..a24e0e299c --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/system/README.TXT @@ -0,0 +1,63 @@ +Embedded JavaScript System Model + + +- Need args, arg types and exceptions thrown +- Error classes + +class Global + class System + class environment + var + class GC + void function run() + function tune() + function getUsedMemory() // Should be properties + function getAllocatedMemory() // Should be properties + + var javascript + var null + var undefined + var true + var false + var Nan + var Infinity + + function random // Not implemented + function sleep // Not implemented + function exit + function yield // Not implemented + + Debug + isDebugMode + + Limits + isLimitsMode // Not implemented + stack // Not implemented + heap // Not implemented + flash // Not implemented + + Memory + getUsedMemory() // Should be properties + getAvailableMemory() // Should be properties + used + flash // Not implemented + + assert() + breakpoint() + dirname() + basename() + eval() + exit() + print() + println() + printVars() + sleep() + sort() + time() + typeof() + include() + trace() + printf() // Not implemented + sprintf() + + diff --git a/source4/lib/appweb/ejs-2.0/ejs/system/UNIX/.ignore b/source4/lib/appweb/ejs-2.0/ejs/system/UNIX/.ignore new file mode 100644 index 0000000000..fb5a29031e --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/system/UNIX/.ignore @@ -0,0 +1 @@ +.updated diff --git a/source4/lib/appweb/ejs-2.0/ejs/system/UNIX/Makefile b/source4/lib/appweb/ejs-2.0/ejs/system/UNIX/Makefile new file mode 100755 index 0000000000..424747052a --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/system/UNIX/Makefile @@ -0,0 +1,21 @@ +# +# Makefile to build the EJS Object Model for WIN +# +# Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved. +# + +COMPILE := *.c +EXPORT_OBJECTS := yes +MAKE_IFLAGS := -I../.. -I../../../mpr + +include make.dep + +compileExtra: .updated + +.updated: $(FILES) + @touch .updated + +## Local variables: +## tab-width: 4 +## End: +## vim: tw=78 sw=4 ts=4 diff --git a/source4/lib/appweb/ejs-2.0/ejs/system/UNIX/ejsFile.c b/source4/lib/appweb/ejs-2.0/ejs/system/UNIX/ejsFile.c new file mode 100644 index 0000000000..772303152e --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/system/UNIX/ejsFile.c @@ -0,0 +1,98 @@ +/* + * @file ejsFile.c + * @brief File class for the EJ System Object Model + */ +/********************************** Copyright *********************************/ +/* + * Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved. + */ +/********************************** Includes **********************************/ + +#include "ejs.h" + +/******************************************************************************/ +/* + * Default Constructor + */ + +/******************************************************************************/ +/************************************ Methods *********************************/ +/******************************************************************************/ +/* + * function open(); + */ + +static int openProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + ejsTrace(ep, "File.open()\n"); + return 0; +} + +/******************************************************************************/ +/* + * function close(); + */ + +static int closeProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + ejsTrace(ep, "File.close()\n"); + return 0; +} + +/******************************************************************************/ +/* + * function read(); + */ + +static int readProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + ejsTrace(ep, "File.read()\n"); + return 0; +} + +/******************************************************************************/ +/* + * function write(); + */ + +static int writeProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + ejsTrace(ep, "File.write()\n"); + return 0; +} + +/******************************************************************************/ +/******************************** Initialization ******************************/ +/******************************************************************************/ + +int ejsDefineFileClass(Ejs *ep) +{ + EjsVar *fileClass; + + fileClass = ejsDefineClass(ep, "File", "Object", 0); + if (fileClass == 0) { + return MPR_ERR_CANT_INITIALIZE; + } + + /* + * Define the methods + */ + ejsDefineCMethod(ep, fileClass, "open", openProc, 0); + ejsDefineCMethod(ep, fileClass, "close", closeProc, 0); + ejsDefineCMethod(ep, fileClass, "read", readProc, 0); + ejsDefineCMethod(ep, fileClass, "write", writeProc, 0); + + return ejsObjHasErrors(fileClass) ? MPR_ERR_CANT_INITIALIZE: 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 + */ diff --git a/source4/lib/appweb/ejs-2.0/ejs/system/UNIX/ejsFileSystem.c b/source4/lib/appweb/ejs-2.0/ejs/system/UNIX/ejsFileSystem.c new file mode 100755 index 0000000000..7b39c16e4d --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/system/UNIX/ejsFileSystem.c @@ -0,0 +1,454 @@ +/* + * @file ejsFileSystem.c + * @brief FileSystem class for the EJ System Object Model + * MOB -- this is almost the same as for Windows. Should common up. + */ +/********************************** Copyright *********************************/ +/* + * Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved. + */ +/********************************** Includes **********************************/ + +#include "ejs.h" +#include <dirent.h> + +/******************************************************************************/ +/************************************ Methods *********************************/ +/******************************************************************************/ +/* + * function void access(string path); + * MOB - API insufficient. Access for read or write? + */ + +static int accessProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + int rc; + + if (argc != 1 || !ejsVarIsString(argv[0])) { + ejsError(ejs, EJS_ARG_ERROR, "Bad usage: access(path)"); + return -1; + } + + rc = access(argv[0]->string, 04); + + ejsSetReturnValueToBoolean(ejs, (rc == 0) ? 1 : 0); + return 0; +} + +/******************************************************************************/ +/* + * function void mkdir(string path); + */ + +static int mkdirProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + if (argc != 1 || !ejsVarIsString(argv[0])) { + ejsError(ejs, EJS_ARG_ERROR, "Bad usage: mkdir(path)"); + return -1; + } + + if (mprMakeDirPath(ejs, argv[0]->string) < 0) { + ejsError(ejs, EJS_IO_ERROR, "Cant create directory"); + return -1; + } + return 0; +} + +/******************************************************************************/ +/* + * function void rmdir(string path); + */ + +static int rmdirProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + int rc; + + if (argc != 1 || !ejsVarIsString(argv[0])) { + ejsError(ejs, EJS_ARG_ERROR, "Bad usage: mkdir(path)"); + return -1; + } + + rc = mprDeleteDir(ejs, argv[0]->string); + + if (rc < 0) { + ejsError(ejs, EJS_IO_ERROR, "Cant remove directory"); + return -1; + } + return 0; +} + +/******************************************************************************/ +/* + * function void dirList(string path, [bool enumDirs]); + * MOB -- need pattern to match (what about "." and ".." and ".*" + */ + +static int dirListProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + DIR *dir; + struct dirent *dirent; + char path[MPR_MAX_FNAME]; + EjsVar *array, *vp; + uchar enumDirs; + + if (argc < 1 || !ejsVarIsString(argv[0])) { + ejsError(ejs, EJS_ARG_ERROR, "Bad usage: dirList(path)"); + return -1; + } + if (argc == 2) { + enumDirs = ejsVarToBoolean(argv[1]); + } else { + enumDirs = 0; + } + array = ejsCreateArray(ejs, 0); + ejsMakeObjPermanent(array, 1); + + /* + * First collect the files + */ + mprSprintf(path, sizeof(path), "%s/*.*", argv[0]->string); + + dir = opendir(path); + if (dir == 0) { + ejsError(ejs, EJS_ARG_ERROR, "Can't enumerate dirList(path)"); + return -1; + } + + while ((dirent = readdir(dir)) != 0) { + if (dirent->d_name[0] == '.') { + continue; + } + if (!enumDirs || (dirent->d_type & DT_DIR)) { + mprSprintf(path, sizeof(path), "%s/%s", argv[0]->string, + dirent->d_name); + vp = ejsCreateStringVar(ejs, path); + ejsAddArrayElt(ejs, array, vp, EJS_SHALLOW_COPY); + ejsFreeVar(ejs, vp); + } + } + + closedir(dir); + + ejsSetReturnValue(ejs, array); + ejsMakeObjPermanent(array, 0); + + /* + * Can free now as the return value holds the reference + */ + ejsFreeVar(ejs, array); + + return 0; +} + +/******************************************************************************/ +/* + * function void getFreeSpace(); + */ + +static int getFreeSpaceProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ +#if UNUSED + MprApp *app; + uint space; + + app = mprGetApp(ejs); + space = IFILEMGR_GetFreeSpace(app->fileMgr, 0); + ejsSetReturnValueToInteger(ejs, space); +#endif + + return 0; +} + +/******************************************************************************/ +/* + * function void writeFile(string path, var data); + */ + +static int writeFileProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + MprFile *file; + char *data, *buf; + int bytes, length, rc; + + if (argc != 2 || !ejsVarIsString(argv[0])) { + ejsError(ejs, EJS_ARG_ERROR, "Bad usage: writeFile(path, var)"); + return -1; + } + + if (ejsVarIsString(argv[1])) { + data = argv[1]->string; + length = argv[1]->length; + buf = 0; + } else { + buf = data = ejsVarToString(ejs, argv[1]); + length = strlen(data); + } + + /* + * Create fails if already present + */ + rc = mprDelete(ejs, argv[0]->string); + file = mprOpen(ejs, argv[0]->string, O_CREAT | O_WRONLY | O_BINARY, 0664); + if (file == 0) { + ejsError(ejs, EJS_IO_ERROR, "Cant create %s", argv[0]->string); + mprFree(buf); + return -1; + } + + rc = 0; + bytes = mprWrite(file, data, length); + if (bytes != length) { + ejsError(ejs, EJS_IO_ERROR, "Write error to %s", argv[1]->string); + rc = -1; + } + + mprClose(file); + + mprFree(buf); + return rc; +} + +/******************************************************************************/ +/* + * function string readFile(string path); + */ + +static int readFileProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + MprApp *app; + MprFile *file; + MprBuf *buf; + char *data; + int bytes, rc; + + if (argc != 1 || !ejsVarIsString(argv[0])) { + ejsError(ejs, EJS_ARG_ERROR, "Bad usage: readFile(path)"); + return -1; + } + buf = mprCreateBuf(ejs, MPR_BUF_INCR, MPR_MAX_BUF); + if (buf == 0) { + ejsMemoryError(ejs); + return -1; + } + + data = mprAlloc(ejs, MPR_BUFSIZE); + if (buf == 0) { + mprFree(buf); + ejsMemoryError(ejs); + return -1; + } + + app = mprGetApp(ejs); + file = mprOpen(ejs, argv[0]->string, O_RDONLY, 0664); + if (file == 0) { + ejsError(ejs, EJS_IO_ERROR, "Cant open %s", argv[0]->string); + mprFree(buf); + return -1; + } + + rc = 0; + while ((bytes = mprRead(file, data, MPR_BUFSIZE)) > 0) { + if (mprPutBlockToBuf(buf, data, bytes) != bytes) { + ejsError(ejs, EJS_IO_ERROR, "Write error to %s", argv[1]->string); + rc = -1; + break; + } + } + + ejsSetReturnValueToBinaryString(ejs, (uchar*) mprGetBufStart(buf), + mprGetBufLength(buf)); + + mprClose(file); + mprFree(data); + mprFree(buf); + + return rc; +} + +/******************************************************************************/ +/* + * function void remove(string path); + */ + +static int removeProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + int rc; + + if (argc != 1 || !ejsVarIsString(argv[0])) { + ejsError(ejs, EJS_ARG_ERROR, "Bad usage: remove(path)"); + return -1; + } + + rc = unlink(argv[0]->string); + if (rc < 0) { + ejsError(ejs, EJS_IO_ERROR, "Cant remove file"); + return -1; + } + return 0; +} + +/******************************************************************************/ +/* + * function void rename(string from, string to); + */ + +static int renameProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + int rc; + + if (argc != 2 || !ejsVarIsString(argv[0]) || !ejsVarIsString(argv[1])) { + ejsError(ejs, EJS_ARG_ERROR, "Bad usage: rename(old, new)"); + return -1; + } + + unlink(argv[1]->string); + rc = rename(argv[0]->string, argv[1]->string); + if (rc < 0) { + ejsError(ejs, EJS_IO_ERROR, "Cant rename file"); + return -1; + } + return 0; +} + +/******************************************************************************/ +/* + * function void copy(string old, string new); + */ + +static int copyProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + MprFile *from, *to; + char *buf; + uint bytes; + int rc; + + if (argc != 2 || !ejsVarIsString(argv[0]) || !ejsVarIsString(argv[1])) { + ejsError(ejs, EJS_ARG_ERROR, "Bad usage: copy(old, new)"); + return -1; + } + + buf = mprAlloc(ejs, MPR_BUFSIZE); + if (buf == 0) { + ejsMemoryError(ejs); + return -1; + } + + from = mprOpen(ejs, argv[0]->string, O_RDONLY | O_BINARY, 0664); + if (from == 0) { + ejsError(ejs, EJS_IO_ERROR, "Cant open %s", argv[0]->string); + mprFree(buf); + return -1; + } + + to = mprOpen(ejs, argv[1]->string, O_CREAT | O_BINARY, 0664); + if (to == 0) { + ejsError(ejs, EJS_IO_ERROR, "Cant create %s", argv[1]->string); + mprClose(from); + mprFree(buf); + return -1; + } + + rc = 0; + while ((bytes = mprRead(from, buf, MPR_BUFSIZE)) > 0) { + if (mprWrite(to, buf, bytes) != bytes) { + ejsError(ejs, EJS_IO_ERROR, "Write error to %s", argv[1]->string); + rc = -1; + break; + } + } + + mprClose(from); + mprClose(to); + mprFree(buf); + + return rc; +} + +/******************************************************************************/ +/* + * function FileInfo getFileInfo(string path); + * + * MOB -- should create a real class FileInfo + */ + +static int getFileInfoProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + MprFileInfo info; + EjsVar *fileInfo; + int rc; + + if (argc != 1 || !ejsVarIsString(argv[0])) { + ejsError(ejs, EJS_ARG_ERROR, "Bad usage: getFileInfo(path)"); + return -1; + } + + fileInfo = ejsCreateObjVar(ejs); + if (fileInfo == 0) { + ejsMemoryError(ejs); + return -1; + } + ejsMakeObjPermanent(fileInfo, 1); + + rc = mprGetFileInfo(ejs, argv[0]->string, &info); + if (rc < 0) { + ejsMakeObjPermanent(fileInfo, 0); + ejsFreeVar(ejs, fileInfo); + ejsError(ejs, EJS_IO_ERROR, "Cant get file info for %s", + argv[0]->string); + return -1; + } + + ejsSetPropertyToInteger(ejs, fileInfo, "created", info.ctime); + ejsSetPropertyToInteger(ejs, fileInfo, "length", info.size); + ejsSetPropertyToBoolean(ejs, fileInfo, "isDir", info.isDir); + + ejsSetReturnValue(ejs, fileInfo); + ejsMakeObjPermanent(fileInfo, 0); + + return 0; +} + +/******************************************************************************/ +/******************************** Initialization ******************************/ +/******************************************************************************/ + +int ejsDefineFileSystemClass(Ejs *ejs) +{ + EjsVar *fileSystemClass; + + fileSystemClass = ejsDefineClass(ejs, "FileSystem", "Object", 0); + if (fileSystemClass == 0) { + return MPR_ERR_CANT_INITIALIZE; + } + + /* + * Define the methods + */ + ejsDefineCMethod(ejs, fileSystemClass, "access", accessProc, 0); + ejsDefineCMethod(ejs, fileSystemClass, "mkdir", mkdirProc, 0); + ejsDefineCMethod(ejs, fileSystemClass, "rmdir", rmdirProc, 0); + ejsDefineCMethod(ejs, fileSystemClass, "dirList", dirListProc, 0); + ejsDefineCMethod(ejs, fileSystemClass, "writeFile", writeFileProc, 0); + ejsDefineCMethod(ejs, fileSystemClass, "readFile", readFileProc, 0); + ejsDefineCMethod(ejs, fileSystemClass, "remove", removeProc, 0); + ejsDefineCMethod(ejs, fileSystemClass, "rename", renameProc, 0); + ejsDefineCMethod(ejs, fileSystemClass, "copy", copyProc, 0); + ejsDefineCMethod(ejs, fileSystemClass, "getFileInfo", getFileInfoProc, 0); + + // MOB -- should be a property with accessor + ejsDefineCMethod(ejs, fileSystemClass, "getFreeSpace", getFreeSpaceProc, 0); + + return ejsObjHasErrors(fileSystemClass) ? MPR_ERR_CANT_INITIALIZE: 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 + */ diff --git a/source4/lib/appweb/ejs-2.0/ejs/system/UNIX/ejsHTTP.c b/source4/lib/appweb/ejs-2.0/ejs/system/UNIX/ejsHTTP.c new file mode 100755 index 0000000000..25821f6960 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/system/UNIX/ejsHTTP.c @@ -0,0 +1,488 @@ +/* + * @file ejsHTTP.c + * @brief HTTP class for the EJ System Object Model + */ +/********************************** Copyright *********************************/ +/* + * Copyright (c) Mbedthis Software LLC, 2005-2006. All Rights Reserved. + */ +/********************************** Includes **********************************/ + +#include "ejs.h" + +#if UNUSED +/*********************************** Defines **********************************/ + +#define EJS_WEB_PROPERTY "-web" +#define EJS_HTTP_PROPERTY "-http" + +#define EJS_HTTP_DISPOSED 550 + +/* + * Control structure for one HTTP request structure + */ +typedef struct HTTPControl { + Ejs *ejs; + IWebResp *webResp; + AEECallback *callback; + MprBuf *buf; + EjsVar *thisObj; + char *url; + MprTime requestStarted; + uint timeout; +} HTTPControl; + +/****************************** Forward Declarations **************************/ + +static void cleanup(HTTPControl *hp); +static int createWeb(Ejs *ejs, EjsVar *thisObj); +static void brewCallback(HTTPControl *hp); +static int httpDestructor(Ejs *ejs, EjsVar *vp); +static void httpCallback(HTTPControl *hp, int responseCode); +static int setCallback(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv); + +/******************************************************************************/ +/* + * Constructor + */ + +int ejsHTTPConstructor(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + if (argc != 0 && argc != 2) { + ejsError(ejs, EJS_ARG_ERROR, + "Bad usage: HTTP([obj = this, method = onComplete]);"); + return -1; + } + + if (createWeb(ejs, thisObj) < 0) { + return -1; + } + + setCallback(ejs, thisObj, argc, argv); + return 0; +} + +/******************************************************************************/ + +static int createWeb(Ejs *ejs, EjsVar *thisObj) +{ + MprApp *app; + void *web; + + app = mprGetApp(ejs); + + /* + * Create one instance of IWeb for the entire application. Do it here + * so only widgets that require HTTP incurr the overhead. + */ + web = mprGetKeyValue(ejs, "bpWeb"); + if (web == 0) { + if (ISHELL_CreateInstance(app->shell, AEECLSID_WEB, &web) != SUCCESS) { + ejsError(ejs, EJS_IO_ERROR, "Can't create IWEB"); + return -1; + } + } + mprSetKeyValue(ejs, "bpWeb", web); + return 0; +} + +/******************************************************************************/ +/************************************ Methods *********************************/ +/******************************************************************************/ +/* + * function setCallback(obj, methodString); + */ + +static int setCallback(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + if (argc >= 1) { + ejsSetProperty(ejs, thisObj, "obj", argv[0]); + } else { + ejsSetProperty(ejs, thisObj, "obj", thisObj); + } + + if (argc >= 2) { + ejsSetProperty(ejs, thisObj, "method", argv[1]); + } else { + ejsSetPropertyToString(ejs, thisObj, "method", "onComplete"); + } + + return 0; +} + +/******************************************************************************/ +/* + * function fetch(); + */ + +static int fetchProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + HTTPControl *hp; + EjsProperty *pp; + MprApp *app; + IWeb *web; + + if (argc != 1 || !ejsVarIsString(argv[0])) { + ejsError(ejs, EJS_ARG_ERROR, "Bad usage: fetch(url)"); + return -1; + } + + app = mprGetApp(ejs); + web = (IWeb*) mprGetKeyValue(ejs, "bpWeb"); + + /* + * Web options + * + * WEBOPT_USERAGENT (char*) sets user agent + * WEBOPT_HANDLERDATA (void*) + * WEBOPT_CONNECTTIMEOUT (uint) msec + * WEBOPT_CONTENTLENGTH (long) + * WEBOPT_IDLECONNTIMEOUT (int) + * WEBOPT_ACTIVEXACTIONST (uint) Number of active requests + * + * WEBREQUEST_REDIRECT redirect transparently + * + */ + + hp = mprAllocType(ejs, HTTPControl); + if (hp == 0) { + ejsMemoryError(ejs); + return -1; + } + + hp->ejs = ejs; + hp->buf = mprCreateBuf(hp, MPR_BUF_INCR, MPR_MAX_BUF); + if (hp->buf == 0) { + mprFree(hp); + ejsMemoryError(ejs); + return -1; + } + + /* + * We copy thisObj because we need to preserve both the var and the object. + * We pass the var to brewCallback and so it must persist. The call to + * ejsMakeObjPermanent will stop the GC from collecting the object. + */ + hp->thisObj = ejsDupVar(ejs, thisObj, EJS_SHALLOW_COPY); + ejsSetVarName(ejs, hp->thisObj, "internalHttp"); + + /* + * Must keep a reference to the http object + */ + ejsMakeObjPermanent(hp->thisObj, 1); + + /* + * Make a property so we can access the HTTPControl structure from other + * methods. + */ + pp = ejsSetPropertyToPtr(ejs, thisObj, EJS_HTTP_PROPERTY, hp, 0); + ejsMakePropertyEnumerable(pp, 0); + ejsSetObjDestructor(ejs, hp->thisObj, httpDestructor); + + hp->url = mprStrdup(hp, argv[0]->string); + + hp->timeout = ejsGetPropertyAsInteger(ejs, thisObj, "timeout"); + mprGetTime(hp, &hp->requestStarted); + + hp->callback = mprAllocTypeZeroed(hp, AEECallback); + CALLBACK_Init(hp->callback, brewCallback, hp); + + hp->webResp = 0; + IWEB_GetResponse(web, + (web, &hp->webResp, hp->callback, hp->url, + WEBOPT_HANDLERDATA, hp, + WEBOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0)", + WEBOPT_CONNECTTIMEOUT, hp->timeout, + WEBOPT_COPYOPTS, TRUE, + WEBOPT_CONTENTLENGTH, 0, + WEBOPT_END)); + + ejsSetPropertyToString(ejs, thisObj, "status", "active"); + + return 0; +} + +/******************************************************************************/ +/* + * Called whenver the http object is deleted. + */ + +static int httpDestructor(Ejs *ejs, EjsVar *thisObj) +{ + HTTPControl *hp; + + /* + * If the httpCallback has run, then this property will not exist + */ + hp = ejsGetPropertyAsPtr(ejs, thisObj, EJS_HTTP_PROPERTY); + + if (hp) { + cleanup(hp); + } + + return 0; +} + +/******************************************************************************/ +/* + * Stop the request immediately without calling the callback + */ + +static int stopProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + HTTPControl *hp; + + hp = ejsGetPropertyAsPtr(ejs, thisObj, EJS_HTTP_PROPERTY); + + if (hp) { + cleanup(hp); + } + + return 0; +} + +/******************************************************************************/ +/* + * Brew HTTP callback. Invoked for any return data. + */ + +static void brewCallback(HTTPControl *hp) +{ + Ejs *ejs; + EjsVar *thisObj; + ISource *source; + WebRespInfo *info; + char data[MPR_BUF_INCR]; + int bytes; + + mprAssert(hp); + mprAssert(hp->webResp); + + info = IWEBRESP_GetInfo(hp->webResp); + + if (info == 0) { + mprAssert(info); + /* should not happen */ + return; + } + + ejs = hp->ejs; + thisObj = hp->thisObj; + + if (! WEB_ERROR_SUCCEEDED(info->nCode)) { + ejsSetPropertyToString(ejs, thisObj, "status", "error"); + httpCallback(hp, info->nCode); + return; + } + + if (hp->timeout) { + if (mprGetTimeRemaining(hp, hp->requestStarted, hp->timeout) <= 0) { + ejsSetPropertyToString(ejs, thisObj, "status", "timeout"); + httpCallback(hp, 504); + return; + } + } + + /* + * Normal success + */ + source = info->pisMessage; + mprAssert(source); + + bytes = ISOURCE_Read(source, data, sizeof(data)); + + switch (bytes) { + case ISOURCE_WAIT: // No data yet + ISOURCE_Readable(source, hp->callback); + break; + + case ISOURCE_ERROR: + ejsSetPropertyToString(ejs, thisObj, "status", "error"); + httpCallback(hp, info->nCode); + break; + + case ISOURCE_END: + mprAddNullToBuf(hp->buf); + ejsSetPropertyToString(ejs, thisObj, "status", "complete"); + httpCallback(hp, info->nCode); + break; + + default: + if (bytes > 0) { + if (mprPutBlockToBuf(hp->buf, data, bytes) != bytes) { + ejsSetPropertyToString(ejs, thisObj, "status", "partialData"); + httpCallback(hp, 500); + } + } + ISOURCE_Readable(source, hp->callback); + break; + } +} + +/******************************************************************************/ +/* + * Invoke the HTTP completion method + */ + +static void httpCallback(HTTPControl *hp, int responseCode) +{ + Ejs *ejs; + EjsVar *thisObj, *callbackObj; + MprArray *args; + char *msg; + const char *callbackMethod; + + mprAssert(hp); + mprAssert(hp->webResp); + + thisObj = hp->thisObj; + ejs = hp->ejs; + + ejsSetPropertyToInteger(ejs, thisObj, "responseCode", responseCode); + if (mprGetBufLength(hp->buf) > 0) { + ejsSetPropertyToBinaryString(ejs, thisObj, "responseData", + mprGetBufStart(hp->buf), mprGetBufLength(hp->buf)); + } + + callbackObj = ejsGetPropertyAsVar(ejs, thisObj, "obj"); + callbackMethod = ejsGetPropertyAsString(ejs, thisObj, "method"); + + if (callbackObj != 0 && callbackMethod != 0) { + + args = mprCreateItemArray(ejs, EJS_INC_ARGS, EJS_MAX_ARGS); + mprAddItem(args, ejsDupVar(ejs, hp->thisObj, EJS_SHALLOW_COPY)); + + if (ejsRunMethod(ejs, callbackObj, callbackMethod, args) < 0) { + msg = ejsGetErrorMsg(ejs); + mprError(ejs, MPR_LOC, "HTTP callback failed. Details: %s", msg); + } + ejsFreeMethodArgs(ejs, args); + + } else if (ejsRunMethod(ejs, thisObj, "onComplete", 0) < 0) { + msg = ejsGetErrorMsg(ejs); + mprError(ejs, MPR_LOC, "HTTP onComplete failed. Details: %s", msg); + } + + cleanup(hp); +} + +/******************************************************************************/ +/* + * Cleanup + */ + +static void cleanup(HTTPControl *hp) +{ + Ejs *ejs; + MprApp *app; + int rc; + + mprAssert(hp); + mprAssert(hp->webResp); + + ejs = hp->ejs; + + if (hp->webResp) { + rc = IWEBRESP_Release(hp->webResp); + // mprAssert(rc == 0); + hp->webResp = 0; + } + + if (hp->callback) { + CALLBACK_Cancel(hp->callback); + mprFree(hp->callback); + hp->callback = 0; + } + + /* + * Once the property is deleted, then if the destructor runs, it will + * notice that the EJS_HTTP_PROPERTY is undefined. + */ + ejsDeleteProperty(ejs, hp->thisObj, EJS_HTTP_PROPERTY); + + /* + * Allow garbage collection to work on thisObj + */ + ejsMakeObjPermanent(hp->thisObj, 0); + ejsFreeVar(ejs, hp->thisObj); + + mprFree(hp->buf); + mprFree(hp->url); + + mprFree(hp); + + app = mprGetApp(ejs); + + + ISHELL_SendEvent(app->shell, (AEECLSID) app->classId, EVT_USER, 0, 0); +} + +/******************************************************************************/ +/******************************** Initialization ******************************/ +/******************************************************************************/ + +int ejsDefineHTTPClass(Ejs *ejs) +{ + EjsVar *httpClass; + + httpClass = + ejsDefineClass(ejs, "HTTP", "Object", ejsHTTPConstructor); + if (httpClass == 0) { + return MPR_ERR_CANT_INITIALIZE; + } + + /* + * Define the methods + */ + ejsDefineCMethod(ejs, httpClass, "fetch", fetchProc, 0); + ejsDefineCMethod(ejs, httpClass, "stop", stopProc, 0); + ejsDefineCMethod(ejs, httpClass, "setCallback", setCallback, 0); + +#if FUTURE + ejsDefineCMethod(ejs, httpClass, "put", put, 0); + ejsDefineCMethod(ejs, httpClass, "upload", upload, 0); + ejsDefineCMethod(ejs, httpClass, "addUploadFile", addUploadFile, 0); + ejsDefineCMethod(ejs, httpClass, "addPostData", addPostData, 0); + ejsDefineCMethod(ejs, httpClass, "setUserPassword", setUserPassword, 0); + ejsDefineCMethod(ejs, httpClass, "addCookie", addCookie, 0); +#endif + + /* + * Define properties + */ + ejsSetPropertyToString(ejs, httpClass, "status", "inactive"); + + /* This default should come from player.xml */ + + ejsSetPropertyToInteger(ejs, httpClass, "timeout", 30 * 1000); + ejsSetPropertyToInteger(ejs, httpClass, "responseCode", 0); + + return ejsObjHasErrors(httpClass) ? MPR_ERR_CANT_INITIALIZE: 0; +} + +/******************************************************************************/ + +void ejsTermHTTPClass(Ejs *ejs) +{ + IWeb *web; + int rc; + + web = (IWeb*) mprGetKeyValue(ejs, "bpWeb"); + if (web) { + rc = IWEB_Release(web); + mprAssert(rc == 0); + } +} + +#endif +/******************************************************************************/ + +/* + * 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 + */ diff --git a/source4/lib/appweb/ejs-2.0/ejs/system/WIN/.ignore b/source4/lib/appweb/ejs-2.0/ejs/system/WIN/.ignore new file mode 100644 index 0000000000..fb5a29031e --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/system/WIN/.ignore @@ -0,0 +1 @@ +.updated diff --git a/source4/lib/appweb/ejs-2.0/ejs/system/WIN/Makefile b/source4/lib/appweb/ejs-2.0/ejs/system/WIN/Makefile new file mode 100755 index 0000000000..424747052a --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/system/WIN/Makefile @@ -0,0 +1,21 @@ +# +# Makefile to build the EJS Object Model for WIN +# +# Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved. +# + +COMPILE := *.c +EXPORT_OBJECTS := yes +MAKE_IFLAGS := -I../.. -I../../../mpr + +include make.dep + +compileExtra: .updated + +.updated: $(FILES) + @touch .updated + +## Local variables: +## tab-width: 4 +## End: +## vim: tw=78 sw=4 ts=4 diff --git a/source4/lib/appweb/ejs-2.0/ejs/system/WIN/ejsFile.c b/source4/lib/appweb/ejs-2.0/ejs/system/WIN/ejsFile.c new file mode 100644 index 0000000000..24c521891e --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/system/WIN/ejsFile.c @@ -0,0 +1,98 @@ +/* + * @file ejsFile.c + * @brief File class for the EJScript System Object Model + */ +/********************************** Copyright *********************************/ +/* + * Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved. + */ +/********************************** Includes **********************************/ + +#include "ejs.h" + +/******************************************************************************/ +/* + * Default Constructor + */ + +/******************************************************************************/ +/************************************ Methods *********************************/ +/******************************************************************************/ +/* + * function open(); + */ + +static int openProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + ejsTrace(ep, "File.open()\n"); + return 0; +} + +/******************************************************************************/ +/* + * function close(); + */ + +static int closeProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + ejsTrace(ep, "File.close()\n"); + return 0; +} + +/******************************************************************************/ +/* + * function read(); + */ + +static int readProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + ejsTrace(ep, "File.read()\n"); + return 0; +} + +/******************************************************************************/ +/* + * function write(); + */ + +static int writeProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + ejsTrace(ep, "File.write()\n"); + return 0; +} + +/******************************************************************************/ +/******************************** Initialization ******************************/ +/******************************************************************************/ + +int ejsDefineFileClass(Ejs *ep) +{ + EjsVar *fileClass; + + fileClass = ejsDefineClass(ep, "File", "Object", 0); + if (fileClass == 0) { + return MPR_ERR_CANT_INITIALIZE; + } + + /* + * Define the methods + */ + ejsDefineCMethod(ep, fileClass, "open", openProc, 0); + ejsDefineCMethod(ep, fileClass, "close", closeProc, 0); + ejsDefineCMethod(ep, fileClass, "read", readProc, 0); + ejsDefineCMethod(ep, fileClass, "write", writeProc, 0); + + return ejsObjHasErrors(fileClass) ? MPR_ERR_CANT_INITIALIZE: 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 + */ diff --git a/source4/lib/appweb/ejs-2.0/ejs/system/WIN/ejsFileSystem.c b/source4/lib/appweb/ejs-2.0/ejs/system/WIN/ejsFileSystem.c new file mode 100755 index 0000000000..66c3b84870 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/system/WIN/ejsFileSystem.c @@ -0,0 +1,456 @@ +/* + * @file ejsFileSystem.c + * @brief FileSystem class for the EJ System Object Model + */ +/********************************** Copyright *********************************/ +/* + * Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved. + */ +/********************************** Includes **********************************/ + +#include "ejs.h" + +/******************************************************************************/ +/* + * Default Constructor + */ + +/******************************************************************************/ +/************************************ Methods *********************************/ +/******************************************************************************/ +/* + * function void access(string path); + * MOB - API insufficient. Access for read or write? + */ + +static int accessProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + int rc; + + if (argc != 1 || !ejsVarIsString(argv[0])) { + ejsError(ejs, EJS_ARG_ERROR, "Bad usage: access(path)"); + return -1; + } + + rc = access(argv[0]->string, 04); + + ejsSetReturnValueToBoolean(ejs, (rc == 0) ? 1 : 0); + return 0; +} + +/******************************************************************************/ +/* + * function void mkdir(string path); + */ + +static int mkdirProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + if (argc != 1 || !ejsVarIsString(argv[0])) { + ejsError(ejs, EJS_ARG_ERROR, "Bad usage: mkdir(path)"); + return -1; + } + + if (mprMakeDirPath(ejs, argv[0]->string) < 0) { + ejsError(ejs, EJS_IO_ERROR, "Cant create directory"); + return -1; + } + return 0; +} + +/******************************************************************************/ +/* + * function void rmdir(string path); + */ + +static int rmdirProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + int rc; + + if (argc != 1 || !ejsVarIsString(argv[0])) { + ejsError(ejs, EJS_ARG_ERROR, "Bad usage: mkdir(path)"); + return -1; + } + + rc = mprDeleteDir(ejs, argv[0]->string); + + if (rc < 0) { + ejsError(ejs, EJS_IO_ERROR, "Cant remove directory"); + return -1; + } + return 0; +} + +/******************************************************************************/ +/* + * function void dirList(string path, [bool enumDirs]); + * MOB -- need pattern to match (what about "." and ".." and ".*" + */ + +static int dirListProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + WIN32_FIND_DATA findData; + HANDLE h; + char path[MPR_MAX_FNAME]; + EjsVar *array, *vp; + uchar enumDirs; + + if (argc < 1 || !ejsVarIsString(argv[0])) { + ejsError(ejs, EJS_ARG_ERROR, "Bad usage: dirList(path)"); + return -1; + } + if (argc == 2) { + enumDirs = ejsVarToBoolean(argv[1]); + } else { + enumDirs = 0; + } + array = ejsCreateArray(ejs, 0); + ejsMakeObjPermanent(array, 1); + + /* + * First collect the files + */ + mprSprintf(path, sizeof(path), "%s/*.*", argv[0]->string); + h = FindFirstFile(path, &findData); + if (h == INVALID_HANDLE_VALUE) { + ejsError(ejs, EJS_ARG_ERROR, "Can't enumerate dirList(path)"); + return -1; + } + + do { + if (findData.cFileName[0] == '.') { + continue; + } + if (!enumDirs || + (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { + mprSprintf(path, sizeof(path), "%s/%s", argv[0]->string, + findData.cFileName); + vp = ejsCreateStringVar(ejs, path); + ejsAddArrayElt(ejs, array, vp, EJS_SHALLOW_COPY); + ejsFreeVar(ejs, vp); + } + } while (FindNextFile(h, &findData) != 0); + + FindClose(h); + + ejsSetReturnValue(ejs, array); + ejsMakeObjPermanent(array, 0); + + /* + * Can free now as the return value holds the reference + */ + ejsFreeVar(ejs, array); + + return 0; +} + +/******************************************************************************/ +/* + * function void getFreeSpace(); + */ + +static int getFreeSpaceProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ +#if UNUSED + MprApp *app; + uint space; + + app = mprGetApp(ejs); + space = IFILEMGR_GetFreeSpace(app->fileMgr, 0); + ejsSetReturnValueToInteger(ejs, space); +#endif + + return 0; +} + +/******************************************************************************/ +/* + * function void writeFile(string path, var data); + */ + +static int writeFileProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + MprFile *file; + char *data, *buf; + int bytes, length, rc; + + if (argc != 2 || !ejsVarIsString(argv[0])) { + ejsError(ejs, EJS_ARG_ERROR, "Bad usage: writeFile(path, var)"); + return -1; + } + + if (ejsVarIsString(argv[1])) { + data = argv[1]->string; + length = argv[1]->length; + buf = 0; + } else { + buf = data = ejsVarToString(ejs, argv[1]); + length = strlen(data); + } + + /* + * Create fails if already present + */ + rc = mprDelete(ejs, argv[0]->string); + file = mprOpen(ejs, argv[0]->string, O_CREAT | O_WRONLY | O_BINARY, 0664); + if (file == 0) { + ejsError(ejs, EJS_IO_ERROR, "Cant create %s", argv[0]->string); + mprFree(buf); + return -1; + } + + rc = 0; + bytes = mprWrite(file, data, length); + if (bytes != length) { + ejsError(ejs, EJS_IO_ERROR, "Write error to %s", argv[1]->string); + rc = -1; + } + + mprClose(file); + + mprFree(buf); + return rc; +} + +/******************************************************************************/ +/* + * function string readFile(string path); + */ + +static int readFileProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + MprApp *app; + MprFile *file; + MprBuf *buf; + char *data; + int bytes, rc; + + if (argc != 1 || !ejsVarIsString(argv[0])) { + ejsError(ejs, EJS_ARG_ERROR, "Bad usage: readFile(path)"); + return -1; + } + buf = mprCreateBuf(ejs, MPR_BUF_INCR, MPR_MAX_BUF); + if (buf == 0) { + ejsMemoryError(ejs); + return -1; + } + + data = mprAlloc(ejs, MPR_BUFSIZE); + if (buf == 0) { + mprFree(buf); + ejsMemoryError(ejs); + return -1; + } + + app = mprGetApp(ejs); + file = mprOpen(ejs, argv[0]->string, O_RDONLY, 0664); + if (file == 0) { + ejsError(ejs, EJS_IO_ERROR, "Cant open %s", argv[0]->string); + mprFree(buf); + return -1; + } + + rc = 0; + while ((bytes = mprRead(file, data, MPR_BUFSIZE)) > 0) { + if (mprPutBlockToBuf(buf, data, bytes) != bytes) { + ejsError(ejs, EJS_IO_ERROR, "Write error to %s", argv[1]->string); + rc = -1; + break; + } + } + + ejsSetReturnValueToBinaryString(ejs, mprGetBufStart(buf), + mprGetBufLength(buf)); + + mprClose(file); + mprFree(data); + mprFree(buf); + + return rc; +} + +/******************************************************************************/ +/* + * function void remove(string path); + */ + +static int removeProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + int rc; + + if (argc != 1 || !ejsVarIsString(argv[0])) { + ejsError(ejs, EJS_ARG_ERROR, "Bad usage: remove(path)"); + return -1; + } + + rc = unlink(argv[0]->string); + if (rc < 0) { + ejsError(ejs, EJS_IO_ERROR, "Cant remove file"); + return -1; + } + return 0; +} + +/******************************************************************************/ +/* + * function void rename(string from, string to); + */ + +static int renameProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + int rc; + + if (argc != 2 || !ejsVarIsString(argv[0]) || !ejsVarIsString(argv[1])) { + ejsError(ejs, EJS_ARG_ERROR, "Bad usage: rename(old, new)"); + return -1; + } + + unlink(argv[1]->string); + rc = rename(argv[0]->string, argv[1]->string); + if (rc < 0) { + ejsError(ejs, EJS_IO_ERROR, "Cant rename file"); + return -1; + } + return 0; +} + +/******************************************************************************/ +/* + * function void copy(string old, string new); + */ + +static int copyProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + MprFile *from, *to; + char *buf; + int bytes, rc; + + if (argc != 2 || !ejsVarIsString(argv[0]) || !ejsVarIsString(argv[1])) { + ejsError(ejs, EJS_ARG_ERROR, "Bad usage: copy(old, new)"); + return -1; + } + + buf = mprAlloc(ejs, MPR_BUFSIZE); + if (buf == 0) { + ejsMemoryError(ejs); + return -1; + } + + from = mprOpen(ejs, argv[0]->string, O_RDONLY | O_BINARY, 0664); + if (from == 0) { + ejsError(ejs, EJS_IO_ERROR, "Cant open %s", argv[0]->string); + mprFree(buf); + return -1; + } + + to = mprOpen(ejs, argv[1]->string, O_CREAT | O_BINARY, 0664); + if (to == 0) { + ejsError(ejs, EJS_IO_ERROR, "Cant create %s", argv[1]->string); + mprClose(from); + mprFree(buf); + return -1; + } + + rc = 0; + while ((bytes = mprRead(from, buf, MPR_BUFSIZE)) > 0) { + if (mprWrite(to, buf, bytes) != bytes) { + ejsError(ejs, EJS_IO_ERROR, "Write error to %s", argv[1]->string); + rc = -1; + break; + } + } + + mprClose(from); + mprClose(to); + mprFree(buf); + + return rc; +} + +/******************************************************************************/ +/* + * function FileInfo getFileInfo(string path); + * + * MOB -- should create a real class FileInfo + */ + +static int getFileInfoProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + MprFileInfo info; + EjsVar *fileInfo; + int rc; + + if (argc != 1 || !ejsVarIsString(argv[0])) { + ejsError(ejs, EJS_ARG_ERROR, "Bad usage: getFileInfo(path)"); + return -1; + } + + fileInfo = ejsCreateObjVar(ejs); + if (fileInfo == 0) { + ejsMemoryError(ejs); + return -1; + } + ejsMakeObjPermanent(fileInfo, 1); + + rc = mprGetFileInfo(ejs, argv[0]->string, &info); + if (rc < 0) { + ejsMakeObjPermanent(fileInfo, 0); + ejsFreeVar(ejs, fileInfo); + ejsError(ejs, EJS_IO_ERROR, "Cant get file info for %s", + argv[0]->string); + return -1; + } + + ejsSetPropertyToInteger(ejs, fileInfo, "created", info.ctime); + ejsSetPropertyToInteger(ejs, fileInfo, "length", info.size); + ejsSetPropertyToBoolean(ejs, fileInfo, "isDir", info.isDir); + + ejsSetReturnValue(ejs, fileInfo); + ejsMakeObjPermanent(fileInfo, 0); + + return 0; +} + +/******************************************************************************/ +/******************************** Initialization ******************************/ +/******************************************************************************/ + +int ejsDefineFileSystemClass(Ejs *ejs) +{ + EjsVar *fileSystemClass; + + fileSystemClass = ejsDefineClass(ejs, "FileSystem", "Object", 0); + if (fileSystemClass == 0) { + return MPR_ERR_CANT_INITIALIZE; + } + + /* + * Define the methods + */ + ejsDefineCMethod(ejs, fileSystemClass, "access", accessProc, 0); + ejsDefineCMethod(ejs, fileSystemClass, "mkdir", mkdirProc, 0); + ejsDefineCMethod(ejs, fileSystemClass, "rmdir", rmdirProc, 0); + ejsDefineCMethod(ejs, fileSystemClass, "dirList", dirListProc, 0); + ejsDefineCMethod(ejs, fileSystemClass, "writeFile", writeFileProc, 0); + ejsDefineCMethod(ejs, fileSystemClass, "readFile", readFileProc, 0); + ejsDefineCMethod(ejs, fileSystemClass, "remove", removeProc, 0); + ejsDefineCMethod(ejs, fileSystemClass, "rename", renameProc, 0); + ejsDefineCMethod(ejs, fileSystemClass, "copy", copyProc, 0); + ejsDefineCMethod(ejs, fileSystemClass, "getFileInfo", getFileInfoProc, 0); + + // MOB -- should be a property with accessor + ejsDefineCMethod(ejs, fileSystemClass, "getFreeSpace", getFreeSpaceProc, 0); + + return ejsObjHasErrors(fileSystemClass) ? MPR_ERR_CANT_INITIALIZE: 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 + */ diff --git a/source4/lib/appweb/ejs-2.0/ejs/system/WIN/ejsHTTP.c b/source4/lib/appweb/ejs-2.0/ejs/system/WIN/ejsHTTP.c new file mode 100755 index 0000000000..25821f6960 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/system/WIN/ejsHTTP.c @@ -0,0 +1,488 @@ +/* + * @file ejsHTTP.c + * @brief HTTP class for the EJ System Object Model + */ +/********************************** Copyright *********************************/ +/* + * Copyright (c) Mbedthis Software LLC, 2005-2006. All Rights Reserved. + */ +/********************************** Includes **********************************/ + +#include "ejs.h" + +#if UNUSED +/*********************************** Defines **********************************/ + +#define EJS_WEB_PROPERTY "-web" +#define EJS_HTTP_PROPERTY "-http" + +#define EJS_HTTP_DISPOSED 550 + +/* + * Control structure for one HTTP request structure + */ +typedef struct HTTPControl { + Ejs *ejs; + IWebResp *webResp; + AEECallback *callback; + MprBuf *buf; + EjsVar *thisObj; + char *url; + MprTime requestStarted; + uint timeout; +} HTTPControl; + +/****************************** Forward Declarations **************************/ + +static void cleanup(HTTPControl *hp); +static int createWeb(Ejs *ejs, EjsVar *thisObj); +static void brewCallback(HTTPControl *hp); +static int httpDestructor(Ejs *ejs, EjsVar *vp); +static void httpCallback(HTTPControl *hp, int responseCode); +static int setCallback(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv); + +/******************************************************************************/ +/* + * Constructor + */ + +int ejsHTTPConstructor(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + if (argc != 0 && argc != 2) { + ejsError(ejs, EJS_ARG_ERROR, + "Bad usage: HTTP([obj = this, method = onComplete]);"); + return -1; + } + + if (createWeb(ejs, thisObj) < 0) { + return -1; + } + + setCallback(ejs, thisObj, argc, argv); + return 0; +} + +/******************************************************************************/ + +static int createWeb(Ejs *ejs, EjsVar *thisObj) +{ + MprApp *app; + void *web; + + app = mprGetApp(ejs); + + /* + * Create one instance of IWeb for the entire application. Do it here + * so only widgets that require HTTP incurr the overhead. + */ + web = mprGetKeyValue(ejs, "bpWeb"); + if (web == 0) { + if (ISHELL_CreateInstance(app->shell, AEECLSID_WEB, &web) != SUCCESS) { + ejsError(ejs, EJS_IO_ERROR, "Can't create IWEB"); + return -1; + } + } + mprSetKeyValue(ejs, "bpWeb", web); + return 0; +} + +/******************************************************************************/ +/************************************ Methods *********************************/ +/******************************************************************************/ +/* + * function setCallback(obj, methodString); + */ + +static int setCallback(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + if (argc >= 1) { + ejsSetProperty(ejs, thisObj, "obj", argv[0]); + } else { + ejsSetProperty(ejs, thisObj, "obj", thisObj); + } + + if (argc >= 2) { + ejsSetProperty(ejs, thisObj, "method", argv[1]); + } else { + ejsSetPropertyToString(ejs, thisObj, "method", "onComplete"); + } + + return 0; +} + +/******************************************************************************/ +/* + * function fetch(); + */ + +static int fetchProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + HTTPControl *hp; + EjsProperty *pp; + MprApp *app; + IWeb *web; + + if (argc != 1 || !ejsVarIsString(argv[0])) { + ejsError(ejs, EJS_ARG_ERROR, "Bad usage: fetch(url)"); + return -1; + } + + app = mprGetApp(ejs); + web = (IWeb*) mprGetKeyValue(ejs, "bpWeb"); + + /* + * Web options + * + * WEBOPT_USERAGENT (char*) sets user agent + * WEBOPT_HANDLERDATA (void*) + * WEBOPT_CONNECTTIMEOUT (uint) msec + * WEBOPT_CONTENTLENGTH (long) + * WEBOPT_IDLECONNTIMEOUT (int) + * WEBOPT_ACTIVEXACTIONST (uint) Number of active requests + * + * WEBREQUEST_REDIRECT redirect transparently + * + */ + + hp = mprAllocType(ejs, HTTPControl); + if (hp == 0) { + ejsMemoryError(ejs); + return -1; + } + + hp->ejs = ejs; + hp->buf = mprCreateBuf(hp, MPR_BUF_INCR, MPR_MAX_BUF); + if (hp->buf == 0) { + mprFree(hp); + ejsMemoryError(ejs); + return -1; + } + + /* + * We copy thisObj because we need to preserve both the var and the object. + * We pass the var to brewCallback and so it must persist. The call to + * ejsMakeObjPermanent will stop the GC from collecting the object. + */ + hp->thisObj = ejsDupVar(ejs, thisObj, EJS_SHALLOW_COPY); + ejsSetVarName(ejs, hp->thisObj, "internalHttp"); + + /* + * Must keep a reference to the http object + */ + ejsMakeObjPermanent(hp->thisObj, 1); + + /* + * Make a property so we can access the HTTPControl structure from other + * methods. + */ + pp = ejsSetPropertyToPtr(ejs, thisObj, EJS_HTTP_PROPERTY, hp, 0); + ejsMakePropertyEnumerable(pp, 0); + ejsSetObjDestructor(ejs, hp->thisObj, httpDestructor); + + hp->url = mprStrdup(hp, argv[0]->string); + + hp->timeout = ejsGetPropertyAsInteger(ejs, thisObj, "timeout"); + mprGetTime(hp, &hp->requestStarted); + + hp->callback = mprAllocTypeZeroed(hp, AEECallback); + CALLBACK_Init(hp->callback, brewCallback, hp); + + hp->webResp = 0; + IWEB_GetResponse(web, + (web, &hp->webResp, hp->callback, hp->url, + WEBOPT_HANDLERDATA, hp, + WEBOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0)", + WEBOPT_CONNECTTIMEOUT, hp->timeout, + WEBOPT_COPYOPTS, TRUE, + WEBOPT_CONTENTLENGTH, 0, + WEBOPT_END)); + + ejsSetPropertyToString(ejs, thisObj, "status", "active"); + + return 0; +} + +/******************************************************************************/ +/* + * Called whenver the http object is deleted. + */ + +static int httpDestructor(Ejs *ejs, EjsVar *thisObj) +{ + HTTPControl *hp; + + /* + * If the httpCallback has run, then this property will not exist + */ + hp = ejsGetPropertyAsPtr(ejs, thisObj, EJS_HTTP_PROPERTY); + + if (hp) { + cleanup(hp); + } + + return 0; +} + +/******************************************************************************/ +/* + * Stop the request immediately without calling the callback + */ + +static int stopProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + HTTPControl *hp; + + hp = ejsGetPropertyAsPtr(ejs, thisObj, EJS_HTTP_PROPERTY); + + if (hp) { + cleanup(hp); + } + + return 0; +} + +/******************************************************************************/ +/* + * Brew HTTP callback. Invoked for any return data. + */ + +static void brewCallback(HTTPControl *hp) +{ + Ejs *ejs; + EjsVar *thisObj; + ISource *source; + WebRespInfo *info; + char data[MPR_BUF_INCR]; + int bytes; + + mprAssert(hp); + mprAssert(hp->webResp); + + info = IWEBRESP_GetInfo(hp->webResp); + + if (info == 0) { + mprAssert(info); + /* should not happen */ + return; + } + + ejs = hp->ejs; + thisObj = hp->thisObj; + + if (! WEB_ERROR_SUCCEEDED(info->nCode)) { + ejsSetPropertyToString(ejs, thisObj, "status", "error"); + httpCallback(hp, info->nCode); + return; + } + + if (hp->timeout) { + if (mprGetTimeRemaining(hp, hp->requestStarted, hp->timeout) <= 0) { + ejsSetPropertyToString(ejs, thisObj, "status", "timeout"); + httpCallback(hp, 504); + return; + } + } + + /* + * Normal success + */ + source = info->pisMessage; + mprAssert(source); + + bytes = ISOURCE_Read(source, data, sizeof(data)); + + switch (bytes) { + case ISOURCE_WAIT: // No data yet + ISOURCE_Readable(source, hp->callback); + break; + + case ISOURCE_ERROR: + ejsSetPropertyToString(ejs, thisObj, "status", "error"); + httpCallback(hp, info->nCode); + break; + + case ISOURCE_END: + mprAddNullToBuf(hp->buf); + ejsSetPropertyToString(ejs, thisObj, "status", "complete"); + httpCallback(hp, info->nCode); + break; + + default: + if (bytes > 0) { + if (mprPutBlockToBuf(hp->buf, data, bytes) != bytes) { + ejsSetPropertyToString(ejs, thisObj, "status", "partialData"); + httpCallback(hp, 500); + } + } + ISOURCE_Readable(source, hp->callback); + break; + } +} + +/******************************************************************************/ +/* + * Invoke the HTTP completion method + */ + +static void httpCallback(HTTPControl *hp, int responseCode) +{ + Ejs *ejs; + EjsVar *thisObj, *callbackObj; + MprArray *args; + char *msg; + const char *callbackMethod; + + mprAssert(hp); + mprAssert(hp->webResp); + + thisObj = hp->thisObj; + ejs = hp->ejs; + + ejsSetPropertyToInteger(ejs, thisObj, "responseCode", responseCode); + if (mprGetBufLength(hp->buf) > 0) { + ejsSetPropertyToBinaryString(ejs, thisObj, "responseData", + mprGetBufStart(hp->buf), mprGetBufLength(hp->buf)); + } + + callbackObj = ejsGetPropertyAsVar(ejs, thisObj, "obj"); + callbackMethod = ejsGetPropertyAsString(ejs, thisObj, "method"); + + if (callbackObj != 0 && callbackMethod != 0) { + + args = mprCreateItemArray(ejs, EJS_INC_ARGS, EJS_MAX_ARGS); + mprAddItem(args, ejsDupVar(ejs, hp->thisObj, EJS_SHALLOW_COPY)); + + if (ejsRunMethod(ejs, callbackObj, callbackMethod, args) < 0) { + msg = ejsGetErrorMsg(ejs); + mprError(ejs, MPR_LOC, "HTTP callback failed. Details: %s", msg); + } + ejsFreeMethodArgs(ejs, args); + + } else if (ejsRunMethod(ejs, thisObj, "onComplete", 0) < 0) { + msg = ejsGetErrorMsg(ejs); + mprError(ejs, MPR_LOC, "HTTP onComplete failed. Details: %s", msg); + } + + cleanup(hp); +} + +/******************************************************************************/ +/* + * Cleanup + */ + +static void cleanup(HTTPControl *hp) +{ + Ejs *ejs; + MprApp *app; + int rc; + + mprAssert(hp); + mprAssert(hp->webResp); + + ejs = hp->ejs; + + if (hp->webResp) { + rc = IWEBRESP_Release(hp->webResp); + // mprAssert(rc == 0); + hp->webResp = 0; + } + + if (hp->callback) { + CALLBACK_Cancel(hp->callback); + mprFree(hp->callback); + hp->callback = 0; + } + + /* + * Once the property is deleted, then if the destructor runs, it will + * notice that the EJS_HTTP_PROPERTY is undefined. + */ + ejsDeleteProperty(ejs, hp->thisObj, EJS_HTTP_PROPERTY); + + /* + * Allow garbage collection to work on thisObj + */ + ejsMakeObjPermanent(hp->thisObj, 0); + ejsFreeVar(ejs, hp->thisObj); + + mprFree(hp->buf); + mprFree(hp->url); + + mprFree(hp); + + app = mprGetApp(ejs); + + + ISHELL_SendEvent(app->shell, (AEECLSID) app->classId, EVT_USER, 0, 0); +} + +/******************************************************************************/ +/******************************** Initialization ******************************/ +/******************************************************************************/ + +int ejsDefineHTTPClass(Ejs *ejs) +{ + EjsVar *httpClass; + + httpClass = + ejsDefineClass(ejs, "HTTP", "Object", ejsHTTPConstructor); + if (httpClass == 0) { + return MPR_ERR_CANT_INITIALIZE; + } + + /* + * Define the methods + */ + ejsDefineCMethod(ejs, httpClass, "fetch", fetchProc, 0); + ejsDefineCMethod(ejs, httpClass, "stop", stopProc, 0); + ejsDefineCMethod(ejs, httpClass, "setCallback", setCallback, 0); + +#if FUTURE + ejsDefineCMethod(ejs, httpClass, "put", put, 0); + ejsDefineCMethod(ejs, httpClass, "upload", upload, 0); + ejsDefineCMethod(ejs, httpClass, "addUploadFile", addUploadFile, 0); + ejsDefineCMethod(ejs, httpClass, "addPostData", addPostData, 0); + ejsDefineCMethod(ejs, httpClass, "setUserPassword", setUserPassword, 0); + ejsDefineCMethod(ejs, httpClass, "addCookie", addCookie, 0); +#endif + + /* + * Define properties + */ + ejsSetPropertyToString(ejs, httpClass, "status", "inactive"); + + /* This default should come from player.xml */ + + ejsSetPropertyToInteger(ejs, httpClass, "timeout", 30 * 1000); + ejsSetPropertyToInteger(ejs, httpClass, "responseCode", 0); + + return ejsObjHasErrors(httpClass) ? MPR_ERR_CANT_INITIALIZE: 0; +} + +/******************************************************************************/ + +void ejsTermHTTPClass(Ejs *ejs) +{ + IWeb *web; + int rc; + + web = (IWeb*) mprGetKeyValue(ejs, "bpWeb"); + if (web) { + rc = IWEB_Release(web); + mprAssert(rc == 0); + } +} + +#endif +/******************************************************************************/ + +/* + * 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 + */ diff --git a/source4/lib/appweb/ejs-2.0/ejs/system/ejsGC.c b/source4/lib/appweb/ejs-2.0/ejs/system/ejsGC.c new file mode 100644 index 0000000000..411975f80e --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/system/ejsGC.c @@ -0,0 +1,326 @@ +/* + * @file ejsGC.c + * @brief Garbage collector class for the EJS Object Model + */ +/********************************** Copyright *********************************/ +/* + * Copyright (c) Mbedthis Software LLC, 2005-2006. All Rights Reserved. + */ +/********************************** Includes **********************************/ + +#include "ejs.h" + +/******************************************************************************/ +/************************************ Methods *********************************/ +/******************************************************************************/ +#if (WIN || BREW_SIMULATOR) && BLD_DEBUG + +static int checkProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + _CrtCheckMemory(); + return 0; +} + +#endif +/******************************************************************************/ + +static int debugProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + if (argc != 1) { + ejsError(ep, EJS_ARG_ERROR, "Bad args: debug(debugLevel)"); + return -1; + } + + ejsSetGCDebugLevel(ep, ejsVarToInteger(argv[0])); + return 0; +} + +/******************************************************************************/ +/* + * Print stats and dump objects + */ + +static int printStatsProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + bool leakStats; + + if (argc > 1) { + leakStats = ejsVarToInteger(argv[0]); + } else { + leakStats = 0; + } + +#if BLD_FEATURE_ALLOC_STATS + ejsPrintAllocReport(ep, 0); + + mprPrintAllocReport(mprGetApp(ep), leakStats, 0); +#endif + +#if BLD_DEBUG + ejsDumpObjects(ep); +#endif + + return 0; +} + +/******************************************************************************/ + +static int runProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + if (argc > 1) { + ejsError(ep, EJS_ARG_ERROR, "Bad args: run([quick])"); + return -1; + } + + if (argc == 1) { + ejsIncrementalCollectGarbage(ep); + } else { + ejsCollectGarbage(ep, -1); + } + return 0; +} + +/******************************************************************************/ + +static int usedMemoryProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + ejsSetReturnValueToInteger(ep, ejsGetUsedMemory(ep)); + return 0; +} + +/******************************************************************************/ + +static int allocatedMemoryProc(Ejs *ep, EjsVar *thisObj, int argc, + EjsVar **argv) +{ +#if BLD_FEATURE_ALLOC_STATS + ejsSetReturnValueToInteger(ep, ejsGetAllocatedMemory(ep)); +#endif + return 0; +} + +/******************************************************************************/ + +static int mprMemoryProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ +#if BLD_FEATURE_ALLOC_STATS + ejsSetReturnValueToInteger(ep, mprGetAllocatedMemory(ep)); +#endif + return 0; +} + +/******************************************************************************/ + +static int peakMprMemoryProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ +#if BLD_FEATURE_ALLOC_STATS + ejsSetReturnValueToInteger(ep, mprGetPeakAllocatedMemory(ep)); +#endif + return 0; +} + +/******************************************************************************/ + +static int getDebugLevel(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + ejsSetReturnValueToInteger(ep, ep->gc.debugLevel); + return 0; +} + +/******************************************************************************/ + +static int setDebugLevel(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + if (argc != 1) { + ejsArgError(ep, "Bad arguments"); + return -1; + } + ep->gc.debugLevel= ejsVarToInteger(argv[0]); + return 0; +} + +/******************************************************************************/ + +static int getEnable(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + ejsSetReturnValueToBoolean(ep, ep->gc.enable); + return 0; +} + +/******************************************************************************/ + +static int setEnable(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + if (argc != 1) { + ejsArgError(ep, "Bad arguments"); + return -1; + } + ep->gc.enable= ejsVarToBoolean(argv[0]); + return 0; +} + +/******************************************************************************/ + +static int getDemandCollect(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + ejsSetReturnValueToBoolean(ep, ep->gc.enableDemandCollect); + return 0; +} + +/******************************************************************************/ + +static int setDemandCollect(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + if (argc != 1) { + ejsArgError(ep, "Bad arguments"); + return -1; + } + ep->gc.enableDemandCollect = ejsVarToBoolean(argv[0]); + return 0; +} + +/******************************************************************************/ + +static int getIdleCollect(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + ejsSetReturnValueToBoolean(ep, ep->gc.enableIdleCollect); + return 0; +} + +/******************************************************************************/ + +static int setIdleCollect(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + if (argc != 1) { + ejsArgError(ep, "Bad arguments"); + return -1; + } + ep->gc.enableIdleCollect = ejsVarToBoolean(argv[0]); + return 0; +} + +/******************************************************************************/ + +static int getWorkQuota(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + ejsSetReturnValueToInteger(ep, ep->gc.workQuota); + return 0; +} + +/******************************************************************************/ + +static int setWorkQuota(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + int quota; + + if (argc != 1) { + ejsArgError(ep, "Bad arguments"); + return -1; + } + quota = ejsVarToInteger(argv[0]); + if (quota < EJS_GC_MIN_WORK_QUOTA && quota != 0) { + ejsArgError(ep, "Bad work quota"); + return -1; + } + + ep->gc.workQuota = quota; + return 0; +} + +/******************************************************************************/ + +static int getMaxMemory(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + ejsSetReturnValueToInteger(ep, ep->gc.maxMemory); + return 0; +} + +/******************************************************************************/ + +static int setMaxMemory(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + int maxMemory; + + if (argc != 1) { + ejsArgError(ep, "Bad arguments"); + return -1; + } + maxMemory = ejsVarToInteger(argv[0]); + if (maxMemory < 0) { + ejsArgError(ep, "Bad maxMemory"); + return -1; + } + + ep->gc.maxMemory = maxMemory; + return 0; +} + +/******************************************************************************/ +/******************************** Initialization ******************************/ +/******************************************************************************/ + +int ejsDefineGCClass(Ejs *ep) +{ + EjsVar *gcClass; + int flags; + + flags = EJS_NO_LOCAL; + + /* + * NOTE: We create the GC class and define static methods on it. There + * is no object instance + */ + gcClass = ejsDefineClass(ep, "System.GC", "Object", 0); + if (gcClass == 0) { + return MPR_ERR_CANT_INITIALIZE; + } + + /* + * MOB -- convert these to properties with accessors when available + */ + ejsDefineCMethod(ep, gcClass, "printStats", printStatsProc, flags); + ejsDefineCMethod(ep, gcClass, "run", runProc, flags); + + ejsDefineCMethod(ep, gcClass, "getUsedMemory", usedMemoryProc, flags); + ejsDefineCMethod(ep, gcClass, "getAllocatedMemory", allocatedMemoryProc, + flags); + ejsDefineCMethod(ep, gcClass, "getMprMemory", mprMemoryProc, flags); + ejsDefineCMethod(ep, gcClass, "getPeakMprMemory", peakMprMemoryProc, flags); + ejsDefineCMethod(ep, gcClass, "debug", debugProc, flags); + +#if (WIN || BREW_SIMULATOR) && BLD_DEBUG + ejsDefineCMethod(ep, gcClass, "check", checkProc, flags); +#endif + + ejsDefineCAccessors(ep, gcClass, "debugLevel", + getDebugLevel, setDebugLevel, flags); + + ejsDefineCAccessors(ep, gcClass, "enable", + getEnable, setEnable, flags); + + ejsDefineCAccessors(ep, gcClass, "demandCollect", + getDemandCollect, setDemandCollect, flags); + + ejsDefineCAccessors(ep, gcClass, "idleCollect", + getIdleCollect, setIdleCollect, flags); + + ejsDefineCAccessors(ep, gcClass, "workQuota", + getWorkQuota, setWorkQuota, flags); + + ejsDefineCAccessors(ep, gcClass, "maxMemory", + getMaxMemory, setMaxMemory, flags); + + return ejsObjHasErrors(gcClass) ? MPR_ERR_CANT_INITIALIZE : 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 + */ diff --git a/source4/lib/appweb/ejs-2.0/ejs/system/ejsGlobal.c b/source4/lib/appweb/ejs-2.0/ejs/system/ejsGlobal.c new file mode 100755 index 0000000000..6ab8f867e1 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/system/ejsGlobal.c @@ -0,0 +1,785 @@ +/* + * @file ejsGlobal.c + * @brief EJS support methods + */ +/********************************* 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 + +/******************************************************************************/ +/************************************* Code ***********************************/ +/******************************************************************************/ +/* + * assert(condition) + */ + +static int assertProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + int b; + + if (argc < 1) { + ejsError(ep, EJS_ARG_ERROR, "usage: assert(condition)"); + return -1; + } + b = ejsVarToBoolean(argv[0]); + if (b == 0) { + ejsError(ep, EJS_ASSERT_ERROR, "Assertion failure at line %d", + ejsGetLineNumber(ep)); + return -1; + } + ejsWriteVarAsBoolean(ep, ep->result, b); + return 0; +} + +/******************************************************************************/ +/* + * breakpoint(msg) + */ + +static int breakpointProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + char *buf; + + if (argc < 1) { + return 0; + } + buf = ejsVarToString(ep, argv[0]); + if (buf) { + mprBreakpoint(0, buf); + } + return 0; +} + +/******************************************************************************/ +/* + * basename(path) + */ + +static int basenameProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + char *path; + + if (argc != 1) { + ejsError(ep, EJS_ARG_ERROR, "usage: basename(path)"); + return -1; + } + + path = ejsVarToString(ep, argv[0]); + if (path == 0) { + return MPR_ERR_MEMORY; + } + ejsSetReturnValueToString(ep, mprGetBaseName(path)); + + return 0; +} + +/******************************************************************************/ +/* + * stripext(path) + */ + +static int stripextProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + char *cp, *path, *stripPath; + + if (argc != 1) { + ejsError(ep, EJS_ARG_ERROR, "usage: stripext(path)"); + return -1; + } + + path = ejsVarToString(ep, argv[0]); + if (path == 0) { + return MPR_ERR_MEMORY; + } + stripPath = mprStrdup(ep, path); + + if ((cp = strrchr(stripPath, '.')) != 0) { + *cp = '\0'; + } + + ejsSetReturnValueToString(ep, stripPath); + + mprFree(stripPath); + + return 0; +} + +/******************************************************************************/ +/* + * dirname(path) + */ + +static int dirnameProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + char *path; + char dirname[MPR_MAX_FNAME]; + + if (argc != 1) { + ejsError(ep, EJS_ARG_ERROR, "usage: dirname(path)"); + return -1; + } + + path = ejsVarToString(ep, argv[0]); + if (path == 0) { + return MPR_ERR_MEMORY; + } + + ejsSetReturnValueToString(ep, + mprGetDirName(dirname, sizeof(dirname), path)); + + return 0; +} + +/******************************************************************************/ +/* + * trim(string) -- trim white space + */ + +static int trimProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + char *str, *buf, *cp; + + if (argc != 1) { + ejsError(ep, EJS_ARG_ERROR, "usage: trim(string)"); + return -1; + } + + str = ejsVarToString(ep, argv[0]); + if (str == 0) { + return MPR_ERR_MEMORY; + } + str = buf = mprStrdup(ep, str); + + while (isspace(*str)) { + str++; + } + cp = &str[strlen(str) - 1]; + while (cp >= str) { + if (isspace(*cp)) { + *cp = '\0'; + } else { + break; + } + cp--; + } + + ejsSetReturnValueToString(ep, str); + + mprFree(buf); + + return 0; +} + +/******************************************************************************/ +/* + * Terminate the script + */ + +static int exitScript(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + int status; + + if (argc != 1) { + ejsError(ep, EJS_ARG_ERROR, "usage: exit(status)"); + return -1; + } + status = (int) ejsVarToInteger(argv[0]); + ejsExit(ep, status); + + ejsWriteVarAsString(ep, ep->result, ""); + return 0; +} + +/******************************************************************************/ +/* + * include javascript libraries. + */ + +static int includeProc(Ejs *ep, EjsVar *thisObj, int argc, char **argv) +{ + int i; + + mprAssert(argv); + + for (i = 0; i < argc; i++) { + if (ejsEvalFile(ep, argv[i], 0) < 0) { + return -1; + } + } + return 0; +} + +/******************************************************************************/ +/* + * include javascript libraries at the global level + */ + +static int includeGlobalProc(Ejs *ep, EjsVar *thisObj, int argc, char **argv) +{ + int fid, i; + + mprAssert(argv); + + /* + * Create a new block and set the context to be the global scope + */ + fid = ejsSetBlock(ep, ep->global); + + for (i = 0; i < argc; i++) { + if (ejsEvalFile(ep, argv[i], 0) < 0) { + ejsCloseBlock(ep, fid); + return -1; + } + } + ejsCloseBlock(ep, fid); + return 0; +} + +/******************************************************************************/ +#if BLD_DEBUG +/* + * Print variables to stdout + */ + +static int printvProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + EjsVar *vp; + char *buf; + int i; + + for (i = 0; i < argc; ) { + vp = argv[i++]; + + /* mprPrintf(ep, "arg[%d] = ", i); */ + + buf = ejsVarToString(ep, vp); + + if (vp->propertyName == 0 || *vp->propertyName == '\0') { + mprPrintf(ep, "%s: ", buf); + + } else if (i < argc) { + mprPrintf(ep, "%s = %s, ", vp->propertyName, buf); + } else { + mprPrintf(ep, "%s = %s\n", vp->propertyName, buf); + } + } + return 0; +} + +#endif +/******************************************************************************/ +/* + * Print the args to stdout + */ + +static int printProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + char *buf; + int i; + + for (i = 0; i < argc; i++) { + buf = ejsVarToString(ep, argv[i]); + mprPrintf(ep, "%s", buf); + } + return 0; +} + +/******************************************************************************/ +/* + * println + */ + +static int printlnProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + printProc(ep, thisObj, argc, argv); + mprPrintf(ep, "\n"); + return 0; +} + +/******************************************************************************/ +#if FUTURE +/* + * sprintf + */ + +static int sprintfProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + va_list ap; + char *buf; + void **args; + int result; + + if (argc <= 1) { + ejsError(ep, EJS_ARG_ERROR, "Usage: sprintf(fmt, [args ...])"); + return -1; + } + + args = mprAlloc(ep, sizeof(void*) * (argc - 1)); + if (args == 0) { + mprAssert(args); + return -1; + } + + for (i = 1; i < argc; i++) { + args[i - 1] = argv[i]); + } + + va_start(ap, fmt); + *buf = 0; + result = inner(0, &buf, MPR_MAX_STRING, fmt, args); + va_end(ap); + + ejsSetReturnValueToString(ep, buf); + + mprFree(buf); + return 0; +} + +/******************************************************************************/ + +inner(const char *fmt, void **args) +{ + va_list ap; + + va_start(ap, fmt); + *buf = 0; + mprSprintfCore(ctx, &buf, maxSize, fmt, ap, MPR_PRINTF_ARGV); + va_end(ap); +} + +#endif +/******************************************************************************/ +/* + * sleep + */ + +static int sleepProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + if (argc != 1) { + ejsError(ep, EJS_ARG_ERROR, "Usage: sleep(milliseconds)"); + return -1; + } + mprSleep(ep, ejsVarToInteger(argv[0])); + return 0; +} + +/******************************************************************************/ +/* + * sort properties + * FUTURE -- should have option to sort object based on a given property value + * ascending or descending + * Usage: sort(object, order = ascending, property = 0); + */ + +static int sortProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + const char *property; + int error, order; + + error = 0; + property = 0; + + /* + * Default order is increasing + */ + order = 1; + + if (argc < 1 || argc > 3 || !ejsVarIsObject(argv[0])) { + error++; + } + + if (argc >= 2) { + order = ejsVarToInteger(argv[1]); + } + + /* + * If property is not defined, it sorts the properties in the object + */ + if (argc == 3) { + if (! ejsVarIsString(argv[2])) { + error++; + } else { + property = argv[2]->string; + } + } + + if (error) { + ejsError(ep, EJS_ARG_ERROR, "Usage: sort(object, [order], [property])"); + return -1; + } + ejsSortProperties(ep, argv[0], 0, property, order); + return 0; +} + +/******************************************************************************/ +/* + * Get a time mark + * MOB -- WARNING: this can overflow. OK on BREW, but other O/Ss it may have + * overflowed on the first call. It should be renamed. + * MOB -- replace with proper Date. + */ + +static int timeProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + MprTime now; + + mprGetTime(ep, &now); +#if WIN || LINUX || SOLARIS +{ + /* MOB -- poor hack */ + static MprTime initial; + if (initial.sec == 0) { + initial = now; + } + now.sec -= initial.sec; + + if (initial.msec > now.msec) { + now.msec = now.msec + 1000 - initial.msec; + now.sec--; + } else { + now.msec -= initial.msec; + } +} +#endif + /* MOB -- this can overflow */ + ejsSetReturnValueToInteger(ep, now.sec * 1000 + now.msec); + return 0; +} + +/******************************************************************************/ +/* + * MOB -- Temporary Get the date (time since Jan 6, 1980 GMT + */ + +static int dateProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ +#if BREW + uint now; + + now = GETTIMESECONDS(); + ejsSetReturnValueToInteger(ep, now); +#endif + return 0; +} + +/******************************************************************************/ +/* + * strlen(string) + */ + +static int strlenProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + char *buf; + int len; + + if (argc != 1) { + ejsError(ep, EJS_ARG_ERROR, "Usage: strlen(var)"); + return -1; + } + + len = 0; + if (! ejsVarIsString(argv[0])) { + buf = ejsVarToString(ep, argv[0]); + if (buf) { + len = strlen(buf); + } + + } else { + len = argv[0]->length; + } + + ejsSetReturnValueToInteger(ep, len); + return 0; +} + +/******************************************************************************/ +/* + * toint(num) + */ + +static int tointProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + int i; + + if (argc != 1) { + ejsError(ep, EJS_ARG_ERROR, "Usage: toint(number)"); + return -1; + } + + i = ejsVarToInteger(argv[0]); + + ejsSetReturnValueToInteger(ep, i); + return 0; +} + +/******************************************************************************/ +/* + * string strstr(string, pat) + */ + +static int strstrProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + char *str, *pat; + char *s; + int strAlloc; + + if (argc != 2) { + ejsError(ep, EJS_ARG_ERROR, "Usage: strstr(string, pat)"); + return -1; + } + + str = ejsVarToString(ep, argv[0]); + + strAlloc = ep->castAlloc; + ep->castTemp = 0; + + pat = ejsVarToString(ep, argv[1]); + + s = strstr(str, pat); + + if (s == 0) { + ejsSetReturnValueToUndefined(ep); + } else { + ejsSetReturnValueToString(ep, s); + } + + if (strAlloc) { + mprFree(str); + } + + return 0; +} + +/******************************************************************************/ +/* + * Trace + */ + +static int traceProc(Ejs *ep, EjsVar *thisObj, int argc, char **argv) +{ + if (argc == 1) { + mprLog(ep, 0, "%s", argv[0]); + + } else if (argc == 2) { + mprLog(ep, atoi(argv[0]), "%s", argv[1]); + + } else { + ejsError(ep, EJS_ARG_ERROR, "Usage: trace([level], message)"); + return -1; + } + ejsWriteVarAsString(ep, ep->result, ""); + return 0; +} + +/******************************************************************************/ +/* + * Evaluate a sub-script. It is evaluated in the same variable scope as + * the calling script / method. + */ + +static int evalScriptProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + EjsVar *arg; + int i; + + ejsWriteVarAsUndefined(ep, ep->result); + + for (i = 0; i < argc; i++) { + arg = argv[i]; + if (arg->type != EJS_TYPE_STRING) { + continue; + } + if (ejsEvalScript(ep, arg->string, 0) < 0) { + return -1; + } + } + /* + * Return with the value of the last expression + */ + return 0; +} + +/******************************************************************************/ + +/* MOB -- need a real datatype returning int, int64, etc */ + +static int typeofProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + const struct { + EjsType type; + const char *name; + } types[] = { + { EJS_TYPE_UNDEFINED, "undefined" }, +#if EJS_ECMA_STND + { EJS_TYPE_NULL, "object" }, +#else + { EJS_TYPE_NULL, "null" }, +#endif + { EJS_TYPE_BOOL, "boolean" }, + { EJS_TYPE_CMETHOD, "function" }, + { EJS_TYPE_FLOAT, "number" }, + { EJS_TYPE_INT, "number" }, + { EJS_TYPE_INT64, "number" }, + { EJS_TYPE_OBJECT, "object" }, + { EJS_TYPE_METHOD, "function" }, + { EJS_TYPE_STRING, "string" }, + { EJS_TYPE_STRING_CMETHOD, "function" }, + { EJS_TYPE_PTR, "pointer" } + }; + const char *type; + int i; + + type = NULL; + if (argc != 1) { + ejsError(ep, EJS_ARG_ERROR, "Bad args: typeof(var)"); + return -1; + } + + for (i = 0; i < MPR_ARRAY_SIZE(types); i++) { + if (argv[0]->type == types[i].type) { + type = types[i].name; + break; + } + } + if (type == NULL) { + mprAssert(type); + return -1; + } + + ejsSetReturnValueToString(ep, type); + return 0; +} + +/******************************************************************************/ +/* + * Define the standard properties and methods inherited by all interpreters + * Obj is set to the global class in the default interpreter. When an + * interpreter attempts to write to any property, a copy will be written + * into the interpeters own global space. This is like a "copy-on-write". + */ + +int ejsDefineGlobalProperties(Ejs *ep) +{ + EjsVar *obj; + + obj = ep->service->globalClass; + mprAssert(obj); + + ejsSetPropertyToNull(ep, obj, "null"); + ejsSetPropertyToUndefined(ep, obj, "undefined"); + ejsSetPropertyToBoolean(ep, obj, "true", 1); + ejsSetPropertyToBoolean(ep, obj, "false", 0); + +#if BLD_FEATURE_FLOATING_POINT + { + /* MOB. Fix. This generates warnings on some systems. + This is intended. */ + double d = 0.0; + double e = 0.0; + ejsSetPropertyToFloat(ep, obj, "NaN", e / d); + + d = MAX_FLOAT; + ejsSetPropertyToFloat(ep, obj, "Infinity", d * d); + } +#endif + +#if BLD_FEATURE_LEGACY_API + /* + * DEPRECATED: 2.0. + * So that ESP/ASP can ignore "language=javascript" statements + */ + ejsSetPropertyToInteger(ep, obj, "javascript", 0); +#endif + + /* + * Extension methods. We go directly to the mpr property APIs for speed. + * Flags will cause the callbacks to be supplied the Ejs handle. + */ + ejsDefineCMethod(ep, obj, "assert", assertProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, obj, "breakpoint", breakpointProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, obj, "basename", basenameProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, obj, "dirname", dirnameProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, obj, "stripext", stripextProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, obj, "trim", trimProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, obj, "eval", evalScriptProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, obj, "exit", exitScript, EJS_NO_LOCAL); + ejsDefineCMethod(ep, obj, "print", printProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, obj, "println", printlnProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, obj, "sleep", sleepProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, obj, "sort", sortProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, obj, "time", timeProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, obj, "date", dateProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, obj, "strlen", strlenProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, obj, "strstr", strstrProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, obj, "typeof", typeofProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, obj, "toint", tointProc, EJS_NO_LOCAL); + + ejsDefineStringCMethod(ep, obj, "include", includeProc, EJS_NO_LOCAL); + ejsDefineStringCMethod(ep, obj, "includeGlobal", includeGlobalProc, + EJS_NO_LOCAL); + ejsDefineStringCMethod(ep, obj, "trace", traceProc, EJS_NO_LOCAL); + +#if BLD_DEBUG + ejsDefineCMethod(ep, obj, "printv", printvProc, EJS_NO_LOCAL); +#endif + +#if FUTURE + ejsDefineCMethod(ep, obj, "printf", printfProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, obj, "sprintf", sprintfProc, EJS_NO_LOCAL); +#endif + + if (ejsObjHasErrors(obj)) { + return MPR_ERR_CANT_INITIALIZE; + } + return 0; +} + +/******************************************************************************/ + +#else +void ejsProcsDummy() {} + +/******************************************************************************/ +#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 + */ diff --git a/source4/lib/appweb/ejs-2.0/ejs/system/ejsSystem.c b/source4/lib/appweb/ejs-2.0/ejs/system/ejsSystem.c new file mode 100644 index 0000000000..e035e1c740 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/system/ejsSystem.c @@ -0,0 +1,112 @@ +/* + * @file ejsSystem.c + * @brief System class for the EJS Object Model + */ +/********************************** Copyright *********************************/ +/* + * Copyright (c) Mbedthis Software LLC, 2005-2006. All Rights Reserved. + */ +/********************************** Includes **********************************/ + +#include "ejs.h" + +/******************************************************************************/ +/************************************ Methods *********************************/ +/******************************************************************************/ +#if UNUSED +/* + * function int random() + */ + +static int randomProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + ejsTrace(ep, "random()\n"); + return 0; +} + +/******************************************************************************/ +/* + * function void yield() + */ + +static int yieldProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + ejsTrace(ep, "yield()\n"); + return 0; +} + +/******************************************************************************/ +/* + * function void sleep(int milliSeconds) + */ + +static int sleepProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + ejsTrace(ep, "sleep()\n"); + return 0; +} + +#endif +/******************************************************************************/ +/* + * function void exit(int status) + * + * Exit the widget with the given status. All JavaScript processing ceases. + */ + +static int exitProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + int status; + + status = 0; + if ((argc == 1) && ejsVarIsInteger(argv[0])) { + status = argv[0]->integer; + } + ejsExit(ep, status); + return 0; +} + +/******************************************************************************/ +/******************************** Initialization ******************************/ +/******************************************************************************/ + +int ejsDefineSystemClass(Ejs *ep) +{ + EjsVar *systemClass; + + /* + * We create the system class and define static methods on it. + * NOTE: There is no object instance + */ + systemClass = ejsDefineClass(ep, "System", "Object", 0); + if (systemClass == 0) { + return MPR_ERR_CANT_INITIALIZE; + } + + ejsDefineCMethod(ep, systemClass, "exit", exitProc, EJS_NO_LOCAL); + +#if UNUSED + ejsDefineCMethod(ep, systemClass, "random", randomProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, systemClass, "yield", yieldProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, systemClass, "sleep", sleepProc, EJS_NO_LOCAL); + + /* + * Define properties + */ + ejsSetPropertyToString(systemClass, "name", ""); +#endif + + return ejsObjHasErrors(systemClass) ? MPR_ERR_CANT_INITIALIZE : 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 + */ diff --git a/source4/lib/appweb/ejs-2.0/ejs/system/ejsSystemApp.c b/source4/lib/appweb/ejs-2.0/ejs/system/ejsSystemApp.c new file mode 100644 index 0000000000..e2f1ceb363 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/system/ejsSystemApp.c @@ -0,0 +1,49 @@ +/* + * @file ejsSystemApp.c + * @brief App class + */ +/********************************** Copyright *********************************/ +/* + * Copyright (c) Mbedthis Software Inc, 2005-2006. All Rights Reserved. + */ +/********************************** Includes **********************************/ + +#include "ejs.h" + +/************************************ Code ************************************/ + +int ejsDefineAppClass(Ejs *ep) +{ + EjsVar *appClass; + + appClass = ejsDefineClass(ep, "System.App", "Object", 0); + if (appClass == 0) { + return MPR_ERR_CANT_INITIALIZE; + } + + /* + * Define properties + */ + ejsSetPropertyToString(ep, appClass, "name", BLD_PRODUCT); + ejsSetPropertyToString(ep, appClass, "title", BLD_NAME); + ejsSetPropertyToString(ep, appClass, "version", BLD_VERSION); + + /* + * Command line arguments + */ + ejsSetPropertyToNull(ep, appClass, "args"); + + return ejsObjHasErrors(appClass) ? MPR_ERR_CANT_INITIALIZE : 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 + */ diff --git a/source4/lib/appweb/ejs-2.0/ejs/system/ejsSystemDebug.c b/source4/lib/appweb/ejs-2.0/ejs/system/ejsSystemDebug.c new file mode 100644 index 0000000000..5a011e2a2d --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/system/ejsSystemDebug.c @@ -0,0 +1,60 @@ +/* + * @file ejsSystemDebug.c + * @brief System.Debug class + */ +/********************************** Copyright *********************************/ +/* + * Copyright (c) Mbedthis Software LLC, 2005-2006. All Rights Reserved. + */ +/********************************** Includes **********************************/ + +#include "ejs.h" + +/******************************************************************************/ +/************************************ Methods *********************************/ +/******************************************************************************/ +/* + * function bool isDebugMode() + * MOB -- convert to accessor + */ + +static int isDebugMode(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + ejsTrace(ejs, "isDebugMode()\n"); + ejsSetReturnValueToInteger(ejs, mprGetDebugMode(ejs)); + return 0; +} + +/******************************************************************************/ +/******************************** Initialization ******************************/ +/******************************************************************************/ + +int ejsDefineDebugClass(Ejs *ejs) +{ + EjsVar *systemDebugClass; + + systemDebugClass = ejsDefineClass(ejs, "System.Debug", "Object", 0); + if (systemDebugClass == 0) { + return MPR_ERR_CANT_INITIALIZE; + } + + /* + * Define the class methods + */ + ejsDefineCMethod(ejs, systemDebugClass, "isDebugMode", isDebugMode, + EJS_NO_LOCAL); + + return ejsObjHasErrors(systemDebugClass) ? MPR_ERR_CANT_INITIALIZE : 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 + */ diff --git a/source4/lib/appweb/ejs-2.0/ejs/system/ejsSystemLog.c b/source4/lib/appweb/ejs-2.0/ejs/system/ejsSystemLog.c new file mode 100644 index 0000000000..66467f8fcf --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/system/ejsSystemLog.c @@ -0,0 +1,163 @@ +/* + * @file ejsSystemLog.c + * @brief System.Log class for the EJS Object Model + */ +/********************************** Copyright *********************************/ +/* + * Copyright (c) Mbedthis Software LLC, 2005-2006. All Rights Reserved. + */ +/********************************** Includes **********************************/ + +#include "ejs.h" + +/*********************************** Usage ************************************/ +/* + * System.Log.setLog(path); + * System.Log.enable; + */ +/******************************************************************************/ + +static void logHandler(MPR_LOC_DEC(ctx, loc), int flags, int level, + const char *msg) +{ + MprApp *app; + char *buf; + int len; + + app = mprGetApp(ctx); + if (app->logFile == 0) { + return; + } + + if (flags & MPR_LOG_SRC) { + len = mprAllocSprintf(MPR_LOC_PASS(ctx, loc), &buf, 0, + "Log %d: %s\n", level, msg); + + } else if (flags & MPR_ERROR_SRC) { + len = mprAllocSprintf(MPR_LOC_PASS(ctx, loc), &buf, 0, + "Error: %s\n", msg); + + } else if (flags & MPR_FATAL_SRC) { + len = mprAllocSprintf(MPR_LOC_PASS(ctx, loc), &buf, 0, + "Fatal: %s\n", msg); + + } else if (flags & MPR_ASSERT_SRC) { +#if BLD_FEATURE_ALLOC_LEAK_TRACK + len = mprAllocSprintf(MPR_LOC_PASS(ctx, loc), &buf, 0, + "Assertion %s, failed at %s\n", + msg, loc); +#else + len = mprAllocSprintf(MPR_LOC_PASS(ctx, loc), &buf, 0, + "Assertion %s, failed\n", msg); +#endif + + } else if (flags & MPR_RAW) { + /* OPT */ + len = mprAllocSprintf(MPR_LOC_PASS(ctx, loc), &buf, 0, + "%s", msg); + + } else { + return; + } + + mprPuts(app->logFile, buf, len); + + mprFree(buf); +} + +/******************************************************************************/ +/************************************ Methods *********************************/ +/******************************************************************************/ +/* + * function int setLog(string path) + */ + +static int setLog(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + const char *path; + MprFile *file; + MprApp *app; + + if (argc != 1 || !ejsVarIsString(argv[0])) { + ejsArgError(ejs, "Usage: setLog(path)"); + return -1; + } + + app = mprGetApp(ejs); + + /* + * Ignore errors if we can't create the log file. + * Use the app context so this will live longer than the interpreter + * MOB -- this leaks files. + */ + path = argv[0]->string; + file = mprOpen(app, path, O_CREAT | O_TRUNC | O_WRONLY, 0664); + if (file) { + app->logFile = file; + mprSetLogHandler(ejs, logHandler); + } + mprLog(ejs, 0, "Test log"); + + return 0; +} + +/******************************************************************************/ +#if UNUSED + +static int enableSetAccessor(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + if (argc != 1) { + ejsArgError(ejs, "Usage: set(value)"); + return -1; + } + ejsSetProperty(ejs, thisObj, "_enabled", argv[0]); + return 0; +} + +/******************************************************************************/ + +static int enableGetAccessor(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + ejsSetReturnValue(ejs, ejsGetPropertyAsVar(ejs, thisObj, "_enabled")); + return 0; +} + +#endif +/******************************************************************************/ +/******************************** Initialization ******************************/ +/******************************************************************************/ + +int ejsDefineLogClass(Ejs *ejs) +{ + EjsVar *logClass; + + logClass = ejsDefineClass(ejs, "System.Log", "Object", 0); + if (logClass == 0) { + return MPR_ERR_CANT_INITIALIZE; + } + + ejsDefineCMethod(ejs, logClass, "setLog", setLog, EJS_NO_LOCAL); + +#if UNUSED + EjsProperty *pp; + ejsDefineCAccessors(ejs, logClass, "enable", enableSetAccessor, + enableGetAccessor, EJS_NO_LOCAL); + + pp = ejsSetPropertyToBoolean(ejs, logClass, "_enabled", 0); + ejsMakePropertyEnumerable(pp, 0); +#endif + + return ejsObjHasErrors(logClass) ? MPR_ERR_CANT_INITIALIZE : 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 + */ diff --git a/source4/lib/appweb/ejs-2.0/ejs/system/ejsSystemMemory.c b/source4/lib/appweb/ejs-2.0/ejs/system/ejsSystemMemory.c new file mode 100755 index 0000000000..d10787b1b4 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/system/ejsSystemMemory.c @@ -0,0 +1,174 @@ +/* + * @file ejsSystemMemory.c + * @brief System.Memory class + */ +/********************************** Copyright *********************************/ +/* + * Copyright (c) Mbedthis Software LLC, 2005-2006. All Rights Reserved. + */ +/********************************** Includes **********************************/ + +#include "ejs.h" + +/****************************** Forward Declarations***************************/ + +static uint getUsedMemory(Ejs *ejs); + +/******************************************************************************/ +/*********************************** Methods *********************************/ +/******************************************************************************/ + +static int getUsedMemoryProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + ejsSetReturnValueToInteger(ejs, getUsedMemory(ejs)); + return 0; +} + +/******************************************************************************/ + +static int getUsedStackProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + ejsSetReturnValueToInteger(ejs, mprStackSize(ejs)); + return 0; +} + +/******************************************************************************/ +/* + * Public function + */ + +uint ejsGetAvailableMemory(Ejs *ejs) +{ + EjsVar *memoryClass; + uint ram; + + memoryClass = ejsGetClass(ejs, 0, "System.Memory"); + + ram = ejsGetPropertyAsInteger(ejs, memoryClass, "ram"); + return ram - getUsedMemory(ejs); +} + +/******************************************************************************/ + +static int getAvailableMemoryProc(Ejs *ejs, EjsVar *thisObj, int argc, + EjsVar **argv) +{ + EjsVar *memoryClass; + uint ram; + + memoryClass = ejsGetClass(ejs, 0, "System.Memory"); + + ram = ejsGetPropertyAsInteger(ejs, memoryClass, "ram"); +#if BREW + ejsSetReturnValueToInteger(ejs, ram - getUsedMemory(ejs)); +#else + ejsSetReturnValueToInteger(ejs, 0); +#endif + return 0; +} + +/******************************************************************************/ + +static uint getUsedMemory(Ejs *ejs) +{ +#if BREW + MprApp *app; + IHeap *heap; + uint memInUse; + void *ptr; + + app = mprGetApp(ejs); + ptr = (void*) &heap; + if (ISHELL_CreateInstance(app->shell, AEECLSID_HEAP, (void**) ptr) + == SUCCESS) { + memInUse = IHEAP_GetMemStats(heap); + IHEAP_Release(heap); + } else { + memInUse = 0; + } + + return memInUse; +#else + return 0; +#endif +} + +/******************************************************************************/ +/******************************** Initialization ******************************/ +/******************************************************************************/ + +int ejsDefineMemoryClass(Ejs *ejs) +{ + EjsVar *memoryClass; + uint used; + +#if BREW + MprApp *app; + AEEDeviceInfo *info; + + /* + * Get needed information for class properties. + */ + info = mprAllocType(ejs, AEEDeviceInfo); + if (info == 0) { + return MPR_ERR_CANT_ALLOCATE; + } + info->wStructSize = sizeof(AEEDeviceInfo); + app = mprGetApp(ejs); + ISHELL_GetDeviceInfo(app->shell, info); + used = getUsedMemory(ejs); +#else + used = 0; +#endif + + /* + * Create the class + */ + memoryClass = ejsDefineClass(ejs, "System.Memory", "Object", 0); + if (memoryClass == 0) { + return MPR_ERR_CANT_INITIALIZE; + } + + /* + * Define the class methods + * MOB -- change to accessors + */ + ejsDefineCMethod(ejs, memoryClass, "getUsedStack", getUsedStackProc, + EJS_NO_LOCAL); + ejsDefineCMethod(ejs, memoryClass, "getUsedMemory", getUsedMemoryProc, + EJS_NO_LOCAL); + ejsDefineCMethod(ejs, memoryClass, "getAvailableMemory", + getAvailableMemoryProc, EJS_NO_LOCAL); + + /* + * Define properties + */ +#if BREW + ejsSetPropertyToInteger(ejs, memoryClass, "ram", info->dwRAM); + +#if UNUSED + /* MOB -- delete this */ + ejsSetPropertyToInteger(ejs, memoryClass, "available", + info->dwRAM - used); +#endif +#endif + +#if UNUSED + ejsSetPropertyToInteger(ejs, memoryClass, "used", used); + ejsSetPropertyToInteger(ejs, memoryClass, "flash", 0); +#endif + + return ejsObjHasErrors(memoryClass) ? MPR_ERR_CANT_INITIALIZE : 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 + */ |