r18925: Add current snapshot of the ejs-2.0 code. Tridge, will you be incorporating...
[bbaumbach/samba-autobuild/.git] / source4 / lib / appweb / ejs-2.0 / ejs / ejs.c
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 (file)
index 0000000..0fcc6f0
--- /dev/null
@@ -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
+ */