mirror of
https://github.com/samba-team/samba.git
synced 2025-12-17 04:23:50 +03:00
1379 lines
30 KiB
C
1379 lines
30 KiB
C
/*
|
|
* @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
|
|
*/
|