summaryrefslogtreecommitdiff
path: root/source4/lib/appweb/ejs
diff options
context:
space:
mode:
Diffstat (limited to 'source4/lib/appweb/ejs')
-rw-r--r--source4/lib/appweb/ejs/config.h141
-rw-r--r--source4/lib/appweb/ejs/ejs.h136
-rw-r--r--source4/lib/appweb/ejs/ejsInternal.h295
-rw-r--r--source4/lib/appweb/ejs/ejsLex.c923
-rw-r--r--source4/lib/appweb/ejs/ejsLib.c1090
-rw-r--r--source4/lib/appweb/ejs/ejsParser.c2436
-rw-r--r--source4/lib/appweb/ejs/ejsProcs.c704
7 files changed, 5725 insertions, 0 deletions
diff --git a/source4/lib/appweb/ejs/config.h b/source4/lib/appweb/ejs/config.h
new file mode 100644
index 0000000000..8c06d28147
--- /dev/null
+++ b/source4/lib/appweb/ejs/config.h
@@ -0,0 +1,141 @@
+#define BLD_PRODUCT "Samba4"
+#define BLD_NAME "Samba4 WEB Applications"
+#define BLD_VERSION "4"
+#define BLD_NUMBER "1"
+#define BLD_TYPE "DEBUG"
+#define BLD_DEFAULTS "normal"
+#define BLD_PACKAGES ""
+#define BLD_APPWEB_CONFIG "normal.conf"
+#define BLD_APPWEB 0
+#define BLD_COMPANY "Mbedthis"
+#define BLD_DEBUG 0
+#define BLD_DIRS "bootstrap include obj bin mpr ejs esp http doc appWeb appWebSamples images"
+#define BLD_HTTP_PORT 7777
+#define BLD_LIB_VERSION "1.0.0"
+#define BLD_SSL_PORT 4443
+#define BLD_CLEAN_INSTALL "0"
+#define BLD_LICENSE "gpl"
+#define BLD_HOST_SYSTEM "i686-pc-linux-gnu"
+#define BLD_BUILD_SYSTEM "i686-pc-linux-gnu"
+#define BLD_HOST_OS "LINUX"
+#define BLD_HOST_CPU_ARCH MPR_CPU_IX86
+#define BLD_HOST_CPU "i686"
+#define BLD_HOST_UNIX 1
+#define BLD_BUILD_OS "LINUX"
+#define BLD_BUILD_CPU_ARCH MPR_CPU_IX86
+#define BLD_BUILD_CPU i686
+#define BLD_BUILD_UNIX 1
+#define BLD_ROOT_PREFIX "/"
+#define BLD_FEATURE_ACCESS_LOG 0
+#define BLD_FEATURE_ADMIN_MODULE 0
+#define BLD_FEATURE_ASPNET_MODULE 0
+#define BLD_FEATURE_ASSERT 1
+#define BLD_FEATURE_AUTH_MODULE 0
+#define BLD_FEATURE_C_API_MODULE 1
+#define BLD_FEATURE_C_API_CLIENT 0
+#define BLD_FEATURE_CGI_MODULE 0
+#define BLD_FEATURE_COMPAT_MODULE 0
+#define BLD_FEATURE_CONFIG_PARSE 0
+#define BLD_FEATURE_CONFIG_SAVE 0
+#define BLD_FEATURE_COOKIE 0
+#define BLD_FEATURE_COPY_MODULE 0
+#define BLD_FEATURE_DIGEST 0
+#define BLD_FEATURE_DLL 0
+#define BLD_FEATURE_EGI_MODULE 0
+#define BLD_FEATURE_EJS 1
+#define BLD_FEATURE_ESP_MODULE 1
+#define BLD_FEATURE_EVAL_PERIOD 30
+#define BLD_FEATURE_FLOATING_POINT 1
+#define BLD_FEATURE_IF_MODIFIED 0
+#define BLD_FEATURE_INT64 1
+#define BLD_FEATURE_KEEP_ALIVE 0
+#define BLD_FEATURE_LEGACY_API 0
+#define BLD_FEATURE_LIB_STDCPP 0
+#define BLD_FEATURE_LICENSE 0
+#define BLD_FEATURE_LOG 0
+#define BLD_FEATURE_MULTITHREAD 0
+#define BLD_FEATURE_MALLOC 0
+#define BLD_FEATURE_MALLOC_STATS 0
+#define BLD_FEATURE_MALLOC_LEAK 0
+#define BLD_FEATURE_MALLOC_HOOK 0
+#define BLD_FEATURE_NUM_TYPE int64_t
+#define BLD_FEATURE_NUM_TYPE_ID MPR_TYPE_INT64
+#define BLD_FEATURE_ROMFS 0
+#define BLD_FEATURE_RUN_AS_SERVICE 0
+#define BLD_FEATURE_SAFE_STRINGS 0
+#define BLD_FEATURE_SAMPLES 0
+#define BLD_FEATURE_SESSION 1
+#define BLD_FEATURE_SHARED 0
+#define BLD_FEATURE_SQUEEZE 0
+#define BLD_FEATURE_SSL_MODULE 0
+#define BLD_FEATURE_STATIC 1
+#define BLD_FEATURE_STATIC_LINK_LIBC 0
+#define BLD_FEATURE_TEST 0
+#define BLD_FEATURE_UPLOAD_MODULE 0
+#define BLD_FEATURE_XDB_MODULE 0
+#define BLD_FEATURE_ADMIN_MODULE_BUILTIN 0
+#define BLD_FEATURE_ASPNET_MODULE_BUILTIN 0
+#define BLD_FEATURE_AUTH_MODULE_BUILTIN 0
+#define BLD_FEATURE_C_API_MODULE_BUILTIN 0
+#define BLD_FEATURE_CGI_MODULE_BUILTIN 0
+#define BLD_FEATURE_COMPAT_MODULE_BUILTIN 0
+#define BLD_FEATURE_COPY_MODULE_BUILTIN 0
+#define BLD_FEATURE_EGI_MODULE_BUILTIN 0
+#define BLD_FEATURE_ESP_MODULE_BUILTIN 0
+#define BLD_FEATURE_SSL_MODULE_BUILTIN 0
+#define BLD_FEATURE_UPLOAD_MODULE_BUILTIN 0
+#define BLD_FEATURE_XDB_MODULE_BUILTIN 0
+#define BLD_FEATURE_ADMIN_MODULE_LOADABLE 0
+#define BLD_FEATURE_ASPNET_MODULE_LOADABLE 0
+#define BLD_FEATURE_AUTH_MODULE_LOADABLE 0
+#define BLD_FEATURE_C_API_MODULE_LOADABLE 0
+#define BLD_FEATURE_CGI_MODULE_LOADABLE 0
+#define BLD_FEATURE_COMPAT_MODULE_LOADABLE 0
+#define BLD_FEATURE_COPY_MODULE_LOADABLE 0
+#define BLD_FEATURE_EGI_MODULE_LOADABLE 0
+#define BLD_FEATURE_ESP_MODULE_LOADABLE 0
+#define BLD_FEATURE_SSL_MODULE_LOADABLE 0
+#define BLD_FEATURE_UPLOAD_MODULE_LOADABLE 0
+#define BLD_FEATURE_XDB_MODULE_LOADABLE 0
+#define BLD_AR_FOR_BUILD "ar"
+#define BLD_CC_FOR_BUILD "cc"
+#define BLD_CSC_FOR_BUILD ""
+#define BLD_JAVAC_FOR_BUILD ""
+#define BLD_LD_FOR_BUILD "ld"
+#define BLD_RANLIB_FOR_BUILD ""
+#define BLD_NM_FOR_BUILD "nm"
+#define BLD_CFLAGS_FOR_BUILD ""
+#define BLD_IFLAGS_FOR_BUILD ""
+#define BLD_LDFLAGS_FOR_BUILD ""
+#define BLD_ARCHIVE_FOR_BUILD ".a"
+#define BLD_EXE_FOR_BUILD ""
+#define BLD_OBJ_FOR_BUILD ".o"
+#define BLD_PIOBJ_FOR_BUILD ".lo"
+#define BLD_CLASS_FOR_BUILD ".class"
+#define BLD_SHLIB_FOR_BUILD ""
+#define BLD_SHOBJ_FOR_BUILD ".so"
+#define BLD_AR_FOR_HOST "ar"
+#define BLD_CC_FOR_HOST "cc"
+#define BLD_CSC_FOR_HOST "csc"
+#define BLD_JAVAC_FOR_HOST "javac"
+#define BLD_LD_FOR_HOST "ld"
+#define BLD_RANLIB_FOR_HOST "true"
+#define BLD_NM_FOR_HOST "nm"
+#define BLD_CFLAGS_FOR_HOST ""
+#define BLD_IFLAGS_FOR_HOST ""
+#define BLD_LDFLAGS_FOR_HOST ""
+#define BLD_ARCHIVE_FOR_HOST ".a"
+#define BLD_EXE_FOR_HOST ""
+#define BLD_OBJ_FOR_HOST ".o"
+#define BLD_PIOBJ_FOR_HOST ".lo"
+#define BLD_CLASS_FOR_HOST ".class"
+#define BLD_SHLIB_FOR_HOST ""
+#define BLD_SHOBJ_FOR_HOST ".so"
+#define BLD_TOOLS_DIR "${BLD_TOP}/bin"
+#define BLD_BIN_DIR "${BLD_TOP}/bin"
+#define BLD_INC_DIR "/usr/include/${BLD_PRODUCT}"
+#define BLD_EXP_OBJ_DIR "${BLD_TOP}/obj"
+
+#ifndef MAX_FLOAT
+#define MAX_FLOAT 3.40282347e+38F
+#endif
diff --git a/source4/lib/appweb/ejs/ejs.h b/source4/lib/appweb/ejs/ejs.h
new file mode 100644
index 0000000000..c7b0c54d8e
--- /dev/null
+++ b/source4/lib/appweb/ejs/ejs.h
@@ -0,0 +1,136 @@
+/*
+ * @file ejs.h
+ * @brief Primary Embedded Javascript (ECMAScript) header.
+ * @overview This Embedded Javascript (EJS) header defines the
+ * public API. This API should only be used by those directly
+ * using EJS without using Embedded Server Pages (ESP). ESP
+ * wraps all relevant APIs to expose a single consistent API.
+ * \n\n
+ * This API requires the mpr/var.h facilities to create and
+ * manage objects and properties.
+ */
+/********************************* Copyright **********************************/
+/*
+ * @copy default.g
+ *
+ * Copyright (c) Mbedthis Software LLC, 2003-2005. 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 "lib/appweb/mpr/miniMpr.h"
+#include "lib/appweb/mpr/var.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/********************************* Prototypes *********************************/
+
+typedef MprVarHandle EjsId;
+typedef MprVarHandle EjsHandle;
+
+/*
+ * Multithreaded lock routines
+ */
+typedef void (*EjsLock)(void *lockData);
+typedef void (*EjsUnlock)(void *lockData);
+
+/********************************* Prototypes *********************************/
+/*
+ * Module management
+ */
+extern int ejsOpen(EjsLock lock, EjsUnlock unlock, void *lockData);
+extern void ejsClose(void);
+extern EjsId ejsOpenEngine(EjsHandle primaryHandle, EjsHandle altHandle);
+extern void ejsCloseEngine(EjsId eid);
+
+void *ejs_save_state(void);
+void ejs_restore_state(void *ptr);
+
+/*
+ * Evaluation functions
+ */
+extern int ejsEvalFile(EjsId eid, char *path, MprVar *result, char **emsg);
+extern int ejsEvalScript(EjsId eid, char *script, MprVar *result,
+ char **emsg);
+extern int ejsRunFunction(int eid, MprVar *obj, const char *functionName,
+ MprArray *args);
+
+/*
+ * Composite variable get / set routines. Can also use the MPR property
+ * routines on an object variable.
+ */
+extern MprVar ejsCreateObj(const char *name, int hashSize);
+extern MprVar ejsCreateArray(const char *name, int hashSize);
+extern bool ejsDestroyVar(MprVar *obj);
+extern int ejsCopyVar(EjsId eid, const char *var, MprVar *value,
+ bool copyRef);
+extern int ejsReadVar(EjsId eid, const char *var, MprVar *value);
+extern int ejsWriteVar(EjsId eid, const char *var, MprVar *value);
+extern int ejsWriteVarValue(EjsId eid, const char *var, MprVar value);
+extern int ejsDeleteVar(EjsId eid, const char *var);
+
+extern MprVar *ejsGetLocalObject(EjsId eid);
+extern MprVar *ejsGetGlobalObject(EjsId eid);
+
+/*
+ * Function routines
+ */
+extern void ejsDefineFunction(EjsId eid, const char *functionName,
+ char *args, char *body);
+extern void ejsDefineCFunction(EjsId eid, const char *functionName,
+ MprCFunction fn, void *thisPtr, int flags);
+extern void ejsDefineStringCFunction(EjsId eid, const char *functionName,
+ MprStringCFunction fn, void *thisPtr, int flags);
+extern void *ejsGetThisPtr(EjsId eid);
+extern MprVar *ejsGetReturnValue(EjsId eid);
+extern int ejsGetLineNumber(EjsId eid);
+extern int ejsParseArgs(int argc, char **argv, char *fmt, ...);
+extern void ejsSetErrorMsg(EjsId eid, const char* fmt, ...)
+ PRINTF_ATTRIBUTE(2,3);
+extern void ejsSetReturnValue(EjsId eid, MprVar value);
+extern void ejsSetReturnString(EjsId eid, const char *str);
+
+#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/ejsInternal.h b/source4/lib/appweb/ejs/ejsInternal.h
new file mode 100644
index 0000000000..8b66dafdca
--- /dev/null
+++ b/source4/lib/appweb/ejs/ejsInternal.h
@@ -0,0 +1,295 @@
+/*
+ * @file ejsInternal.h
+ * @brief Private header for Embedded Javascript (ECMAScript)
+ * @overview This Embedded Javascript header defines the private Embedded
+ * Javascript internal structures.
+ */
+/********************************* Copyright **********************************/
+/*
+ * @copy default.g
+ *
+ * Copyright (c) Mbedthis Software LLC, 2003-2005. 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_INTERNAL
+#define _h_EJS_INTERNAL 1
+
+#include "ejs.h"
+
+/********************************** Defines ***********************************/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Constants
+ */
+
+#if BLD_FEATURE_SQUEEZE
+ #define EJS_PARSE_INCR 256 /* Growth factor */
+ #define EJS_MAX_RECURSE 25 /* Sanity for maximum recursion */
+ #define EJS_MAX_ID 128 /* Maximum ID length */
+ #define EJS_OBJ_HASH_SIZE 13 /* Object hash table size */
+ #define EJS_SMALL_OBJ_HASH_SIZE 11 /* Small object hash size */
+ #define EJS_LIST_INCR 8 /* Growth increment for lists */
+#else
+ #define EJS_PARSE_INCR 1024 /* Growth factor */
+ #define EJS_MAX_RECURSE 100 /* Sanity for maximum recursion */
+ #define EJS_MAX_ID 256 /* Maximum ID length */
+ #define EJS_OBJ_HASH_SIZE 29 /* Object hash table size */
+ #define EJS_SMALL_OBJ_HASH_SIZE 11 /* Small object hash size */
+ #define EJS_LIST_INCR 16 /* Growth increment for lists */
+#endif
+#define EJS_TOKEN_STACK 4 /* Put back token 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_FUNCTION_NAME 11 /* functionName */
+#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 */
+
+/*
+ * 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 /* ! */
+
+/*
+ * States
+ */
+#define EJS_STATE_ERR -1 /* Error state */
+#define EJS_STATE_EOF 1 /* 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 /* Function 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_RET 20 /* Return statement */
+
+#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_FOREACH 0x40 /* In foreach */
+#define EJS_FLAGS_NEW 0x80 /* In a new stmt() */
+#define EJS_FLAGS_EXIT 0x100 /* Must exit */
+
+/*
+ * Putback token
+ */
+
+typedef struct EjsToken {
+ char *token; /* Token string */
+ int id; /* Token ID */
+} EjsToken;
+
+/*
+ * EJ evaluation block structure
+ */
+typedef struct ejEval {
+ EjsToken putBack[EJS_TOKEN_STACK]; /* Put back token stack */
+ int putBackIndex; /* Top of stack index */
+ MprStr line; /* Current line */
+ int lineLength; /* Current line length */
+ int lineNumber; /* Parse line number */
+ int lineColumn; /* Column in line */
+ MprStr script; /* Input script for parsing */
+ char *scriptServp; /* Next token in the script */
+ int scriptSize; /* Length of script */
+ MprStr tokbuf; /* Current token */
+ char *tokEndp; /* Pointer past end of token */
+ char *tokServp; /* Pointer to next token char */
+ int tokSize; /* Size of token buffer */
+ struct ejEval *next; /* used for backtraces */
+ const char *procName; /* gives name in backtrace */
+} EjsInput;
+
+/*
+ * Function call structure
+ */
+typedef struct {
+ MprArray *args; /* Args for function */
+ MprVar *fn; /* Function definition */
+ char *procName; /* Function name */
+} EjsProc;
+
+/*
+ * Per EJS structure
+ */
+typedef struct ej {
+ EjsHandle altHandle; /* alternate callback handle */
+ MprVar *currentObj; /* Ptr to current object */
+ MprVar *currentProperty; /* Ptr to current property */
+ EjsId eid; /* Halloc handle */
+ char *error; /* Error message */
+ int exitStatus; /* Status to exit() */
+ int flags; /* Flags */
+ MprArray *frames; /* List of variable frames */
+ MprVar *global; /* Global object */
+ EjsInput *input; /* Input evaluation block */
+ MprVar *local; /* Local object */
+ EjsHandle primaryHandle; /* primary callback handle */
+ EjsProc *proc; /* Current function */
+ MprVar result; /* Variable result */
+ void *thisPtr; /* C++ ptr for functions */
+ int tid; /* Current token id */
+ char *token; /* Pointer to token string */
+ MprVar tokenNumber; /* Parsed number */
+} Ejs;
+
+typedef int EjsBlock; /* Scope block id */
+
+/*
+ * Function callback when using Alternate handles.
+ */
+typedef int (*EjsAltStringCFunction)(EjsHandle userHandle, EjsHandle altHandle,
+ int argc, char **argv);
+typedef int (*EjsAltCFunction)(EjsHandle userHandle, EjsHandle altHandle,
+ int argc, MprVar **argv);
+
+/******************************** Prototypes **********************************/
+/*
+ * Ejs Lex
+ */
+extern int ejsLexOpenScript(Ejs* ep, char *script);
+extern void ejsLexCloseScript(Ejs* ep);
+extern int ejsInitInputState(EjsInput *ip);
+extern void ejsLexSaveInputState(Ejs* ep, EjsInput* state);
+extern void ejsLexFreeInputState(Ejs* ep, EjsInput* state);
+extern void ejsLexRestoreInputState(Ejs* ep, EjsInput* state);
+extern int ejsLexGetToken(Ejs* ep, int state);
+extern void ejsLexPutbackToken(Ejs* ep, int tid, char *string);
+
+/*
+ * Parsing
+ */
+extern MprVar *ejsFindObj(Ejs *ep, int state, const char *property,
+ int flags);
+extern MprVar *ejsFindProperty(Ejs *ep, int state, MprVar *obj,
+ char *property, int flags);
+extern int ejsGetVarCore(Ejs *ep, const char *var, MprVar **obj,
+ MprVar **varValue, int flags);
+extern int ejsParse(Ejs *ep, int state, int flags);
+extern Ejs *ejsPtr(EjsId eid);
+extern void ejsSetExitStatus(int eid, int status);
+extern void ejsSetFlags(int orFlags, int andFlags);
+
+/*
+ * Create variable scope blocks
+ */
+extern EjsBlock ejsOpenBlock(EjsId eid);
+extern int ejsCloseBlock(EjsId eid, EjsBlock vid);
+extern int ejsEvalBlock(EjsId eid, char *script, MprVar *v, char **err);
+extern int ejsDefineStandardProperties(MprVar *objVar);
+
+/*
+ * Error handling
+ */
+extern void ejsError(Ejs *ep, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _h_EJS_INTERNAL */
+
+/*
+ * 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/ejsLex.c b/source4/lib/appweb/ejs/ejsLex.c
new file mode 100644
index 0000000000..b9a363cfc9
--- /dev/null
+++ b/source4/lib/appweb/ejs/ejsLex.c
@@ -0,0 +1,923 @@
+/*
+ * @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-2005. 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 "ejsInternal.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);
+
+/************************************* Code ***********************************/
+/*
+ * Open a new input script
+ */
+
+int ejsLexOpenScript(Ejs *ep, char *script)
+{
+ EjsInput *ip;
+
+ mprAssert(ep);
+ mprAssert(script);
+
+ if ((ip = mprMalloc(sizeof(EjsInput))) == NULL) {
+ return -1;
+ }
+ memset(ip, 0, sizeof(*ip));
+ ip->next = ep->input;
+ ep->input = ip;
+ ip->procName = ep->proc?ep->proc->procName:NULL;
+
+/*
+ * Create the parse token buffer and script buffer
+ */
+ ip->tokbuf = mprMalloc(EJS_PARSE_INCR);
+ ip->tokSize = EJS_PARSE_INCR;
+ ip->tokServp = ip->tokbuf;
+ ip->tokEndp = ip->tokbuf;
+
+ ip->script = mprStrdup(script);
+ ip->scriptSize = strlen(script);
+ ip->scriptServp = ip->script;
+
+ ip->lineNumber = 1;
+ ip->lineLength = 0;
+ ip->lineColumn = 0;
+ ip->line = NULL;
+
+ ip->putBackIndex = -1;
+
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Close the input script
+ */
+
+void ejsLexCloseScript(Ejs *ep)
+{
+ EjsInput *ip;
+ int i;
+
+ mprAssert(ep);
+
+ ip = ep->input;
+ mprAssert(ip);
+ ep->input = ip->next;
+
+ for (i = 0; i < EJS_TOKEN_STACK; i++) {
+ mprFree(ip->putBack[i].token);
+ ip->putBack[i].token = 0;
+ }
+
+ mprFree(ip->line);
+ mprFree(ip->tokbuf);
+ mprFree(ip->script);
+
+ 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++) {
+ state->putBack[i].token = mprStrdup(ip->putBack[i].token);
+ state->putBack[i].id = ip->putBack[i].id;
+ }
+ for (; i < EJS_TOKEN_STACK; i++) {
+ state->putBack[i].token = 0;
+ }
+
+ state->line = mprMalloc(ip->lineLength);
+ mprStrcpy(state->line, ip->lineLength, ip->line);
+
+ state->lineColumn = ip->lineColumn;
+ state->lineNumber = ip->lineNumber;
+ state->lineLength = ip->lineLength;
+}
+
+/******************************************************************************/
+/*
+ * Restore the input state
+ */
+
+void ejsLexRestoreInputState(Ejs *ep, EjsInput *state)
+{
+ EjsInput *ip;
+ int i;
+
+ mprAssert(ep);
+ mprAssert(state);
+
+ ip = ep->input;
+ mprAssert(ip);
+
+ ip->tokbuf = state->tokbuf;
+ ip->tokServp = state->tokServp;
+ ip->tokEndp = state->tokEndp;
+ ip->tokSize = state->tokSize;
+
+ ip->script = state->script;
+ ip->scriptServp = state->scriptServp;
+ ip->scriptSize = state->scriptSize;
+
+ ip->putBackIndex = state->putBackIndex;
+ for (i = 0; i < ip->putBackIndex; i++) {
+ mprFree(ip->putBack[i].token);
+ ip->putBack[i].id = state->putBack[i].id;
+ ip->putBack[i].token = mprStrdup(state->putBack[i].token);
+ }
+
+ mprFree(ip->line);
+ ip->line = mprMalloc(state->lineLength);
+ mprStrcpy(ip->line, state->lineLength, state->line);
+
+ ip->lineColumn = state->lineColumn;
+ ip->lineNumber = state->lineNumber;
+ ip->lineLength = state->lineLength;
+}
+
+/******************************************************************************/
+/*
+ * Free a saved input state
+ */
+
+void ejsLexFreeInputState(Ejs *ep, EjsInput *state)
+{
+ int i;
+
+ mprAssert(ep);
+ mprAssert(state);
+
+ for (i = 0; i < EJS_TOKEN_STACK; i++) {
+ mprFree(state->putBack[i].token);
+ }
+ state->putBackIndex = -1;
+ mprFree(state->line);
+ state->lineLength = 0;
+ 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", "foreach",
+ * "delete", "function", and "return". "new", "in" and "function"
+ * done below. "true", "false", "null", "undefined" are handled
+ * as global objects.
+ *
+ * Other reserved words not supported:
+ * "break", "case", "catch", "continue", "default", "do",
+ * "finally", "instanceof", "switch", "this", "throw", "try",
+ * "typeof", "while", "with"
+ *
+ * ECMA extensions reserved words (not supported):
+ * "abstract", "boolean", "byte", "char", "class", "const",
+ * "debugger", "double", "enum", "export", "extends",
+ * "final", "float", "goto", "implements", "import", "int",
+ * "interface", "long", "native", "package", "private",
+ * "protected", "public", "short", "static", "super",
+ * "synchronized", "throws", "transient", "volatile"
+ */
+
+static int checkReservedWord(Ejs *ep, int state, int c, int tid)
+{
+ if (state == EJS_STATE_STMT) {
+ 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, "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, "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;
+ }
+ }
+ return tid;
+}
+
+/******************************************************************************/
+/*
+ * Get the next EJS token
+ */
+
+static int getLexicalToken(Ejs *ep, int state)
+{
+ MprType type;
+ EjsInput *ip;
+ int done, tid, c, quote, style, idx;
+
+ mprAssert(ep);
+ ip = ep->input;
+ mprAssert(ip);
+
+ ep->tid = -1;
+ tid = -1;
+ type = BLD_FEATURE_NUM_TYPE_ID;
+
+ /*
+ * Use a putback tokens first. Don't free strings as caller needs access.
+ */
+ if (ip->putBackIndex >= 0) {
+ idx = ip->putBackIndex;
+ tid = ip->putBack[idx].id;
+ ep->token = (char*) ip->putBack[idx].token;
+ 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) {
+ ejsError(ep, "Syntax Error");
+ 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) {
+ ejsError(ep, "Syntax Error");
+ 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) {
+ ejsError(ep, "Syntax Error");
+ 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;
+ }
+ ejsError(ep, "Syntax Error");
+ 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) {
+ ejsError(ep, "Syntax Error");
+ 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) {
+ ejsError(ep, "Syntax Error");
+ 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) {
+ ejsError(ep, "Syntax Error");
+ 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) {
+ ejsError(ep, "Syntax Error");
+ 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 '|': /* "||" */
+ if ((c = inputGetc(ep)) < 0 || c != '|') {
+ ejsError(ep, "Syntax Error");
+ return EJS_TOK_ERR;
+ }
+ tokenAddChar(ep, EJS_COND_OR);
+ return EJS_TOK_LOGICAL;
+
+ case '&': /* "&&" */
+ if ((c = inputGetc(ep)) < 0 || c != '&') {
+ ejsError(ep, "Syntax Error");
+ return EJS_TOK_ERR;
+ }
+ tokenAddChar(ep, EJS_COND_AND);
+ return EJS_TOK_LOGICAL;
+
+ case '\"': /* String quote */
+ case '\'':
+ quote = c;
+ if ((c = inputGetc(ep)) < 0) {
+ ejsError(ep, "Syntax Error");
+ 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:
+ ejsError(ep, "Invalid Escape Sequence");
+ 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) {
+ ejsError(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') {
+ do {
+ if (tokenAddChar(ep, c) < 0) {
+ return EJS_TOK_ERR;
+ }
+ if ((c = inputGetc(ep)) < 0) {
+ break;
+ }
+ } while (isdigit(c) || (tolower(c) >= 'a' && tolower(c) <= 'f'));
+
+ mprDestroyVar(&ep->tokenNumber);
+ ep->tokenNumber = mprParseVar(ep->token, type);
+ inputPutback(ep, c);
+ return EJS_TOK_NUMBER;
+ }
+ if (! isdigit(c)) {
+#if BLD_FEATURE_FLOATING_POINT
+ if (c == '.' || tolower(c) == 'e' || c == '+' || c == '-') {
+ /* Fall through */
+ type = MPR_TYPE_FLOAT;
+ } else
+#endif
+ {
+ mprDestroyVar(&ep->tokenNumber);
+ ep->tokenNumber = mprParseVar(ep->token, 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':
+ do {
+ if (tokenAddChar(ep, c) < 0) {
+ return EJS_TOK_ERR;
+ }
+ if ((c = inputGetc(ep)) < 0) {
+ break;
+ }
+#if BLD_FEATURE_FLOATING_POINT
+ if (c == '.' || tolower(c) == 'e' || tolower(c) == 'f') {
+ type = MPR_TYPE_FLOAT;
+ }
+ } while (isdigit(c) || c == '.' || tolower(c) == 'e' || tolower(c) == 'f' ||
+ ((type == MPR_TYPE_FLOAT) && (c == '+' || c == '-')));
+#else
+ } while (isdigit(c));
+#endif
+
+ mprDestroyVar(&ep->tokenNumber);
+ ep->tokenNumber = mprParseVar(ep->token, 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 != '\\') {
+ break;
+ }
+ }
+ if (*ep->token == '\0') {
+ c = inputGetc(ep);
+ break;
+ }
+ if (! isalpha((int) *ep->token) && *ep->token != '$' &&
+ *ep->token != '_') {
+ ejsError(ep, "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;
+ done++;
+ }
+ }
+
+ /*
+ * Putback the last extra character for next time
+ */
+ inputPutback(ep, c);
+ return tid;
+}
+
+/******************************************************************************/
+/*
+ * 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;
+ int idx;
+
+ mprAssert(ep);
+ ip = ep->input;
+ mprAssert(ip);
+
+ ip->putBackIndex += 1;
+ idx = ip->putBackIndex;
+ ip->putBack[idx].id = tid;
+
+ if (ip->putBack[idx].token) {
+ if (ip->putBack[idx].token == string) {
+ return;
+ }
+ mprFree(ip->putBack[idx].token);
+ }
+ ip->putBack[idx].token = mprStrdup(string);
+}
+
+/******************************************************************************/
+/*
+ * Add a character to the token buffer
+ */
+
+static int tokenAddChar(Ejs *ep, int c)
+{
+ EjsInput *ip;
+ uchar *oldbuf;
+
+ mprAssert(ep);
+ ip = ep->input;
+ mprAssert(ip);
+
+ if (ip->tokEndp >= &ip->tokbuf[ip->tokSize - 1]) {
+ ip->tokSize += EJS_PARSE_INCR;
+ oldbuf = ip->tokbuf;
+ ip->tokbuf = mprRealloc(ip->tokbuf, ip->tokSize);
+ if (ip->tokbuf == 0) {
+ ejsError(ep, "Token too big");
+ return -1;
+ }
+ ip->tokEndp += (int) ((uchar*) ip->tokbuf - oldbuf);
+ ip->tokServp += (int) ((uchar*) ip->tokbuf - oldbuf);
+ ep->token += (int) ((uchar*) ip->tokbuf - oldbuf);
+ }
+ *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 BLD_DEBUG && 0
+ if (ip->lineColumn > 0) {
+ printf("PARSED: %s\n", ip->line);
+ }
+#endif
+ ip->lineNumber++;
+ ip->lineColumn = 0;
+ } else {
+ if ((ip->lineColumn + 2) >= ip->lineLength) {
+ ip->lineLength += 80;
+ ip->line = mprRealloc(ip->line, ip->lineLength * sizeof(char));
+ }
+ 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++;
+ ip->lineColumn--;
+ 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/ejsLib.c b/source4/lib/appweb/ejs/ejsLib.c
new file mode 100644
index 0000000000..67d0a4e760
--- /dev/null
+++ b/source4/lib/appweb/ejs/ejsLib.c
@@ -0,0 +1,1090 @@
+/*
+ * @file ejs.c
+ * @brief Embedded JavaScript (EJS)
+ * @overview Main module interface logic.
+ */
+/********************************* Copyright **********************************/
+/*
+ * @copy default.g
+ *
+ * Copyright (c) Mbedthis Software LLC, 2003-2005. 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 "ejsInternal.h"
+
+#if BLD_FEATURE_EJS
+
+/********************************** Local Data ********************************/
+
+/*
+ * These fields must be locked before any access when multithreaded
+ */
+static MprVar master; /* Master object */
+static MprArray *ejsList; /* List of ej handles */
+
+#if BLD_FEATURE_MULTITHREAD
+static EjsLock lock;
+static EjsUnlock unlock;
+static void *lockData;
+#define ejsLock() if (lock) { (lock)(lockData); } else
+#define ejsUnlock() if (unlock) { (unlock)(lockData); } else
+#else
+#define ejsLock()
+#define ejsUnlock()
+#endif
+
+
+/*
+ save/restore global ejs state - used to cope with simultaneous ejs requests
+ this is a workaround for the use of global variables in ejs
+*/
+struct ejs_state_ctx {
+ MprVar master;
+ MprArray *ejsList;
+};
+
+void *ejs_save_state(void)
+{
+ struct ejs_state_ctx *ctx = talloc(talloc_autofree_context(), struct ejs_state_ctx);
+ ctx->master = master;
+ ctx->ejsList = ejsList;
+ return ctx;
+}
+
+void ejs_restore_state(void *ptr)
+{
+ struct ejs_state_ctx *ctx = talloc_get_type(ptr, struct ejs_state_ctx);
+ master = ctx->master;
+ ejsList = ctx->ejsList;
+ talloc_free(ctx);
+}
+
+
+/****************************** Forward Declarations **************************/
+
+static char *getNextVarToken(char **next, char *tokBuf, int tokBufLen);
+
+/************************************* Code ***********************************/
+/*
+ * Initialize the EJ subsystem
+ */
+
+int ejsOpen(EjsLock lockFn, EjsUnlock unlockFn, void *data)
+{
+ MprVar *np;
+
+#if BLD_FEATURE_MULTITHREAD
+ if (lockFn) {
+ lock = lockFn;
+ unlock = unlockFn;
+ lockData = data;
+ }
+#endif
+ ejsLock();
+
+ /*
+ * Master is the top level object (above global). It is used to clone its
+ * contents into the global scope for each. This is never visible to the
+ * user, so don't use ejsCreateObj().
+ */
+ master = mprCreateObjVar("master", EJS_SMALL_OBJ_HASH_SIZE);
+ if (master.type == MPR_TYPE_UNDEFINED) {
+ ejsUnlock();
+ return MPR_ERR_CANT_ALLOCATE;
+ }
+
+ ejsList = mprCreateArray();
+ ejsDefineStandardProperties(&master);
+
+ /*
+ * Make these objects immutable
+ */
+ np = mprGetFirstProperty(&master, MPR_ENUM_FUNCTIONS | MPR_ENUM_DATA);
+ while (np) {
+ mprSetVarReadonly(np, 1);
+ np = mprGetNextProperty(&master, np, MPR_ENUM_FUNCTIONS |
+ MPR_ENUM_DATA);
+ }
+ ejsUnlock();
+ return 0;
+}
+
+/******************************************************************************/
+
+void ejsClose()
+{
+ ejsLock();
+ mprDestroyArray(ejsList);
+ mprDestroyVar(&master);
+ ejsUnlock();
+}
+
+/******************************************************************************/
+/*
+ * Create and initialize an EJS engine
+ */
+
+EjsId ejsOpenEngine(EjsHandle primaryHandle, EjsHandle altHandle)
+{
+ MprVar *np;
+ Ejs *ep;
+
+ ep = (Ejs *)mprMalloc(sizeof(Ejs));
+ if (ep == 0) {
+ return (EjsId) -1;
+ }
+ memset(ep, 0, sizeof(Ejs));
+
+ ejsLock();
+ ep->eid = (EjsId) mprAddToArray(ejsList, ep);
+ ejsUnlock();
+
+ /*
+ * Create array of local variable frames
+ */
+ ep->frames = mprCreateArray();
+ if (ep->frames == 0) {
+ ejsCloseEngine(ep->eid);
+ return (EjsId) -1;
+ }
+ ep->primaryHandle = primaryHandle;
+ ep->altHandle = altHandle;
+
+ /*
+ * Create first frame: global variables
+ */
+ ep->global = (MprVar*) mprMalloc(sizeof(MprVar));
+ *ep->global = ejsCreateObj("global", EJS_OBJ_HASH_SIZE);
+ if (ep->global->type == MPR_TYPE_UNDEFINED) {
+ ejsCloseEngine(ep->eid);
+ return (EjsId) -1;
+ }
+ mprAddToArray(ep->frames, ep->global);
+
+ /*
+ * Create first local variable frame
+ */
+ ep->local = (MprVar*) mprMalloc(sizeof(MprVar));
+ *ep->local = ejsCreateObj("local", EJS_OBJ_HASH_SIZE);
+ if (ep->local->type == MPR_TYPE_UNDEFINED) {
+ ejsCloseEngine(ep->eid);
+ return (EjsId) -1;
+ }
+ mprAddToArray(ep->frames, ep->local);
+
+ /*
+ * Clone all master variables into the global frame. This does a
+ * reference copy.
+ *
+ * ejsDefineStandardProperties(ep->global);
+ */
+ np = mprGetFirstProperty(&master, MPR_ENUM_FUNCTIONS | MPR_ENUM_DATA);
+ while (np) {
+ mprCreateProperty(ep->global, np->name, np);
+ np = mprGetNextProperty(&master, np, MPR_ENUM_FUNCTIONS |
+ MPR_ENUM_DATA);
+ }
+
+ mprCreateProperty(ep->global, "global", ep->global);
+ mprCreateProperty(ep->global, "this", ep->global);
+ mprCreateProperty(ep->local, "local", ep->local);
+
+ return ep->eid;
+}
+
+/******************************************************************************/
+/*
+ * Close an EJS instance
+ */
+
+void ejsCloseEngine(EjsId eid)
+{
+ Ejs *ep;
+ MprVar *vp;
+ void **handles;
+ int i;
+
+ if ((ep = ejsPtr(eid)) == NULL) {
+ mprAssert(ep);
+ return;
+ }
+
+ mprFree(ep->error);
+ mprDestroyVar(&ep->result);
+ mprDestroyVar(&ep->tokenNumber);
+
+ if (ep->local) {
+ mprDeleteProperty(ep->local, "local");
+ }
+ mprDeleteProperty(ep->global, "this");
+ mprDeleteProperty(ep->global, "global");
+
+ handles = ep->frames->handles;
+ for (i = 0; i < ep->frames->max; i++) {
+ vp = handles[i];
+ if (vp) {
+#if BLD_DEBUG
+ if (vp->type == MPR_TYPE_OBJECT && vp->properties->refCount > 1) {
+ mprLog(7, "ejsCloseEngine: %s has ref count %d\n",
+ vp->name, vp->properties->refCount);
+ }
+#endif
+ mprDestroyVar(vp);
+ mprFree(vp);
+ mprRemoveFromArray(ep->frames, i);
+ }
+ }
+ mprDestroyArray(ep->frames);
+
+ ejsLock();
+ mprRemoveFromArray(ejsList, (int) ep->eid);
+ ejsUnlock();
+
+ mprFree(ep);
+}
+
+/******************************************************************************/
+/*
+ * Evaluate an EJS script file
+ */
+
+int ejsEvalFile(EjsId eid, char *path, MprVar *result, char **emsg)
+{
+ struct stat sbuf;
+ Ejs *ep;
+ char *script;
+ int rc, fd;
+
+ mprAssert(path && *path);
+
+ if (emsg) {
+ *emsg = NULL;
+ }
+
+ if ((ep = ejsPtr(eid)) == NULL) {
+ mprAssert(ep);
+ goto error;
+ }
+
+ if ((fd = open(path, O_RDONLY | O_BINARY, 0666)) < 0) {
+ ejsError(ep, "Can't open %s\n", path);
+ goto error;
+ }
+
+ if (stat(path, &sbuf) < 0) {
+ close(fd);
+ ejsError(ep, "Cant stat %s", path);
+ goto error;
+ }
+
+ if ((script = (char*) mprMalloc(sbuf.st_size + 1)) == NULL) {
+ close(fd);
+ ejsError(ep, "Cant malloc %d", (int) sbuf.st_size);
+ goto error;
+ }
+
+ if (read(fd, script, sbuf.st_size) != (int) sbuf.st_size) {
+ close(fd);
+ mprFree(script);
+ ejsError(ep, "Error reading %s", path);
+ goto error;
+ }
+
+ script[sbuf.st_size] = '\0';
+ close(fd);
+
+ rc = ejsEvalBlock(eid, script, result, emsg);
+ mprFree(script);
+
+ return rc;
+
+/*
+ * Error return
+ */
+error:
+ if(emsg)
+ *emsg = mprStrdup(ep->error);
+ 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(EjsId eid)
+{
+ Ejs *ep;
+
+ if((ep = ejsPtr(eid)) == NULL) {
+ return -1;
+ }
+
+ ep->local = (MprVar*) mprMalloc(sizeof(MprVar));
+ *ep->local = ejsCreateObj("localBlock", EJS_OBJ_HASH_SIZE);
+
+ mprCreateProperty(ep->local, "local", ep->local);
+
+ return mprAddToArray(ep->frames, ep->local);
+}
+
+/******************************************************************************/
+/*
+ * Close a variable scope block opened via ejsOpenBlock. Pop back the old
+ * local variables frame.
+ */
+
+int ejsCloseBlock(EjsId eid, int fid)
+{
+ Ejs *ep;
+
+ if((ep = ejsPtr(eid)) == NULL) {
+ mprAssert(ep);
+ return -1;
+ }
+
+ /*
+ * Must remove self-references before destroying "local"
+ */
+ mprDeleteProperty(ep->local, "local");
+
+ mprDestroyVar(ep->local);
+ mprFree(ep->local);
+
+ mprRemoveFromArray(ep->frames, fid);
+ ep->local = (MprVar*) ep->frames->handles[ep->frames->used - 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 and emsg are optional. i.e. created local variables will be discarded
+ * when this routine returns.
+ */
+
+int ejsEvalBlock(EjsId eid, char *script, MprVar *vp, char **emsg)
+{
+ int rc, fid;
+
+ mprAssert(script);
+
+ fid = ejsOpenBlock(eid);
+ rc = ejsEvalScript(eid, script, vp, emsg);
+ ejsCloseBlock(eid, fid);
+
+ return rc;
+}
+
+/******************************************************************************/
+/*
+ * Parse and evaluate a EJS. 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. On errors, emsg will be set to the reason. The caller must
+ * free emsg.
+ */
+
+int ejsEvalScript(EjsId eid, char *script, MprVar *vp, char **emsg)
+{
+ Ejs *ep;
+ int state;
+ void *endlessLoopTest;
+ int loopCounter;
+
+ if (emsg) {
+ *emsg = NULL;
+ }
+
+ if ((ep = ejsPtr(eid)) == NULL) {
+ mprAssert(ep);
+ return -1;
+ }
+
+ mprDestroyVar(&ep->result);
+
+ if (script == 0) {
+ return 0;
+ }
+
+ /*
+ * Allocate a new evaluation block, and save the old one
+ */
+ ejsLexOpenScript(ep, script);
+
+ /*
+ * Do the actual parsing and evaluation
+ */
+ loopCounter = 0;
+ endlessLoopTest = NULL;
+ ep->exitStatus = 0;
+
+ do {
+ state = ejsParse(ep, EJS_STATE_BEGIN, EJS_FLAGS_EXE);
+
+ if (state == EJS_STATE_RET) {
+ state = EJS_STATE_EOF;
+ }
+ /*
+ * Stuck parser and endless recursion protection.
+ */
+ if (endlessLoopTest == ep->input->scriptServp) {
+ if (loopCounter++ > 10) {
+ state = EJS_STATE_ERR;
+ ejsError(ep, "Syntax error");
+ }
+ } else {
+ endlessLoopTest = ep->input->scriptServp;
+ loopCounter = 0;
+ }
+ } while (state != EJS_STATE_EOF && state != EJS_STATE_ERR);
+
+ ejsLexCloseScript(ep);
+
+ /*
+ * Return any error string to the user
+ */
+ if (state == EJS_STATE_ERR && emsg) {
+ *emsg = mprStrdup(ep->error);
+ }
+
+ if (state == EJS_STATE_ERR) {
+ return -1;
+ }
+
+ if (vp) {
+ *vp = ep->result;
+ }
+
+ return ep->exitStatus;
+}
+
+/******************************************************************************/
+/*
+ * Core error handling
+ */
+
+static void ejsErrorCore(Ejs* ep, const char *fmt, va_list args)
+ PRINTF_ATTRIBUTE(2, 0);
+
+static void ejsErrorCore(Ejs* ep, const char *fmt, va_list args)
+{
+ EjsInput *ip;
+ char *errbuf, *msgbuf;
+ int frame = 0;
+
+ mprAssert(ep);
+
+ msgbuf = NULL;
+ mprAllocVsprintf(&msgbuf, MPR_MAX_STRING, fmt, args);
+
+ ip = ep->input;
+ mprAllocSprintf(&errbuf, MPR_MAX_STRING, "%s\nBacktrace:\n", msgbuf);
+
+ /* form a backtrace */
+ while (ip) {
+ char *msg2, *ebuf2;
+ mprAllocSprintf(&msg2, MPR_MAX_STRING,
+ "\t[%2d] %20s:%-4d -> %s\n",
+ frame++, ip->procName?ip->procName:"", ip->lineNumber, ip->line);
+ ebuf2 = mprRealloc(errbuf, strlen(errbuf) + strlen(msg2) + 1);
+ if (ebuf2 == NULL) break;
+ errbuf = ebuf2;
+ memcpy(errbuf+strlen(errbuf), msg2, strlen(msg2)+1);
+ mprFree(msg2);
+ ip = ip->next;
+ }
+ mprFree(ep->error);
+ ep->error = errbuf;
+ mprFree(msgbuf);
+}
+
+/******************************************************************************/
+/*
+ * Internal use function to set the error message
+ */
+
+void ejsError(Ejs* ep, const char* fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ ejsErrorCore(ep, fmt, args);
+ va_end(args);
+}
+
+/******************************************************************************/
+/*
+ * Public routine to set the error message
+ */
+
+void ejsSetErrorMsg(EjsId eid, const char* fmt, ...)
+{
+ va_list args;
+ Ejs *ep;
+
+ if ((ep = ejsPtr(eid)) == NULL) {
+ mprAssert(ep);
+ return;
+ }
+ va_start(args, fmt);
+ ejsErrorCore(ep, fmt, args);
+ va_end(args);
+}
+
+/******************************************************************************/
+/*
+ * Get the current line number
+ */
+
+int ejsGetLineNumber(EjsId eid)
+{
+ Ejs *ep;
+
+ if ((ep = ejsPtr(eid)) == NULL) {
+ mprAssert(ep);
+ return -1;
+ }
+ return ep->input->lineNumber;
+}
+
+/******************************************************************************/
+/*
+ * Return the local object
+ */
+
+MprVar *ejsGetLocalObject(EjsId eid)
+{
+ Ejs *ep;
+
+ if ((ep = ejsPtr(eid)) == NULL) {
+ mprAssert(ep);
+ return 0;
+ }
+ return ep->local;
+}
+
+/******************************************************************************/
+/*
+ * Return the global object
+ */
+
+MprVar *ejsGetGlobalObject(EjsId eid)
+{
+ Ejs *ep;
+
+ if ((ep = ejsPtr(eid)) == NULL) {
+ mprAssert(ep);
+ return 0;
+ }
+ return ep->global;
+}
+
+/******************************************************************************/
+/*
+ * Copy the value of an object property. Return value is in "value".
+ * If deepCopy is true, copy all object/strings. Otherwise, object reference
+ * counts are incremented. Callers must always call mprDestroyVar on the
+ * return value to prevent leaks.
+ *
+ * Returns: -1 on errors or if the variable is not found.
+ */
+
+int ejsCopyVar(EjsId eid, const char *var, MprVar *value, bool deepCopy)
+{
+ Ejs *ep;
+ MprVar *vp;
+
+ mprAssert(var && *var);
+ mprAssert(value);
+
+ if ((ep = ejsPtr(eid)) == NULL) {
+ mprAssert(ep);
+ return -1;
+ }
+
+ if (ejsGetVarCore(ep, var, 0, &vp, 0) < 0) {
+ return -1;
+ }
+
+ return mprCopyProperty(value, vp, deepCopy);
+}
+
+/******************************************************************************/
+/*
+ * Return the value of an object property. Return value is in "value".
+ * Objects and strings are not copied and reference counts are not modified.
+ * Callers should NOT call mprDestroyVar. Returns: -1 on errors or if the
+ * variable is not found.
+ */
+
+int ejsReadVar(EjsId eid, const char *var, MprVar *value)
+{
+ Ejs *ep;
+ MprVar *vp;
+
+ mprAssert(var && *var);
+ mprAssert(value);
+
+ if ((ep = ejsPtr(eid)) == NULL) {
+ mprAssert(ep);
+ return -1;
+ }
+
+ if (ejsGetVarCore(ep, var, 0, &vp, 0) < 0) {
+ return -1;
+ }
+
+ return mprReadProperty(vp, value);
+}
+
+/******************************************************************************/
+/*
+ * Set a variable that may be an arbitrarily complex object or array reference.
+ * Will always define in the top most variable frame.
+ */
+
+int ejsWriteVar(EjsId eid, const char *var, MprVar *value)
+{
+ Ejs *ep;
+ MprVar *vp;
+
+ mprAssert(var && *var);
+
+ if ((ep = ejsPtr(eid)) == NULL) {
+ mprAssert(ep);
+ return -1;
+ }
+
+ if (ejsGetVarCore(ep, var, 0, &vp, EJS_FLAGS_CREATE) < 0) {
+ return -1;
+ }
+ mprAssert(vp);
+
+ /*
+ * Only copy the value. Don't overwrite the object's name
+ */
+ mprWriteProperty(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 ejsWriteVarValue(EjsId eid, const char *var, MprVar value)
+{
+ return ejsWriteVar(eid, var, &value);
+}
+
+/******************************************************************************/
+/*
+ * Delete a variable
+ */
+
+int ejsDeleteVar(EjsId eid, const char *var)
+{
+ Ejs *ep;
+ MprVar *vp;
+ MprVar *obj;
+
+ if ((ep = ejsPtr(eid)) == NULL) {
+ mprAssert(ep);
+ return -1;
+ }
+ if (ejsGetVarCore(ep, var, &obj, &vp, 0) < 0) {
+ return -1;
+ }
+ mprDeleteProperty(obj, vp->name);
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Set the expression return value
+ */
+
+void ejsSetReturnValue(EjsId eid, MprVar value)
+{
+ Ejs *ep;
+
+ if ((ep = ejsPtr(eid)) == NULL) {
+ mprAssert(ep);
+ return;
+ }
+ mprCopyVar(&ep->result, &value, MPR_SHALLOW_COPY);
+}
+
+/******************************************************************************/
+/*
+ * Set the expression return value to a string value
+ */
+
+void ejsSetReturnString(EjsId eid, const char *str)
+{
+ Ejs *ep;
+
+ if ((ep = ejsPtr(eid)) == NULL) {
+ mprAssert(ep);
+ return;
+ }
+ mprCopyVarValue(&ep->result, mprCreateStringVar(str, 0), MPR_SHALLOW_COPY);
+}
+
+/******************************************************************************/
+/*
+ * Get the expression return value
+ */
+
+MprVar *ejsGetReturnValue(EjsId eid)
+{
+ Ejs *ep;
+
+ if ((ep = ejsPtr(eid)) == NULL) {
+ mprAssert(ep);
+ return 0;
+ }
+ return &ep->result;
+}
+
+/******************************************************************************/
+/*
+ * Define a C function. If eid < 0, then update the master object with this
+ * function. NOTE: in this case, functionName must be simple without any "." or
+ * "[]" elements. If eid >= 0, add to the specified script engine. In this
+ * case, functionName can be an arbitrary object reference and can contain "."
+ * or "[]".
+ */
+
+void ejsDefineCFunction(EjsId eid, const char *functionName, MprCFunction fn,
+ void *thisPtr, int flags)
+{
+ if (eid < 0) {
+ ejsLock();
+ mprCreatePropertyValue(&master, functionName,
+ mprCreateCFunctionVar(fn, thisPtr, flags));
+ ejsUnlock();
+ } else {
+ ejsWriteVarValue(eid, functionName,
+ mprCreateCFunctionVar(fn, thisPtr, flags));
+ }
+}
+
+/******************************************************************************/
+/*
+ * Define a C function with String arguments
+ */
+
+void ejsDefineStringCFunction(EjsId eid, const char *functionName,
+ MprStringCFunction fn, void *thisPtr, int flags)
+{
+ if (eid < 0) {
+ ejsLock();
+ mprCreatePropertyValue(&master, functionName,
+ mprCreateStringCFunctionVar(fn, thisPtr, flags));
+ ejsUnlock();
+ } else {
+ ejsWriteVarValue(eid, functionName,
+ mprCreateStringCFunctionVar(fn, thisPtr, flags));
+ }
+}
+
+/******************************************************************************/
+/*
+ * Define a JavaScript function. Args should be comma separated.
+ * Body should not contain braces.
+ */
+
+void ejsDefineFunction(EjsId eid, const char *functionName, char *args,
+ char *body)
+{
+ MprVar v;
+
+ v = mprCreateFunctionVar(args, body, 0);
+ if (eid < 0) {
+ ejsLock();
+ mprCreateProperty(&master, functionName, &v);
+ ejsUnlock();
+ } else {
+ ejsWriteVar(eid, functionName, &v);
+ }
+ mprDestroyVar(&v);
+}
+
+/******************************************************************************/
+
+void *ejsGetThisPtr(EjsId eid)
+{
+ Ejs *ep;
+
+ if ((ep = ejsPtr(eid)) == NULL) {
+ mprAssert(ep);
+ return 0;
+ }
+ return ep->thisPtr;
+}
+
+/******************************************************************************/
+/*
+ * Find a variable given a variable name and return the parent object and
+ * the variable itself, the variable . This routine supports variable names
+ * that may be objects or arrays but may NOT have expressions in the array
+ * indicies. Returns -1 on errors or if the variable is not found.
+ */
+
+int ejsGetVarCore(Ejs *ep, const char *vname, MprVar **obj,
+ MprVar **varValue, int flags)
+{
+ MprVar *currentObj;
+ MprVar *currentVar;
+ char tokBuf[EJS_MAX_ID];
+ char *propertyName, *token, *next, *cp, *varName;
+
+ if (obj) {
+ *obj = 0;
+ }
+ if (varValue) {
+ *varValue = 0;
+ }
+ currentObj = ejsFindObj(ep, 0, vname, flags);
+ currentVar = 0;
+ propertyName = 0;
+
+ next = varName = mprStrdup(vname);
+
+ token = getNextVarToken(&next, tokBuf, sizeof(tokBuf));
+
+ while (currentObj != 0 && token != 0 && *token) {
+
+ if (*token == '[') {
+ token = getNextVarToken(&next, tokBuf, sizeof(tokBuf));
+
+ propertyName = token;
+ if (*propertyName == '\"') {
+ propertyName++;
+ if ((cp = strchr(propertyName, '\"')) != 0) {
+ *cp = '\0';
+ }
+ } else if (*propertyName == '\'') {
+ propertyName++;
+ if ((cp = strchr(propertyName, '\'')) != 0) {
+ *cp = '\0';
+ }
+ }
+
+ currentObj = currentVar;
+ currentVar = ejsFindProperty(ep, 0, currentObj, propertyName, 0);
+
+ token = getNextVarToken(&next, tokBuf, sizeof(tokBuf));
+ if (*token != ']') {
+ mprFree(varName);
+ return -1;
+ }
+
+ } else if (*token == '.') {
+ token = getNextVarToken(&next, tokBuf, sizeof(tokBuf));
+ if (!isalpha((int) token[0]) &&
+ token[0] != '_' && token[0] != '$') {
+ mprFree(varName);
+ return -1;
+ }
+
+ propertyName = token;
+ currentObj = currentVar;
+ currentVar = ejsFindProperty(ep, 0, currentObj, token, 0);
+
+ } else {
+ currentVar = ejsFindProperty(ep, 0, currentObj, token, 0);
+ }
+ token = getNextVarToken(&next, tokBuf, sizeof(tokBuf));
+ }
+ mprFree(varName);
+
+ if (currentVar == 0 && currentObj >= 0 && flags & EJS_FLAGS_CREATE) {
+ currentVar = mprCreatePropertyValue(currentObj, propertyName,
+ mprCreateUndefinedVar());
+ }
+ if (obj) {
+ *obj = currentObj;
+ }
+
+ /*
+ * Don't use mprCopyVar as it will copy the data
+ */
+ if (varValue) {
+ *varValue = currentVar;
+ }
+ return currentVar ? 0 : -1;
+}
+
+/******************************************************************************/
+/*
+ * 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;
+}
+
+/******************************************************************************/
+/*
+ * Get the EJS structure pointer
+ */
+
+Ejs *ejsPtr(EjsId eid)
+{
+ Ejs *handle;
+ int intId;
+
+ intId = (int) eid;
+
+ ejsLock();
+ mprAssert(0 <= intId && intId < ejsList->max);
+
+ if (intId < 0 || intId >= ejsList->max || ejsList->handles[intId] == NULL) {
+ mprAssert(0);
+ ejsUnlock();
+ return NULL;
+ }
+ handle = ejsList->handles[intId];
+ ejsUnlock();
+ return handle;
+}
+
+/******************************************************************************/
+/*
+ * 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) {
+ * mprError("Insufficient args\n");
+ * return -1;
+ * }
+ */
+
+int ejsParseArgs(int argc, char **argv, char *fmt, ...)
+{
+ va_list vargs;
+ bool *bp;
+ char *cp, **sp, *s;
+ int *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, bool*);
+ 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;
+}
+
+/******************************************************************************/
+
+#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/ejsParser.c b/source4/lib/appweb/ejs/ejsParser.c
new file mode 100644
index 0000000000..da922a5728
--- /dev/null
+++ b/source4/lib/appweb/ejs/ejsParser.c
@@ -0,0 +1,2436 @@
+/*
+ * @file ejsParser.c
+ * @brief EJS Parser and Execution
+ */
+/********************************* Copyright **********************************/
+/*
+ * @copy default.g
+ *
+ * Copyright (c) Mbedthis Software LLC, 2003-2005. 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 "ejsInternal.h"
+
+#if BLD_FEATURE_EJS
+
+/****************************** Forward Declarations **************************/
+
+static void appendValue(MprVar *v1, MprVar *v2);
+static int evalCond(Ejs *ep, MprVar *lhs, int rel, MprVar *rhs);
+static int evalExpr(Ejs *ep, MprVar *lhs, int rel, MprVar *rhs);
+#if BLD_FEATURE_FLOATING_POINT
+static int evalFloatExpr(Ejs *ep, double l, int rel, double r);
+#endif
+static int evalBoolExpr(Ejs *ep, bool l, int rel, bool r);
+static int evalPtrExpr(Ejs *ep, void *l, int rel, void *r);
+static int evalNumericExpr(Ejs *ep, MprNum l, int rel, MprNum r);
+static int evalStringExpr(Ejs *ep, MprVar *lhs, int rel, MprVar *rhs);
+static int evalFunction(Ejs *ep, MprVar *obj, int flags);
+static void freeProc(EjsProc *proc);
+static int parseArgs(Ejs *ep, int state, int flags);
+static int parseAssignment(Ejs *ep, int state, int flags, char *id,
+ char *fullName);
+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 parseForIn(Ejs *ep, int state, int flags);
+static int parseFunctionDec(Ejs *ep, int state, int flags);
+static int parseFunction(Ejs *ep, int state, int flags, char *id);
+static int parseId(Ejs *ep, int state, int flags, char **id,
+ char **fullName, int *fullNameLen, int *done);
+static int parseInc(Ejs *ep, int state, int flags);
+static int parseIf(Ejs *ep, int state, int flags, int *done);
+static int parseStmt(Ejs *ep, int state, int flags);
+static void removeNewlines(Ejs *ep, int state);
+static void updateResult(Ejs *ep, int state, int flags, MprVar *vp);
+
+/************************************* Code ***********************************/
+/*
+ * Recursive descent parser for EJS
+ */
+
+int ejsParse(Ejs *ep, int state, int flags)
+{
+ mprAssert(ep);
+
+ switch (state) {
+ /*
+ * Any statement, function arguments or conditional expressions
+ */
+ case EJS_STATE_STMT:
+ if ((state = parseStmt(ep, state, flags)) != EJS_STATE_STMT_DONE &&
+ state != EJS_STATE_EOF && state != EJS_STATE_STMT_BLOCK_DONE &&
+ state != EJS_STATE_RET) {
+ state = EJS_STATE_ERR;
+ }
+ break;
+
+ case EJS_STATE_DEC:
+ if ((state = parseStmt(ep, state, flags)) != EJS_STATE_DEC_DONE &&
+ state != EJS_STATE_EOF) {
+ state = EJS_STATE_ERR;
+ }
+ break;
+
+ case EJS_STATE_EXPR:
+ if ((state = parseStmt(ep, state, flags)) != EJS_STATE_EXPR_DONE &&
+ state != EJS_STATE_EOF) {
+ state = EJS_STATE_ERR;
+ }
+ break;
+
+ /*
+ * Variable declaration list
+ */
+ case EJS_STATE_DEC_LIST:
+ state = parseDeclaration(ep, state, flags);
+ break;
+
+ /*
+ * Function argument string
+ */
+ case EJS_STATE_ARG_LIST:
+ state = parseArgs(ep, state, flags);
+ break;
+
+ /*
+ * Logical condition list (relational operations separated by &&, ||)
+ */
+ case EJS_STATE_COND:
+ state = parseCond(ep, state, flags);
+ break;
+
+ /*
+ * Expression list
+ */
+ case EJS_STATE_RELEXP:
+ state = parseExpr(ep, state, flags);
+ break;
+ }
+
+ if (state == EJS_STATE_ERR && ep->error == NULL) {
+ ejsError(ep, "Syntax error");
+ }
+ return state;
+}
+
+/******************************************************************************/
+/*
+ * Parse any statement including functions and simple relational operations
+ */
+
+static int parseStmt(Ejs *ep, int state, int flags)
+{
+ EjsProc *saveProc;
+ MprVar *vp, *saveObj;
+ char *id, *fullName, *initToken;
+ int done, expectSemi, tid, fullNameLen, rel;
+ int initId;
+
+ mprAssert(ep);
+
+ expectSemi = 0;
+ saveProc = NULL;
+ id = 0;
+ fullName = 0;
+ fullNameLen = 0;
+
+ ep->currentObj = 0;
+ ep->currentProperty = 0;
+
+ for (done = 0; !done && state != EJS_STATE_ERR; ) {
+ tid = ejsLexGetToken(ep, state);
+
+ switch (tid) {
+ default:
+ ejsLexPutbackToken(ep, EJS_TOK_EXPR, ep->token);
+ done++;
+ break;
+
+ case EJS_TOK_EXPR:
+ rel = (int) *ep->token;
+ if (state == EJS_STATE_EXPR) {
+ ejsLexPutbackToken(ep, EJS_TOK_EXPR, ep->token);
+ }
+ done++;
+ break;
+
+ case EJS_TOK_LOGICAL:
+ ejsLexPutbackToken(ep, tid, ep->token);
+ done++;
+ break;
+
+ case EJS_TOK_ERR:
+ state = EJS_STATE_ERR;
+ done++;
+ break;
+
+ case EJS_TOK_EOF:
+ state = EJS_STATE_EOF;
+ done++;
+ break;
+
+ 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, tid, ep->token);
+ }
+ done++;
+ break;
+
+ case EJS_TOK_PERIOD:
+ if (flags & EJS_FLAGS_EXE) {
+ if (ep->currentProperty == 0) {
+ ejsError(ep, "Undefined object \"%s\"\n", id);
+ goto error;
+ }
+ }
+ ep->currentObj = ep->currentProperty;
+
+ if ((tid = ejsLexGetToken(ep, state)) != EJS_TOK_ID) {
+ ejsError(ep, "Bad property after '.': %s\n", ep->token);
+ goto error;
+ }
+ mprFree(id);
+ id = mprStrdup(ep->token);
+
+ vp = ejsFindProperty(ep, state, ep->currentObj, id, flags);
+ updateResult(ep, state, flags, vp);
+
+#if BLD_DEBUG
+ fullNameLen = mprReallocStrcat(&fullName, MPR_MAX_VAR, fullNameLen,
+ 0, ".", NULL);
+#endif
+
+ ep->currentProperty = vp;
+ ejsLexPutbackToken(ep, tid, ep->token);
+ break;
+
+ case EJS_TOK_LBRACKET:
+ ep->currentObj = ep->currentProperty;
+ saveObj = ep->currentObj;
+ if (ejsParse(ep, EJS_STATE_RELEXP, flags) != EJS_STATE_RELEXP_DONE){
+ goto error;
+ }
+ ep->currentObj = saveObj;
+
+ mprFree(id);
+ mprVarToString(&id, MPR_MAX_STRING, 0, &ep->result);
+
+ if (id[0] == '\0') {
+ if (flags & EJS_FLAGS_EXE) {
+ ejsError(ep,
+ "[] expression evaluates to the empty string\n");
+ goto error;
+ }
+ } else {
+ vp = ejsFindProperty(ep, state, ep->currentObj, id, flags);
+ ep->currentProperty = vp;
+ updateResult(ep, state, flags, vp);
+ }
+
+#if BLD_DEBUG
+ if (id[0] && strlen(id) < (MPR_MAX_VAR / 2)) {
+ /*
+ * If not executing yet, id may not be known
+ */
+ fullNameLen = mprReallocStrcat(&fullName, MPR_MAX_VAR,
+ fullNameLen, 0, "[", id, "]", NULL);
+ }
+#endif
+
+ if ((tid = ejsLexGetToken(ep, state)) != EJS_TOK_RBRACKET) {
+ ejsError(ep, "Missing ']'\n");
+ goto error;
+ }
+ break;
+
+ case EJS_TOK_ID:
+ state = parseId(ep, state, flags, &id, &fullName, &fullNameLen,
+ &done);
+ if (done && state == EJS_STATE_STMT) {
+ expectSemi++;
+ }
+ break;
+
+ case EJS_TOK_ASSIGNMENT:
+ state = parseAssignment(ep, state, flags, id, fullName);
+ if (state == EJS_STATE_STMT) {
+ expectSemi++;
+ done++;
+ }
+ break;
+
+ case EJS_TOK_INC_DEC:
+ state = parseInc(ep, state, flags);
+ if (state == EJS_STATE_STMT) {
+ expectSemi++;
+ }
+ break;
+
+ case EJS_TOK_NEW:
+ if (ejsParse(ep, EJS_STATE_EXPR, flags | EJS_FLAGS_NEW)
+ != EJS_STATE_EXPR_DONE) {
+ goto error;
+ }
+ break;
+
+ case EJS_TOK_DELETE:
+ if (ejsParse(ep, EJS_STATE_EXPR,
+ flags | EJS_FLAGS_DELETE) != EJS_STATE_EXPR_DONE) {
+ goto error;
+ }
+ if (flags & EJS_FLAGS_EXE) {
+ mprDeleteProperty(ep->currentObj, ep->currentProperty->name);
+ }
+ done++;
+ break;
+
+ case EJS_TOK_FUNCTION:
+ state = parseFunctionDec(ep, state, flags);
+ done++;
+ break;
+
+ case EJS_TOK_LITERAL:
+ /*
+ * Set the result to the string literal
+ */
+ mprCopyVarValue(&ep->result, mprCreateStringVar(ep->token, 0),
+ MPR_SHALLOW_COPY);
+ if (state == EJS_STATE_STMT) {
+ expectSemi++;
+ }
+ done++;
+ break;
+
+ case EJS_TOK_NUMBER:
+ /*
+ * Set the result to the parsed number
+ */
+ mprCopyVar(&ep->result, &ep->tokenNumber, 0);
+ if (state == EJS_STATE_STMT) {
+ expectSemi++;
+ }
+ done++;
+ break;
+
+ case EJS_TOK_FUNCTION_NAME:
+ state = parseFunction(ep, state, flags, id);
+ if (state == EJS_STATE_STMT) {
+ expectSemi++;
+ }
+ if (ep->flags & EJS_FLAGS_EXIT) {
+ state = EJS_STATE_RET;
+ }
+ done++;
+ break;
+
+ case EJS_TOK_IF:
+ state = parseIf(ep, state, flags, &done);
+ if (state == EJS_STATE_RET) {
+ goto doneParse;
+ }
+ break;
+
+ case EJS_TOK_FOR:
+ if (state != EJS_STATE_STMT) {
+ goto error;
+ }
+ if (ejsLexGetToken(ep, state) != EJS_TOK_LPAREN) {
+ goto error;
+ }
+ /*
+ * Need to peek 2-3 tokens ahead and see if this is a
+ * for ([var] x in set)
+ * or
+ * for (init ; whileCond; incr)
+ */
+ initId = ejsLexGetToken(ep, EJS_STATE_EXPR);
+ if (initId == EJS_TOK_ID && strcmp(ep->token, "var") == 0) {
+ /* Simply eat var tokens */
+ initId = ejsLexGetToken(ep, EJS_STATE_EXPR);
+ }
+ initToken = mprStrdup(ep->token);
+
+ tid = ejsLexGetToken(ep, EJS_STATE_EXPR);
+
+ ejsLexPutbackToken(ep, tid, ep->token);
+ ejsLexPutbackToken(ep, initId, initToken);
+ mprFree(initToken);
+
+ if (tid == EJS_TOK_IN) {
+ if ((state = parseForIn(ep, state, flags)) < 0) {
+ goto error;
+ }
+ } else {
+ if ((state = parseFor(ep, state, flags)) < 0) {
+ goto error;
+ }
+ }
+ done++;
+ break;
+
+ case EJS_TOK_VAR:
+ if (ejsParse(ep, EJS_STATE_DEC_LIST, flags)
+ != EJS_STATE_DEC_LIST_DONE) {
+ goto error;
+ }
+ done++;
+ break;
+
+ case EJS_TOK_COMMA:
+ ejsLexPutbackToken(ep, tid, ep->token);
+ done++;
+ break;
+
+ case EJS_TOK_LPAREN:
+ if (state == EJS_STATE_EXPR) {
+ if (ejsParse(ep, EJS_STATE_RELEXP, flags)
+ != EJS_STATE_RELEXP_DONE) {
+ goto error;
+ }
+ if (ejsLexGetToken(ep, state) != EJS_TOK_RPAREN) {
+ goto error;
+ }
+ }
+ done++;
+ break;
+
+ case EJS_TOK_RPAREN:
+ ejsLexPutbackToken(ep, tid, ep->token);
+ done++;
+ break;
+
+ case EJS_TOK_LBRACE:
+ /*
+ * This handles any code in braces except "if () {} else {}"
+ */
+ if (state != EJS_STATE_STMT) {
+ goto error;
+ }
+
+ /*
+ * Parse will return EJS_STATE_STMT_BLOCK_DONE when the RBRACE
+ * is seen.
+ */
+ do {
+ state = ejsParse(ep, EJS_STATE_STMT, flags);
+ } while (state == EJS_STATE_STMT_DONE);
+
+ if (state != EJS_STATE_RET) {
+ if (ejsLexGetToken(ep, state) != EJS_TOK_RBRACE) {
+ goto error;
+ }
+ state = EJS_STATE_STMT_DONE;
+ }
+ done++;
+ break;
+
+ case EJS_TOK_RBRACE:
+ if (state == EJS_STATE_STMT) {
+ ejsLexPutbackToken(ep, tid, ep->token);
+ state = EJS_STATE_STMT_BLOCK_DONE;
+ done++;
+ break;
+ }
+ goto error;
+
+ case EJS_TOK_RETURN:
+ if (ejsParse(ep, EJS_STATE_RELEXP, flags)
+ != EJS_STATE_RELEXP_DONE) {
+ goto error;
+ }
+ if (flags & EJS_FLAGS_EXE) {
+ while (ejsLexGetToken(ep, state) != EJS_TOK_EOF) {
+ ;
+ }
+ state = EJS_STATE_RET;
+ done++;
+ }
+ break;
+ }
+ }
+
+ if (expectSemi) {
+ tid = ejsLexGetToken(ep, state);
+ if (tid != EJS_TOK_SEMI && tid != EJS_TOK_NEWLINE &&
+ tid != EJS_TOK_EOF) {
+ goto error;
+ }
+
+ /*
+ * Skip newline after semi-colon
+ */
+ removeNewlines(ep, state);
+ }
+
+/*
+ * Free resources and return the correct status
+ */
+doneParse:
+ mprFree(id);
+ mprFree(fullName);
+
+ /*
+ * Advance the state
+ */
+ switch (state) {
+ case EJS_STATE_STMT:
+ return EJS_STATE_STMT_DONE;
+
+ case EJS_STATE_DEC:
+ return EJS_STATE_DEC_DONE;
+
+ case EJS_STATE_EXPR:
+ return EJS_STATE_EXPR_DONE;
+
+ case EJS_STATE_STMT_DONE:
+ case EJS_STATE_STMT_BLOCK_DONE:
+ case EJS_STATE_EOF:
+ case EJS_STATE_RET:
+ return state;
+
+ default:
+ return EJS_STATE_ERR;
+ }
+
+/*
+ * Common error exit
+ */
+error:
+ state = EJS_STATE_ERR;
+ goto doneParse;
+}
+
+/******************************************************************************/
+/*
+ * Parse function arguments
+ */
+
+static int parseArgs(Ejs *ep, int state, int flags)
+{
+ 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;
+ }
+
+ state = ejsParse(ep, EJS_STATE_RELEXP, flags);
+ if (state == EJS_STATE_EOF || state == EJS_STATE_ERR) {
+ return state;
+ }
+ if (state == EJS_STATE_RELEXP_DONE) {
+ if (flags & EJS_FLAGS_EXE) {
+ mprAssert(ep->proc->args);
+ mprAddToArray(ep->proc->args,
+ mprDupVar(&ep->result, MPR_SHALLOW_COPY));
+ }
+ }
+ /*
+ * 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) {
+ return EJS_STATE_ERR;
+ }
+ return EJS_STATE_ARG_LIST_DONE;
+}
+
+/******************************************************************************/
+/*
+ * Parse an assignment statement
+ */
+
+static int parseAssignment(Ejs *ep, int state, int flags, char *id,
+ char *fullName)
+{
+ MprVar *vp, *saveProperty, *saveObj;
+
+ if (id == 0) {
+ return -1;
+ }
+
+ saveObj = ep->currentObj;
+ saveProperty = ep->currentProperty;
+ if (ejsParse(ep, EJS_STATE_RELEXP, flags | EJS_FLAGS_ASSIGNMENT)
+ != EJS_STATE_RELEXP_DONE) {
+ return -1;
+ }
+ ep->currentObj = saveObj;
+ ep->currentProperty = saveProperty;
+
+ if (! (flags & EJS_FLAGS_EXE)) {
+ return state;
+ }
+
+ if (ep->currentProperty) {
+ /*
+ * Update the variable. Update the property name if not
+ * yet defined.
+ */
+ if (ep->currentProperty->name == 0 ||
+ ep->currentProperty->name[0] == '\0') {
+ mprSetVarName(ep->currentProperty, id);
+ }
+ if (mprWriteProperty(ep->currentProperty, &ep->result) < 0){
+ ejsError(ep, "Can't write to variable\n");
+ return -1;
+ }
+
+ } else {
+ /*
+ * Create the variable
+ */
+ if (ep->currentObj) {
+ if (ep->currentObj->type != MPR_TYPE_OBJECT) {
+ if (strcmp(ep->currentObj->name, "session") == 0) {
+ ejsError(ep, "Variable \"%s\" is not an array or object."
+ "If using ESP, you need useSession(); in your page.",
+ ep->currentObj->name);
+ } else {
+ ejsError(ep, "Variable \"%s\" is not an array or object",
+ ep->currentObj->name);
+ }
+ return -1;
+ }
+ vp = mprCreateProperty(ep->currentObj, id, &ep->result);
+
+ } else {
+ /*
+ * Standard says: "var x" means declare locally.
+ * "x = 2" means declare globally if x is undefined.
+ */
+ if (state == EJS_STATE_DEC) {
+ vp = mprCreateProperty(ep->local, id, &ep->result);
+ } else {
+ vp = mprCreateProperty(ep->global, id, &ep->result);
+ }
+ }
+#if BLD_DEBUG
+ mprSetVarFullName(vp, fullName);
+#endif
+ }
+ return state;
+}
+
+/******************************************************************************/
+/*
+ * Parse conditional expression (relational ops separated by ||, &&)
+ */
+
+static int parseCond(Ejs *ep, int state, int flags)
+{
+ MprVar lhs, rhs;
+ int tid, operator;
+
+ mprAssert(ep);
+
+ mprDestroyVar(&ep->result);
+ rhs = lhs = mprCreateUndefinedVar();
+ 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 != EJS_STATE_RELEXP_DONE) {
+ state = EJS_STATE_ERR;
+ break;
+ }
+
+ if (operator > 0) {
+ mprCopyVar(&rhs, &ep->result, MPR_SHALLOW_COPY);
+ if (evalCond(ep, &lhs, operator, &rhs) < 0) {
+ state = EJS_STATE_ERR;
+ break;
+ }
+ }
+ mprCopyVar(&lhs, &ep->result, MPR_SHALLOW_COPY);
+
+ tid = ejsLexGetToken(ep, state);
+ if (tid == EJS_TOK_LOGICAL) {
+ operator = (int) *ep->token;
+
+ } else if (tid == EJS_TOK_RPAREN || tid == EJS_TOK_SEMI) {
+ ejsLexPutbackToken(ep, tid, ep->token);
+ state = EJS_STATE_COND_DONE;
+ break;
+
+ } else {
+ ejsLexPutbackToken(ep, tid, ep->token);
+ }
+ tid = (state == EJS_STATE_RELEXP_DONE);
+
+ } while (state == EJS_STATE_RELEXP_DONE);
+
+ mprDestroyVar(&lhs);
+ mprDestroyVar(&rhs);
+ 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;
+ *
+ * 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) {
+ 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
+ */
+ tid = ejsLexGetToken(ep, state);
+ if (tid == EJS_TOK_SEMI) {
+ return EJS_STATE_DEC_LIST_DONE;
+ } else if (tid != EJS_TOK_COMMA) {
+ return EJS_STATE_ERR;
+ }
+ } while (tid == EJS_TOK_COMMA);
+
+ if (tid != EJS_TOK_SEMI) {
+ return EJS_STATE_ERR;
+ }
+ return EJS_STATE_DEC_LIST_DONE;
+}
+
+/******************************************************************************/
+/*
+ * Parse expression (leftHandSide operator rightHandSide)
+ */
+
+static int parseExpr(Ejs *ep, int state, int flags)
+{
+ MprVar lhs, rhs;
+ int rel, tid;
+
+ mprAssert(ep);
+
+ mprDestroyVar(&ep->result);
+ rhs = lhs = mprCreateUndefinedVar();
+ rel = 0;
+ tid = 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 (tid == EJS_TOK_LOGICAL) {
+ state = ejsParse(ep, EJS_STATE_RELEXP, flags);
+ if (state != EJS_STATE_RELEXP_DONE) {
+ state = EJS_STATE_ERR;
+ break;
+ }
+ } else {
+ tid = ejsLexGetToken(ep, state);
+ if (tid == EJS_TOK_EXPR && (int) *ep->token == EJS_EXPR_MINUS) {
+ lhs = mprCreateIntegerVar(0);
+ rel = (int) *ep->token;
+ } else {
+ ejsLexPutbackToken(ep, tid, ep->token);
+ }
+
+ state = ejsParse(ep, EJS_STATE_EXPR, flags);
+ if (state != EJS_STATE_EXPR_DONE) {
+ state = EJS_STATE_ERR;
+ break;
+ }
+ }
+
+ if (rel > 0) {
+ mprCopyVar(&rhs, &ep->result, MPR_SHALLOW_COPY);
+ if (tid == EJS_TOK_LOGICAL) {
+ if (evalCond(ep, &lhs, rel, &rhs) < 0) {
+ state = EJS_STATE_ERR;
+ break;
+ }
+ } else {
+ if (evalExpr(ep, &lhs, rel, &rhs) < 0) {
+ state = EJS_STATE_ERR;
+ break;
+ }
+ }
+ }
+ mprCopyVar(&lhs, &ep->result, MPR_SHALLOW_COPY);
+
+ if ((tid = ejsLexGetToken(ep, state)) == EJS_TOK_EXPR ||
+ tid == EJS_TOK_INC_DEC || tid == EJS_TOK_LOGICAL) {
+ rel = (int) *ep->token;
+
+ } else {
+ ejsLexPutbackToken(ep, tid, ep->token);
+ state = EJS_STATE_RELEXP_DONE;
+ }
+
+ } while (state == EJS_STATE_EXPR_DONE);
+
+ mprDestroyVar(&lhs);
+ mprDestroyVar(&rhs);
+
+ return state;
+}
+
+/******************************************************************************/
+/*
+ * Parse the "for ... in" statement. Format for the statement is:
+ *
+ * for (var in expr) {
+ * body;
+ * }
+ */
+
+static int parseForIn(Ejs *ep, int state, int flags)
+{
+ EjsInput endScript, bodyScript;
+ MprVar *iteratorVar, *setVar, *vp, v;
+ int forFlags, tid;
+
+ mprAssert(ep);
+
+ tid = ejsLexGetToken(ep, state);
+ if (tid != EJS_TOK_ID) {
+ return -1;
+ }
+ ejsLexPutbackToken(ep, tid, ep->token);
+
+ if (ejsParse(ep, EJS_STATE_EXPR, EJS_FLAGS_FOREACH | EJS_FLAGS_EXE)
+ != EJS_STATE_EXPR_DONE) {
+ return -1;
+ }
+ if (ep->currentProperty == 0) {
+ return -1;
+ }
+ iteratorVar = ep->currentProperty;
+
+ if (ejsLexGetToken(ep, state) != EJS_TOK_IN) {
+ return -1;
+ }
+
+ /*
+ * Get the set
+ */
+ tid = ejsLexGetToken(ep, state);
+ if (tid != EJS_TOK_ID) {
+ return -1;
+ }
+ ejsLexPutbackToken(ep, tid, ep->token);
+
+ if (ejsParse(ep, EJS_STATE_EXPR, flags) != EJS_STATE_EXPR_DONE) {
+ return -1;
+ }
+ if (ep->currentProperty == 0 && flags & EJS_FLAGS_EXE) {
+ return -1;
+ }
+ setVar = ep->currentProperty;
+
+ if (ejsLexGetToken(ep, state) != EJS_TOK_RPAREN) {
+ return -1;
+ }
+
+ /*
+ * Parse the body and remember the end of the body script
+ */
+ forFlags = flags & ~EJS_FLAGS_EXE;
+ ejsLexSaveInputState(ep, &bodyScript);
+ if (ejsParse(ep, EJS_STATE_STMT, forFlags) != EJS_STATE_STMT_DONE) {
+ ejsLexFreeInputState(ep, &bodyScript);
+ return -1;
+ }
+ ejsInitInputState(&endScript);
+ ejsLexSaveInputState(ep, &endScript);
+
+ /*
+ * Now actually do the for loop.
+ */
+ if (flags & EJS_FLAGS_EXE) {
+ if (setVar->type == MPR_TYPE_OBJECT) {
+ vp = mprGetFirstProperty(setVar, MPR_ENUM_DATA);
+ while (vp) {
+ if (strcmp(vp->name, "length") != 0) {
+ v = mprCreateStringVar(vp->name, 0);
+ if (mprWriteProperty(iteratorVar, &v) < 0) {
+ ejsError(ep, "Can't write to variable\n");
+ ejsLexFreeInputState(ep, &bodyScript);
+ ejsLexFreeInputState(ep, &endScript);
+ return -1;
+ }
+
+ ejsLexRestoreInputState(ep, &bodyScript);
+ switch (ejsParse(ep, EJS_STATE_STMT, flags)) {
+ case EJS_STATE_RET:
+ return EJS_STATE_RET;
+ case EJS_STATE_STMT_DONE:
+ break;
+ default:
+ ejsLexFreeInputState(ep, &endScript);
+ ejsLexFreeInputState(ep, &bodyScript);
+ return -1;
+ }
+ }
+ vp = mprGetNextProperty(setVar, vp, MPR_ENUM_DATA);
+ }
+ } else {
+ ejsError(ep, "Variable \"%s\" is not an array or object",
+ setVar->name);
+ ejsLexFreeInputState(ep, &endScript);
+ ejsLexFreeInputState(ep, &bodyScript);
+ return -1;
+ }
+ }
+ ejsLexRestoreInputState(ep, &endScript);
+
+ ejsLexFreeInputState(ep, &endScript);
+ ejsLexFreeInputState(ep, &bodyScript);
+
+ return state;
+}
+
+/******************************************************************************/
+/*
+ * Parse the for statement. Format for the expression is:
+ *
+ * for (initial; condition; incr) {
+ * body;
+ * }
+ */
+
+static int parseFor(Ejs *ep, int state, int flags)
+{
+ EjsInput condScript, endScript, bodyScript, incrScript;
+ int forFlags, cond;
+
+ ejsInitInputState(&endScript);
+ ejsInitInputState(&bodyScript);
+ ejsInitInputState(&incrScript);
+ ejsInitInputState(&condScript);
+
+ mprAssert(ep);
+
+ /*
+ * Evaluate the for loop initialization statement
+ */
+ if (ejsParse(ep, EJS_STATE_EXPR, flags) != EJS_STATE_EXPR_DONE) {
+ return -1;
+ }
+ if (ejsLexGetToken(ep, state) != EJS_TOK_SEMI) {
+ return -1;
+ }
+
+ /*
+ * 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 (ejsParse(ep, EJS_STATE_COND, flags) != EJS_STATE_COND_DONE) {
+ goto error;
+ }
+ cond = (ep->result.boolean != 0);
+
+ if (ejsLexGetToken(ep, state) != EJS_TOK_SEMI) {
+ goto error;
+ }
+
+ /*
+ * Don't execute the loop increment statement or the body
+ * first time.
+ */
+ forFlags = flags & ~EJS_FLAGS_EXE;
+ ejsLexSaveInputState(ep, &incrScript);
+ if (ejsParse(ep, EJS_STATE_EXPR, forFlags) != EJS_STATE_EXPR_DONE) {
+ goto error;
+ }
+ if (ejsLexGetToken(ep, state) != EJS_TOK_RPAREN) {
+ goto error;
+ }
+
+ /*
+ * Parse the body and remember the end of the body script
+ */
+ ejsLexSaveInputState(ep, &bodyScript);
+ if (ejsParse(ep, EJS_STATE_STMT, forFlags) != EJS_STATE_STMT_DONE) {
+ goto error;
+ }
+ 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);
+
+ switch (ejsParse(ep, EJS_STATE_STMT, flags)) {
+ case EJS_STATE_RET:
+ return EJS_STATE_RET;
+ case EJS_STATE_STMT_DONE:
+ break;
+ default:
+ goto error;
+ }
+ /*
+ * Evaluate the increment script
+ */
+ ejsLexRestoreInputState(ep, &incrScript);
+ if (ejsParse(ep, EJS_STATE_EXPR, flags) != EJS_STATE_EXPR_DONE){
+ goto error;
+ }
+ /*
+ * Evaluate the condition
+ */
+ ejsLexRestoreInputState(ep, &condScript);
+ if (ejsParse(ep, EJS_STATE_COND, flags) != EJS_STATE_COND_DONE) {
+ goto error;
+ }
+ mprAssert(ep->result.type == MPR_TYPE_BOOL);
+ cond = (ep->result.boolean != 0);
+ }
+
+ ejsLexRestoreInputState(ep, &endScript);
+
+done:
+ ejsLexFreeInputState(ep, &condScript);
+ ejsLexFreeInputState(ep, &incrScript);
+ ejsLexFreeInputState(ep, &endScript);
+ ejsLexFreeInputState(ep, &bodyScript);
+ return state;
+
+error:
+ state = EJS_STATE_ERR;
+ goto done;
+}
+
+/******************************************************************************/
+/*
+ * Parse a function declaration
+ */
+
+static int parseFunctionDec(Ejs *ep, int state, int flags)
+{
+ EjsInput endScript, bodyScript;
+ MprVar v, *currentObj, *vp;
+ char *procName;
+ int len, tid, bodyFlags;
+
+ mprAssert(ep);
+ mprAssert(ejsPtr(ep->eid));
+
+ /*
+ * function <name>(arg, arg, arg) { body };
+ * function name(arg, arg, arg) { body };
+ */
+
+ tid = ejsLexGetToken(ep, state);
+ if (tid == EJS_TOK_ID) {
+ procName = mprStrdup(ep->token);
+ tid = ejsLexGetToken(ep, state);
+ } else {
+ procName = 0;
+ }
+ if (tid != EJS_TOK_LPAREN) {
+ mprFree(procName);
+ return EJS_STATE_ERR;
+ }
+
+ /*
+ * Hand craft the function value structure.
+ */
+ v = mprCreateFunctionVar(0, 0, 0);
+ tid = ejsLexGetToken(ep, state);
+ while (tid == EJS_TOK_ID) {
+ mprAddToArray(v.function.args, mprStrdup(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);
+ mprDestroyVar(&v);
+ 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);
+ mprDestroyVar(&v);
+ return EJS_STATE_ERR;
+ }
+
+ /*
+ * Register the function name early to allow for recursive
+ * function calls (see note in ECMA standard, page 71)
+ */
+ if (!(flags & EJS_FLAGS_ASSIGNMENT)) {
+ currentObj = ejsFindObj(ep, 0, procName, flags);
+ vp = mprSetProperty(currentObj, procName, &v);
+ }
+
+ /*
+ * Parse the function body. Turn execute off.
+ */
+ bodyFlags = flags & ~EJS_FLAGS_EXE;
+ ejsLexSaveInputState(ep, &bodyScript);
+
+ do {
+ state = ejsParse(ep, EJS_STATE_STMT, bodyFlags);
+ } while (state == EJS_STATE_STMT_DONE);
+
+ tid = ejsLexGetToken(ep, state);
+ if (state != EJS_STATE_STMT_BLOCK_DONE || tid != EJS_TOK_RBRACE) {
+ mprFree(procName);
+ mprDestroyVar(&v);
+ ejsLexFreeInputState(ep, &bodyScript);
+ return EJS_STATE_ERR;
+ }
+ ejsLexSaveInputState(ep, &endScript);
+
+ /*
+ * Save the function body between the starting and ending parse positions.
+ * Overwrite the trailing '}' with a null.
+ */
+ len = endScript.scriptServp - bodyScript.scriptServp;
+ v.function.body = mprMalloc(len + 1);
+ memcpy(v.function.body, bodyScript.scriptServp, len);
+
+ if (len <= 0) {
+ v.function.body[0] = '\0';
+ } else {
+ v.function.body[len - 1] = '\0';
+ }
+ ejsLexFreeInputState(ep, &bodyScript);
+ ejsLexFreeInputState(ep, &endScript);
+
+ /*
+ * If we are in an assignment, don't register the function name, rather
+ * return the function structure in the parser result.
+ */
+ if (flags & EJS_FLAGS_ASSIGNMENT) {
+ mprCopyVar(&ep->result, &v, MPR_SHALLOW_COPY);
+ } else {
+ currentObj = ejsFindObj(ep, 0, procName, flags);
+ vp = mprSetProperty(currentObj, procName, &v);
+ }
+
+ mprFree(procName);
+ mprDestroyVar(&v);
+
+ return EJS_STATE_STMT;
+}
+
+/******************************************************************************/
+/*
+ * Parse a function name and invoke the function
+ */
+
+static int parseFunction(Ejs *ep, int state, int flags, char *id)
+{
+ EjsProc proc, *saveProc;
+ MprVar *saveObj;
+
+ /*
+ * Must save any current ep->proc value for the current stack frame
+ * to allow for recursive function calls.
+ */
+ saveProc = (ep->proc) ? ep->proc: 0;
+
+ memset(&proc, 0, sizeof(EjsProc));
+ proc.procName = mprStrdup(id);
+ proc.fn = ep->currentProperty;
+ proc.args = mprCreateArray();
+ ep->proc = &proc;
+
+ mprDestroyVar(&ep->result);
+
+ saveObj = ep->currentObj;
+ if (ejsParse(ep, EJS_STATE_ARG_LIST, flags) != EJS_STATE_ARG_LIST_DONE) {
+ freeProc(&proc);
+ ep->proc = saveProc;
+ return -1;
+ }
+ ep->currentObj = saveObj;
+
+ /*
+ * Evaluate the function if required
+ */
+ if (flags & EJS_FLAGS_EXE) {
+ if (evalFunction(ep, ep->currentObj, flags) < 0) {
+ freeProc(&proc);
+ ep->proc = saveProc;
+ return -1;
+ }
+ }
+
+ freeProc(&proc);
+ ep->proc = saveProc;
+
+ if (ejsLexGetToken(ep, state) != EJS_TOK_RPAREN) {
+ return -1;
+ }
+ return state;
+}
+
+/******************************************************************************/
+/*
+ * 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, char **fullName,
+ int *fullNameLen, int *done)
+{
+ int tid;
+
+ mprFree(*id);
+ *id = mprStrdup(ep->token);
+#if BLD_DEBUG
+ *fullNameLen = mprReallocStrcat(fullName, MPR_MAX_VAR, *fullNameLen,
+ 0, *id, NULL);
+#endif
+ if (ep->currentObj == 0) {
+ ep->currentObj = ejsFindObj(ep, state, *id, flags);
+ }
+
+ /*
+ * Find the referenced variable and store it in currentProperty.
+ */
+ ep->currentProperty = ejsFindProperty(ep, state, ep->currentObj,
+ *id, flags);
+ updateResult(ep, state, flags, ep->currentProperty);
+
+#if BLD_DEBUG
+ if (ep->currentProperty && (ep->currentProperty->name == 0 ||
+ ep->currentProperty->name[0] == '\0')) {
+ mprSetVarName(ep->currentProperty, *id);
+ }
+#endif
+
+ tid = ejsLexGetToken(ep, state);
+ if (tid == EJS_TOK_LPAREN) {
+ if (ep->currentProperty == 0 && (flags & EJS_FLAGS_EXE)) {
+ ejsError(ep, "Function name not defined \"%s\"\n", *id);
+ return -1;
+ }
+ ejsLexPutbackToken(ep, EJS_TOK_FUNCTION_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;
+ }
+
+ /*
+ * 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, "Variable already defined \"%s\"\n", *id);
+ return -1;
+ }
+#endif
+ /*
+ * Create or overwrite if it already exists
+ */
+ mprSetPropertyValue(ep->currentObj, *id,
+ mprCreateUndefinedVar());
+ ep->currentProperty = 0;
+ mprDestroyVar(&ep->result);
+
+ } else if (flags & EJS_FLAGS_FOREACH) {
+ if (ep->currentProperty == 0) {
+ ep->currentProperty =
+ mprCreatePropertyValue(ep->currentObj, *id,
+ mprCreateUndefinedVar());
+ }
+
+ } else {
+ if (ep->currentProperty == 0) {
+ if (ep->currentObj == ep->global ||
+ ep->currentObj == ep->local) {
+ ejsError(ep, "Undefined variable \"%s\"\n", *id);
+ return -1;
+ }
+ ep->currentProperty = mprCreatePropertyValue(ep->currentObj,
+ *id, mprCreateUndefinedVar());
+ }
+ }
+ }
+ ejsLexPutbackToken(ep, tid, ep->token);
+ if (tid == EJS_TOK_RBRACKET || tid == EJS_TOK_COMMA ||
+ tid == EJS_TOK_IN) {
+ *done = 1;
+ }
+ return state;
+}
+
+/******************************************************************************/
+/*
+ * Parse an "if" statement
+ */
+
+static int parseIf(Ejs *ep, int state, int flags, int *done)
+{
+ bool ifResult;
+ int thenFlags, elseFlags, tid;
+
+ if (state != EJS_STATE_STMT) {
+ return -1;
+ }
+ if (ejsLexGetToken(ep, state) != EJS_TOK_LPAREN) {
+ return -1;
+ }
+
+ /*
+ * Evaluate the entire condition list "(condition)"
+ */
+ if (ejsParse(ep, EJS_STATE_COND, flags) != EJS_STATE_COND_DONE) {
+ return -1;
+ }
+ if (ejsLexGetToken(ep, state) != EJS_TOK_RPAREN) {
+ return -1;
+ }
+
+ /*
+ * This is the "then" case. We need to always parse both cases and
+ * execute only the relevant case.
+ */
+ ifResult = mprVarToBool(&ep->result);
+ if (ifResult) {
+ thenFlags = flags;
+ elseFlags = flags & ~EJS_FLAGS_EXE;
+ } else {
+ thenFlags = flags & ~EJS_FLAGS_EXE;
+ elseFlags = flags;
+ }
+
+ /*
+ * Process the "then" case.
+ */
+ switch (ejsParse(ep, EJS_STATE_STMT, thenFlags)) {
+ case EJS_STATE_RET:
+ state = EJS_STATE_RET;
+ return state;
+ case EJS_STATE_STMT_DONE:
+ break;
+ default:
+ return -1;
+ }
+
+ /*
+ * Check to see if there is an "else" case
+ */
+ removeNewlines(ep, state);
+ tid = ejsLexGetToken(ep, state);
+ if (tid != EJS_TOK_ELSE) {
+ ejsLexPutbackToken(ep, tid, ep->token);
+ *done = 1;
+ return state;
+ }
+
+ /*
+ * Process the "else" case.
+ */
+ switch (ejsParse(ep, EJS_STATE_STMT, elseFlags)) {
+ case EJS_STATE_RET:
+ state = EJS_STATE_RET;
+ return state;
+ case EJS_STATE_STMT_DONE:
+ break;
+ default:
+ return -1;
+ }
+ *done = 1;
+ return state;
+}
+
+/******************************************************************************/
+/*
+ * Parse an "++" or "--" statement
+ */
+
+static int parseInc(Ejs *ep, int state, int flags)
+{
+ MprVar one;
+
+ if (! (flags & EJS_FLAGS_EXE)) {
+ return state;
+ }
+
+ if (ep->currentProperty == 0) {
+ ejsError(ep, "Undefined variable \"%s\"\n", ep->token);
+ return -1;
+ }
+ one = mprCreateIntegerVar(1);
+ if (evalExpr(ep, ep->currentProperty, (int) *ep->token,
+ &one) < 0) {
+ return -1;
+ }
+ if (mprWriteProperty(ep->currentProperty, &ep->result) < 0) {
+ ejsError(ep, "Can't write to variable\n");
+ return -1;
+ }
+ return state;
+}
+
+/******************************************************************************/
+/*
+ * Evaluate a condition. Implements &&, ||, !. Returns with a boolean result
+ * in ep->result. Returns -1 on errors, zero if successful.
+ */
+
+static int evalCond(Ejs *ep, MprVar *lhs, int rel, MprVar *rhs)
+{
+ bool l, r, lval;
+
+ mprAssert(rel > 0);
+
+ l = mprVarToBool(lhs);
+ r = mprVarToBool(rhs);
+
+ switch (rel) {
+ case EJS_COND_AND:
+ lval = l && r;
+ break;
+ case EJS_COND_OR:
+ lval = l || r;
+ break;
+ default:
+ ejsError(ep, "Bad operator %d", rel);
+ return -1;
+ }
+
+ mprCopyVarValue(&ep->result, mprCreateBoolVar(lval), 0);
+ return 0;
+}
+
+
+/*
+ return true if this string is a valid number
+*/
+static int string_is_number(const char *s)
+{
+ char *endptr = NULL;
+ if (s == NULL || *s == 0) {
+ return 0;
+ }
+ strtod(s, &endptr);
+ 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, MprVar *lhs, int rel, MprVar *rhs)
+{
+ char *str;
+ MprNum lval, num;
+ 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.
+ *
+ * First convert objects to comparable types. The "===" operator will
+ * test the sameness of object references. Here, we coerce to comparable
+ * types first.
+ */
+ if (lhs->type == MPR_TYPE_OBJECT) {
+ if (ejsRunFunction(ep->eid, lhs, "toValue", 0) == 0) {
+ mprCopyVar(lhs, &ep->result, MPR_SHALLOW_COPY);
+ } else {
+ if (ejsRunFunction(ep->eid, lhs, "toString", 0) == 0) {
+ mprCopyVar(lhs, &ep->result, MPR_SHALLOW_COPY);
+ }
+ }
+ /* Nothing more can be done */
+ }
+
+ if (rhs->type == MPR_TYPE_OBJECT) {
+ if (ejsRunFunction(ep->eid, rhs, "toValue", 0) == 0) {
+ mprCopyVar(rhs, &ep->result, MPR_SHALLOW_COPY);
+ } else {
+ if (ejsRunFunction(ep->eid, rhs, "toString", 0) == 0) {
+ mprCopyVar(rhs, &ep->result, MPR_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 == MPR_TYPE_UNDEFINED || rhs->type == MPR_TYPE_UNDEFINED) {
+ return evalBoolExpr(ep,
+ lhs->type == MPR_TYPE_UNDEFINED,
+ rel,
+ rhs->type == MPR_TYPE_UNDEFINED);
+ }
+
+ if (lhs->type == MPR_TYPE_NULL || rhs->type == MPR_TYPE_NULL) {
+ return evalBoolExpr(ep,
+ lhs->type == MPR_TYPE_NULL,
+ rel,
+ rhs->type == MPR_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 == MPR_TYPE_BOOL && rhs->type == MPR_TYPE_BOOL &&
+ (rel != EJS_EXPR_EQ && rel != EJS_EXPR_NOTEQ &&
+ rel != EJS_EXPR_BOOL_COMP)) {
+ num = mprVarToNumber(lhs);
+ mprDestroyVar(lhs);
+ *lhs = mprCreateNumberVar(num);
+ }
+
+ /*
+ * 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 == MPR_TYPE_STRING) {
+ if (string_is_number(lhs->string)) {
+ num = mprVarToNumber(lhs);
+ lhs->allocatedVar = 0;
+ mprDestroyVar(lhs);
+ *lhs = mprCreateNumberVar(num);
+ /* Examine further below */
+
+ } else {
+ /*
+ * Convert the RHS to a string
+ */
+ mprVarToString(&str, MPR_MAX_STRING, 0, rhs);
+ rhs->allocatedVar = 0;
+ mprDestroyVar(rhs);
+ *rhs = mprCreateStringVar(str, 1);
+ mprFree(str);
+ }
+
+#if BLD_FEATURE_FLOATING_POINT
+ } else if (lhs->type == MPR_TYPE_FLOAT) {
+ /*
+ * Convert rhs to floating
+ */
+ double f = mprVarToFloat(rhs);
+ mprDestroyVar(rhs);
+ *rhs = mprCreateFloatVar(f);
+
+#endif
+#if BLD_FEATURE_INT64
+ } else if (lhs->type == MPR_TYPE_INT64) {
+ /*
+ * Convert the rhs to 64 bit
+ */
+ int64 n = mprVarToInteger64(rhs);
+ mprDestroyVar(rhs);
+ *rhs = mprCreateInteger64Var(n);
+#endif
+ } else if (lhs->type == MPR_TYPE_BOOL || lhs->type == MPR_TYPE_INT) {
+
+ if (rhs->type == MPR_TYPE_STRING) {
+ /*
+ * Convert to lhs to a string
+ */
+ mprVarToString(&str, MPR_MAX_STRING, 0, lhs);
+ mprDestroyVar(lhs);
+ *lhs = mprCreateStringVar(str, 1);
+ mprFree(str);
+
+#if BLD_FEATURE_FLOATING_POINT
+ } else if (rhs->type == MPR_TYPE_FLOAT) {
+ /*
+ * Convert lhs to floating
+ */
+ double f = mprVarToFloat(lhs);
+ mprDestroyVar(lhs);
+ *lhs = mprCreateFloatVar(f);
+#endif
+
+ } else {
+ /*
+ * Convert both operands to numbers
+ */
+ num = mprVarToNumber(lhs);
+ mprDestroyVar(lhs);
+ *lhs = mprCreateNumberVar(num);
+
+ num = mprVarToNumber(rhs);
+ mprDestroyVar(rhs);
+ *rhs = mprCreateNumberVar(num);
+ }
+ }
+ }
+
+ /*
+ * 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 == MPR_TYPE_UNDEFINED || lhs->type == MPR_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 = ! mprVarToBool(rhs);
+ break;
+ default:
+ lval = 0;
+ }
+ mprCopyVarValue(&ep->result, mprCreateBoolVar((bool) lval), 0);
+ return 0;
+ }
+
+ /*
+ * Types are the same here
+ */
+ switch (lhs->type) {
+ default:
+ case MPR_TYPE_UNDEFINED:
+ case MPR_TYPE_NULL:
+ /* Should be handled above */
+ mprAssert(0);
+ return 0;
+
+ case MPR_TYPE_STRING_CFUNCTION:
+ case MPR_TYPE_CFUNCTION:
+ case MPR_TYPE_FUNCTION:
+ case MPR_TYPE_OBJECT:
+ mprCopyVarValue(&ep->result, mprCreateBoolVar(0), 0);
+ return 0;
+
+ case MPR_TYPE_PTR:
+ rc = evalPtrExpr(ep, lhs->ptr, rel, rhs->ptr);
+ break;
+
+ case MPR_TYPE_BOOL:
+ rc = evalBoolExpr(ep, lhs->boolean, rel, rhs->boolean);
+ break;
+
+#if BLD_FEATURE_FLOATING_POINT
+ case MPR_TYPE_FLOAT:
+ rc = evalFloatExpr(ep, lhs->floating, rel, rhs->floating);
+ break;
+#endif
+
+ case MPR_TYPE_INT:
+ rc = evalNumericExpr(ep, (MprNum) lhs->integer, rel,
+ (MprNum) rhs->integer);
+ break;
+
+#if BLD_FEATURE_INT64
+ case MPR_TYPE_INT64:
+ rc = evalNumericExpr(ep, (MprNum) lhs->integer64, rel,
+ (MprNum) rhs->integer64);
+ break;
+#endif
+
+ case MPR_TYPE_STRING:
+ rc = evalStringExpr(ep, lhs, rel, rhs);
+ }
+ return rc;
+}
+
+/******************************************************************************/
+#if BLD_FEATURE_FLOATING_POINT
+/*
+ * Expressions with floating operands
+ */
+
+static int evalFloatExpr(Ejs *ep, double l, int rel, double r)
+{
+ double lval;
+ bool 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, "Bad operator %d", rel);
+ return -1;
+ }
+ mprCopyVarValue(&ep->result, mprCreateBoolVar(lval != 0), 0);
+
+ } else {
+ mprCopyVarValue(&ep->result, mprCreateFloatVar(lval), 0);
+ }
+ return 0;
+}
+
+#endif /* BLD_FEATURE_FLOATING_POINT */
+/******************************************************************************/
+/*
+ * Expressions with boolean operands
+ */
+
+static int evalBoolExpr(Ejs *ep, bool l, int rel, bool r)
+{
+ bool 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, "Bad operator %d", rel);
+ return -1;
+ }
+ mprCopyVarValue(&ep->result, mprCreateBoolVar(lval), 0);
+ return 0;
+}
+
+static int evalPtrExpr(Ejs *ep, void *l, int rel, void *r)
+{
+ bool 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 == NULL) ? 1 : 0;
+ break;
+ default:
+ ejsError(ep, "Bad operator %d", rel);
+ return -1;
+ }
+ mprCopyVarValue(&ep->result, mprCreateBoolVar(lval), 0);
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Expressions with numeric operands
+ */
+
+static int evalNumericExpr(Ejs *ep, MprNum l, int rel, MprNum r)
+{
+ MprNum lval;
+ bool 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, "Divide by zero");
+ return -1;
+ }
+ break;
+ case EJS_EXPR_MOD:
+ if (r != 0) {
+ lval = l % r;
+ } else {
+ ejsError(ep, "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, "Bad operator %d", rel);
+ return -1;
+ }
+ mprCopyVarValue(&ep->result, mprCreateBoolVar(lval != 0), 0);
+
+ } else {
+ mprCopyVarValue(&ep->result, mprCreateNumberVar(lval), 0);
+ }
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Expressions with string operands
+ */
+
+static int evalStringExpr(Ejs *ep, MprVar *lhs, int rel, MprVar *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.
+ */
+ mprDestroyVar(&ep->result);
+ appendValue(&ep->result, lhs);
+ appendValue(&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:
+ ejsError(ep, "Bad operator");
+ return -1;
+ }
+
+ mprCopyVarValue(&ep->result, mprCreateBoolVar(lval), 0);
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Evaluate a function. obj is set to the current object if a function is being
+ * run.
+ */
+
+static int evalFunction(Ejs *ep, MprVar *obj, int flags)
+{
+ EjsProc *proc;
+ MprVar arguments, callee, thisObject, *prototype, **argValues;
+ MprArray *formalArgs, *actualArgs;
+ char buf[16], **argNames, **argBuf;
+ int i, rc, fid;
+
+ mprAssert(ep);
+ mprAssert(ejsPtr(ep->eid));
+
+ rc = -1;
+ proc = ep->proc;
+ prototype = proc->fn;
+ actualArgs = proc->args;
+ argValues = (MprVar**) actualArgs->handles;
+
+ /*
+ * Create a new variable stack frame. ie. new local variables.
+ */
+ fid = ejsOpenBlock(ep->eid);
+
+ if (flags & EJS_FLAGS_NEW) {
+ /*
+ * Create a new bare object and pass it into the constructor as the
+ * "this" local variable.
+ */
+ thisObject = ejsCreateObj("this", EJS_OBJ_HASH_SIZE);
+ mprCreatePropertyValue(ep->local, "this", thisObject);
+
+ } else if (obj) {
+ mprCreateProperty(ep->local, "this", obj);
+ }
+
+ switch (prototype->type) {
+ default:
+ mprAssert(0);
+ break;
+
+ case MPR_TYPE_STRING_CFUNCTION:
+ if (actualArgs->used > 0) {
+ argBuf = mprMalloc((1+actualArgs->used) * sizeof(char*));
+ for (i = 0; i < actualArgs->used; i++) {
+ mprVarToString(&argBuf[i], MPR_MAX_STRING, 0, argValues[i]);
+ }
+ argBuf[i] = NULL;
+ } else {
+ argBuf = 0;
+ }
+
+ /*
+ * Call the function depending on the various handle flags
+ */
+ ep->thisPtr = prototype->cFunctionWithStrings.thisPtr;
+ if (prototype->flags & MPR_VAR_ALT_HANDLE) {
+ rc = ((EjsAltStringCFunction) prototype->cFunctionWithStrings.fn)
+ (ep->eid, ep->altHandle, actualArgs->used, argBuf);
+ } else if (prototype->flags & MPR_VAR_SCRIPT_HANDLE) {
+ rc = (prototype->cFunctionWithStrings.fn)(ep->eid,
+ actualArgs->used, argBuf);
+ } else {
+ rc = (prototype->cFunctionWithStrings.fn)(ep->primaryHandle,
+ actualArgs->used, argBuf);
+ }
+
+ if (actualArgs->used > 0) {
+ for (i = 0; i < actualArgs->used; i++) {
+ mprFree(argBuf[i]);
+ }
+ mprFree(argBuf);
+ }
+ ep->thisPtr = 0;
+ break;
+
+ case MPR_TYPE_CFUNCTION:
+ /*
+ * Call the function depending on the various handle flags
+ */
+ ep->thisPtr = prototype->cFunction.thisPtr;
+ if (prototype->flags & MPR_VAR_ALT_HANDLE) {
+ rc = ((EjsAltCFunction) prototype->cFunction.fn)
+ (ep->eid, ep->altHandle, actualArgs->used, argValues);
+ } else if (prototype->flags & MPR_VAR_SCRIPT_HANDLE) {
+ rc = (prototype->cFunction.fn)(ep->eid, actualArgs->used,
+ argValues);
+ } else {
+ rc = (prototype->cFunction.fn)(ep->primaryHandle,
+ actualArgs->used, argValues);
+ }
+ ep->thisPtr = 0;
+ break;
+
+ case MPR_TYPE_FUNCTION:
+
+ formalArgs = prototype->function.args;
+ argNames = (char**) formalArgs->handles;
+
+ if (formalArgs->used > actualArgs->used) {
+ ejsError(ep, "Bad number of args. Should be %d",
+ formalArgs->used);
+ return -1;
+ }
+
+ /*
+ * Create the arguments and callee variables
+ */
+ arguments = ejsCreateObj("arguments", EJS_SMALL_OBJ_HASH_SIZE);
+ callee = ejsCreateObj("callee", EJS_SMALL_OBJ_HASH_SIZE);
+
+ /*
+ * Overwrite the length property
+ */
+ mprCreatePropertyValue(&arguments, "length",
+ mprCreateIntegerVar(actualArgs->used));
+ mprCreatePropertyValue(&callee, "length",
+ mprCreateIntegerVar(formalArgs->used));
+
+ /*
+ * Define all the agruments to be set to the actual parameters
+ */
+ for (i = 0; i < formalArgs->used; i++) {
+ mprCreateProperty(ep->local, argNames[i], argValues[i]);
+ }
+ for (i = 0; i < actualArgs->used; i++) {
+ mprItoa(i, buf, sizeof(buf));
+ mprCreateProperty(&arguments, buf, argValues[i]);
+ }
+
+ mprCreateProperty(&arguments, "callee", &callee);
+ mprCreateProperty(ep->local, "arguments", &arguments);
+
+ /*
+ * Can destroy our variables here as they are now referenced via
+ * "local"
+ */
+ mprDestroyVar(&callee);
+ mprDestroyVar(&arguments);
+
+ /*
+ * Actually run the function
+ */
+ rc = ejsEvalScript(ep->eid, prototype->function.body, 0, 0);
+ break;
+ }
+
+ ejsCloseBlock(ep->eid, fid);
+
+ /*
+ * New statements return the newly created object as the result of the
+ * command
+ */
+ if (flags & EJS_FLAGS_NEW) {
+ mprDestroyVar(&ep->result);
+ /*
+ * Don't copy, we want to assign the actual object into result.
+ * (mprCopyVar would inc the refCount to 2).
+ */
+ ep->result = thisObject;
+ }
+ return rc;
+}
+
+/******************************************************************************/
+/*
+ * Run a function
+ */
+
+int ejsRunFunction(int eid, MprVar *obj, const char *functionName,
+ MprArray *args)
+{
+ EjsProc proc, *saveProc;
+ Ejs *ep;
+ int rc;
+
+ mprAssert(obj);
+ mprAssert(functionName && *functionName);
+
+ if ((ep = ejsPtr(eid)) == NULL) {
+ mprAssert(ep);
+ return MPR_ERR_NOT_FOUND;
+ }
+ saveProc = ep->proc;
+ ep->proc = &proc;
+
+ memset(&proc, 0, sizeof(EjsProc));
+ mprDestroyVar(&ep->result);
+
+ proc.fn = mprGetProperty(obj, functionName, 0);
+ if (proc.fn == 0 || proc.fn->type == MPR_TYPE_UNDEFINED) {
+ ep->proc = saveProc;
+ return MPR_ERR_NOT_FOUND;
+ }
+ proc.procName = mprStrdup(functionName);
+ if (args == 0) {
+ proc.args = mprCreateArray();
+ rc = evalFunction(ep, obj, 0);
+ } else {
+ proc.args = args;
+ rc = evalFunction(ep, obj, 0);
+ proc.args = 0;
+ }
+
+ freeProc(&proc);
+ ep->proc = saveProc;
+
+ return rc;
+}
+
+/******************************************************************************/
+/*
+ * Find which object contains the property given the current context.
+ * Only used for top level properties.
+ */
+
+MprVar *ejsFindObj(Ejs *ep, int state, const char *property, int flags)
+{
+ MprVar *obj;
+
+ mprAssert(ep);
+ mprAssert(property && *property);
+
+ 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 look global */
+ if (mprGetProperty(ep->local, property, 0)) {
+ obj = ep->local;
+ } else {
+ obj = ep->global;
+ }
+ }
+ 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 function
+ * names only. Returns the property or NULL.
+ */
+
+MprVar *ejsFindProperty(Ejs *ep, int state, MprVar *obj, char *property,
+ int flags)
+{
+ MprVar *vp;
+
+ mprAssert(ep);
+ if (flags & EJS_FLAGS_EXE) {
+ mprAssert(property && *property);
+ }
+
+ if (obj != 0) {
+#if FUTURE && MB
+ op = obj;
+ do {
+ vp = mprGetProperty(op, property, 0);
+ if (vp != 0) {
+ if (op != obj && mprVarIsFunction(vp->type)) {
+ }
+ break;
+ }
+ op = op->baseObj;
+ } while (op);
+#endif
+ vp = mprGetProperty(obj, property, 0);
+
+ } else {
+ if (state == EJS_STATE_DEC) {
+ vp = mprGetProperty(ep->local, property, 0);
+
+ } else {
+ /* Look local first, then global */
+ vp = mprGetProperty(ep->local, property, 0);
+ if (vp == NULL) {
+ vp = mprGetProperty(ep->global, property, 0);
+ }
+ }
+ }
+ return vp;
+}
+
+/******************************************************************************/
+/*
+ * Update result
+ */
+
+static void updateResult(Ejs *ep, int state, int flags, MprVar *vp)
+{
+ if (flags & EJS_FLAGS_EXE && state != EJS_STATE_DEC) {
+ mprDestroyVar(&ep->result);
+ if (vp) {
+ mprCopyProperty(&ep->result, vp, MPR_SHALLOW_COPY);
+ }
+ }
+}
+
+/******************************************************************************/
+/*
+ * Append to the pointer value
+ */
+
+static void appendValue(MprVar *dest, MprVar *src)
+{
+ char *value, *oldBuf, *buf;
+ int len, oldLen;
+
+ mprAssert(dest);
+
+ mprVarToString(&value, MPR_MAX_STRING, 0, src);
+
+ if (mprVarIsValid(dest)) {
+ len = strlen(value);
+ oldBuf = dest->string;
+ oldLen = strlen(oldBuf);
+ buf = mprRealloc(oldBuf, (len + oldLen + 1) * sizeof(char));
+ dest->string = buf;
+ strcpy(&buf[oldLen], value);
+
+ } else {
+ *dest = mprCreateStringVar(value, 1);
+ }
+ mprFree(value);
+}
+
+/******************************************************************************/
+/*
+ * Exit with status
+ */
+
+void ejsSetExitStatus(int eid, int status)
+{
+ Ejs *ep;
+
+ if ((ep = ejsPtr(eid)) == NULL) {
+ mprAssert(ep);
+ return;
+ }
+ ep->exitStatus = status;
+ ep->flags |= EJS_FLAGS_EXIT;
+}
+
+/******************************************************************************/
+/*
+ * Free an argument list
+ */
+
+static void freeProc(EjsProc *proc)
+{
+ MprVar **argValues;
+ int i;
+
+ if (proc->args) {
+ argValues = (MprVar**) proc->args->handles;
+
+ for (i = 0; i < proc->args->max; i++) {
+ if (argValues[i]) {
+ mprDestroyVar(argValues[i]);
+ mprFree(argValues[i]);
+ mprRemoveFromArray(proc->args, i);
+ }
+ }
+
+ mprDestroyArray(proc->args);
+ }
+
+ if (proc->procName) {
+ mprFree(proc->procName);
+ proc->procName = NULL;
+ }
+}
+
+/******************************************************************************/
+/*
+ * This function 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);
+}
+
+/******************************************************************************/
+
+#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/ejsProcs.c b/source4/lib/appweb/ejs/ejsProcs.c
new file mode 100644
index 0000000000..43fff4a40b
--- /dev/null
+++ b/source4/lib/appweb/ejs/ejsProcs.c
@@ -0,0 +1,704 @@
+/*
+ * @file ejsProc.c
+ * @brief EJS support functions
+ */
+/********************************* Copyright **********************************/
+/*
+ * @copy default.g
+ *
+ * Copyright (c) Mbedthis Software LLC, 2003-2005. 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 "ejsInternal.h"
+
+#if BLD_FEATURE_EJS
+
+/****************************** Forward Declarations **************************/
+/*
+ * Object constructors
+ */
+static int objectConsProc(EjsHandle eid, int argc, MprVar **argv);
+static int arrayConsProc(EjsHandle eid, int argc, MprVar **argv);
+static int booleanConsProc(EjsHandle eid, int argc, MprVar **agv);
+static int numberConsProc(EjsHandle eid, int argc, MprVar **argv);
+static int stringConsProc(EjsHandle eid, int argc, MprVar **argv);
+
+/*
+ * Core functions
+ */
+static int toStringProc(EjsHandle eid, int argc, MprVar **argv);
+static int valueOfProc(EjsHandle eid, int argc, MprVar **argv);
+
+/*
+ * Triggers
+ */
+static MprVarTriggerStatus lengthTrigger(MprVarTriggerOp op,
+ MprProperties *parentProperties, MprVar *prop, MprVar *newValue,
+ int copyRef);
+
+/******************************************************************************/
+/*
+ * Routine to create the base common to all object types
+ */
+
+MprVar ejsCreateObj(const char *name, int hashSize)
+{
+ MprVar o;
+
+ o = mprCreateObjVar(name, hashSize);
+ if (o.type == MPR_TYPE_UNDEFINED) {
+ mprAssert(0);
+ return o;
+ }
+
+ mprCreatePropertyValue(&o, "toString",
+ mprCreateCFunctionVar(toStringProc, 0, MPR_VAR_SCRIPT_HANDLE));
+ mprCreatePropertyValue(&o, "valueOf",
+ mprCreateCFunctionVar(valueOfProc, 0, MPR_VAR_SCRIPT_HANDLE));
+ return o;
+}
+
+/******************************************************************************/
+/*
+ * Routine to destroy a variable
+ */
+
+bool ejsDestroyVar(MprVar *obj)
+{
+ return mprDestroyVar(obj);
+}
+
+/******************************************************************************/
+/*
+ * Routine to create the base array type
+ */
+
+MprVar ejsCreateArray(const char *name, int size)
+{
+ MprVar obj, *lp, undef;
+ char idx[16];
+ int i;
+
+ /* Sanity limit for size of hash table */
+
+ obj = ejsCreateObj(name, max(size, 503));
+ if (obj.type == MPR_TYPE_UNDEFINED) {
+ mprAssert(0);
+ return obj;
+ }
+
+ undef = mprCreateUndefinedVar();
+ for (i = 0; i < size; i++) {
+ mprItoa(i, idx, sizeof(idx));
+ mprCreateProperty(&obj, idx, &undef);
+ }
+
+ lp = mprCreatePropertyValue(&obj, "length", mprCreateIntegerVar(size));
+ mprAssert(lp);
+
+ mprSetVarReadonly(lp, 1);
+ mprAddVarTrigger(lp, lengthTrigger);
+
+ return obj;
+}
+
+/******************************************************************************/
+/******************************** Constructors ********************************/
+/******************************************************************************/
+/*
+ * Object constructor. Nothing really done here. For future expansion.
+ */
+
+static int objectConsProc(EjsHandle eid, int argc, MprVar **argv)
+{
+#if XX_UNUSED_XX
+ MprVar *obj;
+ Ejs *ep;
+
+ if((ep = ejsPtr(eid)) == NULL) {
+ return -1;
+ }
+
+ obj = mprGetProperty(ep->local, "this", 0);
+ mprAssert(obj);
+#endif
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Array constructor
+ */
+
+static int arrayConsProc(EjsHandle eid, int argc, MprVar **argv)
+{
+ MprVar *obj, *lp, undef;
+ Ejs *ep;
+ char idx[16];
+ int i, max;
+
+ objectConsProc(eid, argc, argv);
+
+ if((ep = ejsPtr(eid)) == NULL) {
+ return -1;
+ }
+ obj = mprGetProperty(ep->local, "this", 0);
+ mprAssert(obj);
+
+
+ if (argc == 1 && mprVarIsNumber(argv[0]->type)) {
+ /*
+ * x = new Array(size);
+ */
+ undef = mprCreateUndefinedVar();
+ max = (int) mprVarToInteger(argv[0]);
+ for (i = 0; i < max; i++) {
+ mprItoa(i, idx, sizeof(idx));
+ mprCreateProperty(obj, idx, &undef);
+ }
+ } else {
+ /*
+ * x = new Array(element0, element1, ..., elementN):
+ */
+ max = argc;
+ for (i = 0; i < max; i++) {
+ mprItoa(i, idx, sizeof(idx));
+ mprCreateProperty(obj, idx, argv[i]);
+ }
+ }
+
+ lp = mprCreatePropertyValue(obj, "length", mprCreateIntegerVar(max));
+ mprAssert(lp);
+
+ mprSetVarReadonly(lp, 1);
+ mprAddVarTrigger(lp, lengthTrigger);
+
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Boolean constructor
+ */
+
+static int booleanConsProc(EjsHandle eid, int argc, MprVar **argv)
+{
+ objectConsProc(eid, argc, argv);
+ return 0;
+}
+
+/******************************************************************************/
+#if FUTURE
+/*
+ * Date constructor
+ */
+
+static int dateConsProc(EjsHandle eid, int argc, MprVar **argv)
+{
+ objectConsProc(eid, argc, argv);
+ return 0;
+}
+
+#endif
+/******************************************************************************/
+/*
+ * Number constructor
+ */
+
+static int numberConsProc(EjsHandle eid, int argc, MprVar **argv)
+{
+ objectConsProc(eid, argc, argv);
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * String constructor
+ */
+
+static int stringConsProc(EjsHandle eid, int argc, MprVar **argv)
+{
+ objectConsProc(eid, argc, argv);
+ return 0;
+}
+
+/******************************************************************************/
+/********************************** Functions *********************************/
+/******************************************************************************/
+
+static int toStringProc(EjsHandle eid, int argc, MprVar **argv)
+{
+ MprVar *obj;
+ Ejs *ep;
+ char *buf;
+ int radix;
+
+ if (argc == 0) {
+ radix = 10;
+
+ } else if (argc == 1) {
+ radix = (int) mprVarToInteger(argv[0]);
+
+ } else {
+ mprAssert(0);
+ return -1;
+ }
+
+ if((ep = ejsPtr(eid)) == NULL) {
+ return -1;
+ }
+
+ obj = mprGetProperty(ep->local, "this", 0);
+ mprAssert(obj);
+
+ mprVarToString(&buf, MPR_MAX_STRING, 0, obj);
+ mprCopyVarValue(&ep->result, mprCreateStringVar(buf, 0), MPR_SHALLOW_COPY);
+ mprFree(buf);
+
+ return 0;
+}
+
+/******************************************************************************/
+
+static int valueOfProc(EjsHandle eid, int argc, MprVar **argv)
+{
+ MprVar *obj;
+ Ejs *ep;
+
+ if (argc != 0) {
+ mprAssert(0);
+ return -1;
+ }
+
+ if((ep = ejsPtr(eid)) == NULL) {
+ return -1;
+ }
+
+ obj = mprGetProperty(ep->local, "this", 0);
+ mprAssert(obj);
+
+ switch (obj->type) {
+ default:
+ case MPR_TYPE_UNDEFINED:
+ case MPR_TYPE_NULL:
+ case MPR_TYPE_CFUNCTION:
+ case MPR_TYPE_OBJECT:
+ case MPR_TYPE_FUNCTION:
+ case MPR_TYPE_STRING_CFUNCTION:
+ case MPR_TYPE_PTR:
+ mprCopyVar(&ep->result, obj, MPR_SHALLOW_COPY);
+ break;
+
+ case MPR_TYPE_STRING:
+ mprCopyVarValue(&ep->result, mprCreateIntegerVar(atoi(obj->string)), 0);
+ break;
+
+ case MPR_TYPE_BOOL:
+ case MPR_TYPE_INT:
+#if BLD_FEATURE_INT64
+ case MPR_TYPE_INT64:
+#endif
+#if BLD_FEATURE_FLOATING_POINT
+ case MPR_TYPE_FLOAT:
+#endif
+ mprCopyVar(&ep->result, obj, 0);
+ break;
+ }
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Var access trigger on the Array.length property. Return the count of
+ * enumerable properties (don't count functions).
+ */
+
+static MprVarTriggerStatus lengthTrigger(MprVarTriggerOp op,
+ MprProperties *parentProperties, MprVar *prop, MprVar *newValue,
+ int copyRef)
+{
+ switch (op) {
+ case MPR_VAR_READ:
+ /*
+ * Subtract one for the length property
+ * FUTURE -- need an API to access parentProperties
+ * FUTURE -- contradiction to be read-only yet allow USE_NEW_VALUE.
+ * API needs finer control.
+ */
+ *newValue = mprCreateIntegerVar(parentProperties->numDataItems - 1);
+ return MPR_TRIGGER_USE_NEW_VALUE;
+
+ case MPR_VAR_WRITE:
+ return MPR_TRIGGER_ABORT;
+
+ case MPR_VAR_CREATE_PROPERTY:
+ case MPR_VAR_DELETE_PROPERTY:
+ case MPR_VAR_DELETE:
+ default:
+ break;
+ }
+ return MPR_TRIGGER_PROCEED;
+}
+
+/******************************************************************************/
+/**************************** Extension Functions *****************************/
+/******************************************************************************/
+/*
+ * Assert
+ */
+
+static int assertProc(EjsHandle eid, int argc, MprVar **argv)
+{
+ bool b;
+
+ if (argc < 1) {
+ ejsSetErrorMsg(eid, "usage: assert(condition)\n");
+ return -1;
+ }
+ b = mprVarToBool(argv[0]);
+ if (b == 0) {
+ ejsSetErrorMsg(eid, "Assertion failure\n");
+ return -1;
+ }
+ ejsSetReturnValue(eid, mprCreateBoolVar(b));
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Exit
+ */
+
+static int exitProc(EjsHandle eid, int argc, MprVar **argv)
+{
+ int status;
+
+ if (argc < 1) {
+ ejsSetErrorMsg(eid, "usage: exit(status)\n");
+ return -1;
+ }
+ status = (int) mprVarToInteger(argv[0]);
+ ejsSetExitStatus(eid, status);
+
+ ejsSetReturnValue(eid, mprCreateStringVar("", 0));
+ return 0;
+}
+
+/******************************************************************************/
+
+static void printVar(MprVar *vp, int recurseCount, int indent)
+{
+ MprVar *np;
+ char *buf;
+ int i;
+
+ if (recurseCount > 5) {
+ write(1, "Skipping - recursion too deep\n", 29);
+ return;
+ }
+
+ for (i = 0; i < indent; i++) {
+ write(1, " ", 2);
+ }
+
+ if (vp->type == MPR_TYPE_OBJECT) {
+ if (vp->name) {
+ write(1, vp->name, strlen(vp->name));
+ } else {
+ write(1, "unknown", 7);
+ }
+ write(1, ": {\n", 4);
+ np = mprGetFirstProperty(vp, MPR_ENUM_DATA);
+ while (np) {
+ if (strcmp(np->name, "local") == 0 ||
+ strcmp(np->name, "global") == 0 ||
+ strcmp(np->name, "this") == 0) {
+ np = mprGetNextProperty(vp, np, MPR_ENUM_DATA);
+ continue;
+ }
+ printVar(np, recurseCount + 1, indent + 1);
+ np = mprGetNextProperty(vp, np, MPR_ENUM_DATA);
+ if (np) {
+ write(1, ",\n", 2);
+ }
+ }
+ write(1, "\n", 1);
+ for (i = 0; i < indent; i++) {
+ write(1, " ", 2);
+ }
+ write(1, "}", 1);
+
+ } else {
+ if (vp->name) {
+ write(1, vp->name, strlen(vp->name));
+ } else {
+ write(1, "unknown", 7);
+ }
+ write(1, ": ", 2);
+
+ /* FUTURE -- other types ? */
+ mprVarToString(&buf, MPR_MAX_STRING, 0, vp);
+ if (vp->type == MPR_TYPE_STRING) {
+ write(1, "\"", 1);
+ }
+ write(1, buf, strlen(buf));
+ if (vp->type == MPR_TYPE_STRING) {
+ write(1, "\"", 1);
+ }
+ mprFree(buf);
+ }
+}
+
+/******************************************************************************/
+/*
+ * Print the args to stdout
+ */
+
+static int printVarsProc(EjsHandle eid, int argc, MprVar **argv)
+{
+ MprVar *vp;
+ char *buf;
+ int i;
+
+ for (i = 0; i < argc; i++) {
+ vp = argv[i];
+ switch (vp->type) {
+ case MPR_TYPE_OBJECT:
+ printVar(vp, 0, 0);
+ break;
+ default:
+ mprVarToString(&buf, MPR_MAX_STRING, 0, vp);
+ write(1, buf, strlen(buf));
+ mprFree(buf);
+ break;
+ }
+ }
+ write(1, "\n", 1);
+
+ ejsSetReturnValue(eid, mprCreateStringVar("", 0));
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Print the args to stdout
+ */
+
+static int printProc(EjsHandle eid, int argc, MprVar **argv)
+{
+ char *buf;
+ int i;
+
+ for (i = 0; i < argc; i++) {
+ mprVarToString(&buf, MPR_MAX_STRING, 0, argv[i]);
+ write(1, buf, strlen(buf));
+ mprFree(buf);
+ }
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * println
+ */
+
+static int printlnProc(EjsHandle eid, int argc, MprVar **argv)
+{
+ printProc(eid, argc, argv);
+ write(1, "\n", 1);
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Trace
+ */
+
+static int traceProc(EjsHandle eid, int argc, char **argv)
+{
+ if (argc == 1) {
+ mprLog(0, "%s", argv[0]);
+
+ } else if (argc == 2) {
+ mprLog(atoi(argv[0]), "%s", argv[1]);
+
+ } else {
+ ejsSetErrorMsg(eid, "Usage: trace([level], message)");
+ return -1;
+ }
+ ejsSetReturnString(eid, "");
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Return the object reference count
+ */
+
+static int refCountProc(EjsHandle eid, int argc, MprVar **argv)
+{
+ MprVar *vp;
+ int count;
+
+ vp = argv[0];
+ if (vp->type == MPR_TYPE_OBJECT) {
+ count = mprGetVarRefCount(vp);
+ ejsSetReturnValue(eid, mprCreateIntegerVar(count));
+ } else {
+ ejsSetReturnValue(eid, mprCreateIntegerVar(0));
+ }
+
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Evaluate a sub-script. It is evaluated in the same variable scope as
+ * the calling script / function.
+ */
+
+static int evalScriptProc(EjsHandle eid, int argc, MprVar **argv)
+{
+ MprVar *arg;
+ char *emsg;
+ int i;
+
+ ejsSetReturnValue(eid, mprCreateUndefinedVar());
+
+ for (i = 0; i < argc; i++) {
+ arg = argv[i];
+ if (arg->type != MPR_TYPE_STRING) {
+ continue;
+ }
+ if (ejsEvalScript(eid, arg->string, 0, &emsg) < 0) {
+ ejsSetErrorMsg(eid, "%s", emsg);
+ mprFree(emsg);
+ return -1;
+ }
+ }
+ /*
+ * Return with the value of the last expression
+ */
+ return 0;
+}
+
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/*
+ * Define the standard properties and functions inherited by all script engines.
+ */
+
+int ejsDefineStandardProperties(MprVar *obj)
+{
+#if BLD_FEATURE_FLOATING_POINT
+ double d = 0.0;
+
+ /* FUTURE - this generates warnings on some systems. This is okay. */
+
+ mprCreatePropertyValue(obj, "NaN", mprCreateFloatVar(0.0 / d));
+ d = MAX_FLOAT;
+ mprCreatePropertyValue(obj, "Infinity", mprCreateFloatVar(d * d));
+#endif
+ mprCreatePropertyValue(obj, "null", mprCreateNullVar());
+ mprCreatePropertyValue(obj, "undefined", mprCreateUndefinedVar());
+ mprCreatePropertyValue(obj, "true", mprCreateBoolVar(1));
+ mprCreatePropertyValue(obj, "false", mprCreateBoolVar(0));
+ mprCreatePropertyValue(obj, "NULL", mprCreatePtrVar(NULL));
+
+#if BLD_FEATURE_LEGACY_API
+ /*
+ * DEPRECATED: 2.0.
+ * So that ESP/ASP can ignore "language=javascript" statements
+ */
+ mprCreatePropertyValue(obj, "javascript", mprCreateIntegerVar(0));
+#endif
+
+ /*
+ * Extension functions
+ */
+ mprCreatePropertyValue(obj, "assert",
+ mprCreateCFunctionVar(assertProc, 0, MPR_VAR_SCRIPT_HANDLE));
+ mprCreatePropertyValue(obj, "eval",
+ mprCreateCFunctionVar(evalScriptProc, 0, MPR_VAR_SCRIPT_HANDLE));
+ mprCreatePropertyValue(obj, "exit",
+ mprCreateCFunctionVar(exitProc, 0, MPR_VAR_SCRIPT_HANDLE));
+ mprCreatePropertyValue(obj, "refCount",
+ mprCreateCFunctionVar(refCountProc, 0, MPR_VAR_SCRIPT_HANDLE));
+ mprCreatePropertyValue(obj, "print",
+ mprCreateCFunctionVar(printProc, 0, MPR_VAR_SCRIPT_HANDLE));
+ mprCreatePropertyValue(obj, "println",
+ mprCreateCFunctionVar(printlnProc, 0, MPR_VAR_SCRIPT_HANDLE));
+ mprCreatePropertyValue(obj, "printVars",
+ mprCreateCFunctionVar(printVarsProc,0, MPR_VAR_SCRIPT_HANDLE));
+ mprCreatePropertyValue(obj, "trace",
+ mprCreateStringCFunctionVar(traceProc, 0, MPR_VAR_SCRIPT_HANDLE));
+
+ /*
+ * Constructors
+ */
+ mprCreatePropertyValue(obj, "Array",
+ mprCreateCFunctionVar(arrayConsProc, 0, MPR_VAR_SCRIPT_HANDLE));
+ mprCreatePropertyValue(obj, "Boolean",
+ mprCreateCFunctionVar(booleanConsProc, 0, MPR_VAR_SCRIPT_HANDLE));
+ mprCreatePropertyValue(obj, "Object",
+ mprCreateCFunctionVar(objectConsProc, 0, MPR_VAR_SCRIPT_HANDLE));
+ mprCreatePropertyValue(obj, "Number",
+ mprCreateCFunctionVar(numberConsProc, 0, MPR_VAR_SCRIPT_HANDLE));
+ mprCreatePropertyValue(obj, "String",
+ mprCreateCFunctionVar(stringConsProc, 0, MPR_VAR_SCRIPT_HANDLE));
+
+ /* mprCreatePropertyValue(obj, "Date",
+ * mprCreateCFunctionVar(dateConsProc, 0, MPR_VAR_SCRIPT_HANDLE));
+ * mprCreatePropertyValue(obj, "Regexp",
+ * mprCreateCFunctionVar(regexpConsProc, 0, MPR_VAR_SCRIPT_HANDLE));
+ */
+
+ /*
+ * Can we use on var x = "string text";
+ */
+ 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
+ */