From 7dd0ae5f57bde24e76052ef22591431e1847bd5f Mon Sep 17 00:00:00 2001 From: armin Date: Fri, 3 Sep 2010 22:06:14 +0000 Subject: [PATCH 01/43] branch started From 053662e1f22c01ea758247568bba6226e91694a0 Mon Sep 17 00:00:00 2001 From: armin Date: Fri, 3 Sep 2010 22:11:19 +0000 Subject: [PATCH 02/43] re-commit svn-R21 --- Script.cpp | 30 +- Script.vcproj | 211 ++ TinyJS.cpp | 4375 +++++++++++++++++++++++------------------- TinyJS.h | 723 +++---- TinyJS_Functions.cpp | 16 +- lib-tiny-js.vcproj | 187 ++ targetver.h | 24 + tiny-js.sln | 28 + 8 files changed, 3253 insertions(+), 2341 deletions(-) create mode 100644 Script.vcproj create mode 100644 lib-tiny-js.vcproj create mode 100644 targetver.h create mode 100644 tiny-js.sln diff --git a/Script.cpp b/Script.cpp index f702a30..19baf6e 100755 --- a/Script.cpp +++ b/Script.cpp @@ -27,28 +27,46 @@ * This is a simple program showing how to use TinyJS */ +#if defined(_MSC_VER) +# include "targetver.h" +# include +#endif #include "TinyJS.h" #include "TinyJS_Functions.h" #include #include +#if defined(_MSC_VER) && defined(_DEBUG) +# define new DEBUG_NEW +#endif + +#ifdef __GNUC__ +# define UNUSED(x) __attribute__((__unused__)) +#elif defined(_MSC_VER) +# ifndef UNUSED +# define UNUSED(x) x +# pragma warning( disable : 4100 ) /* unreferenced formal parameter */ +# endif +#else +# define UNUSED(x) x +#endif //const char *code = "var a = 5; if (a==5) a=4; else a=3;"; //const char *code = "{ var a = 4; var b = 1; while (a>0) { b = b * 2; a = a - 1; } var c = 5; }"; //const char *code = "{ var b = 1; for (var i=0;i<4;i=i+1) b = b * 2; }"; const char *code = "function myfunc(x, y) { return x + y; } var a = myfunc(1,2); print(a);"; -void js_print(CScriptVar *v, void *userdata) { +void js_print(CScriptVar *v, void *UNUSED(userdata)) { printf("> %s\n", v->getParameter("text")->getString().c_str()); } -void js_dump(CScriptVar *v, void *userdata) { +void js_dump(CScriptVar *UNUSED(v), void *userdata) { CTinyJS *js = (CTinyJS*)userdata; js->root->trace("> "); } -int main(int argc, char **argv) +int main(int UNUSED(argc), char **UNUSED(argv)) { CTinyJS *js = new CTinyJS(); /* add the functions from TinyJS_Functions.cpp */ @@ -75,10 +93,10 @@ int main(int argc, char **argv) } } delete js; -#ifdef _WIN32 -#ifdef _DEBUG +#if defined(_WIN32) && defined(_DEBUG) && !defined(_MSC_VER) + // by Visual Studio we use the DEBUG_NEW stuff _CrtDumpMemoryLeaks(); #endif -#endif + new int[10]; return 0; } diff --git a/Script.vcproj b/Script.vcproj new file mode 100644 index 0000000..633c130 --- /dev/null +++ b/Script.vcproj @@ -0,0 +1,211 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TinyJS.cpp b/TinyJS.cpp index 8ffa421..f7b464e 100755 --- a/TinyJS.cpp +++ b/TinyJS.cpp @@ -1,1996 +1,2379 @@ -/* - * TinyJS - * - * A single-file Javascript-alike engine - * - * Authored By Gordon Williams - * - * Copyright (C) 2009 Pur3 Ltd - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -/* Version 0.1 : (gw) First published on Google Code - Version 0.11 : Making sure the 'root' variable never changes - 'symbol_base' added for the current base of the sybmbol table - Version 0.12 : Added findChildOrCreate, changed string passing to use references - Fixed broken string encoding in getJSString() - Removed getInitCode and added getJSON instead - Added nil - Added rough JSON parsing - Improved example app - Version 0.13 : Added tokenEnd/tokenLastEnd to lexer to avoid parsing whitespace - Ability to define functions without names - Can now do "var mine = function(a,b) { ... };" - Slightly better 'trace' function - Added findChildOrCreateByPath function - Added simple test suite - Added skipping of blocks when not executing - Version 0.14 : Added parsing of more number types - Added parsing of string defined with ' - Changed nil to null as per spec, added 'undefined' - Now set variables with the correct scope, and treat unknown - as 'undefined' rather than failing - Added proper (I hope) handling of null and undefined - Added === check - Version 0.15 : Fix for possible memory leaks - Version 0.16 : Removal of un-needed findRecursive calls - symbol_base removed and replaced with 'scopes' stack - Added reference counting a proper tree structure - (Allowing pass by reference) - Allowed JSON output to output IDs, not strings - Added get/set for array indices - Changed Callbacks to include user data pointer - Added some support for objects - Added more Java-esque builtin functions - Version 0.17 : Now we don't deepCopy the parent object of the class - Added JSON.stringify and eval() - Nicer JSON indenting - Fixed function output in JSON - Added evaluateComplex - Fixed some reentrancy issues with evaluate/execute - Version 0.18 : Fixed some issues with code being executed when it shouldn't - Version 0.19 : Added array.length - Changed '__parent' to 'prototype' to bring it more in line with javascript - Version 0.20 : Added '%' operator - Version 0.21 : Added array type - String.length() no more - now String.length - Added extra constructors to reduce confusion - Fixed checks against undefined - Version 0.22 : First part of ardi's changes: - sprintf -> sprintf_s - extra tokens parsed - array memory leak fixed - Fixed memory leak in evaluateComplex - Fixed memory leak in FOR loops - Fixed memory leak for unary minus - Version 0.23 : Allowed evaluate[Complex] to take in semi-colon separated - statements and then only return the value from the last one. - Also checks to make sure *everything* was parsed. - Ints + doubles are now stored in binary form (faster + more precise) - - NOTE: This doesn't support constructors for objects - Recursive loops of data such as a.foo = a; fail to be garbage collected - 'length' cannot be set - There is no ternary operator implemented yet - The postfix increment operator returns the current value, not the - previous as it should. - Arrays are implemented as a linked list - hence a lookup is O(n) - - TODO: - Utility va-args style function in TinyJS for executing a function directly - - */ - -#include "TinyJS.h" -#include - -#define ASSERT(X) assert(X) -/* Frees the given link IF it isn't owned by anything else */ -#define CLEAN(x) { CScriptVarLink *__v = x; if (__v && !__v->owned) { delete __v; } } -/* Create a LINK to point to VAR and free the old link. - * BUT this is more clever - it tries to keep the old link if it's not owned to save allocations */ -#define CREATE_LINK(LINK, VAR) { if (!LINK || LINK->owned) LINK = new CScriptVarLink(VAR); else LINK->replaceWith(VAR); } - -#include -#include -#include -#include -#include - -using namespace std; - -#ifdef _WIN32 -#ifdef _DEBUG - #ifndef DBG_NEW - #define DBG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ ) - #define new DBG_NEW - #endif -#endif -#endif - -#ifdef __GNUC__ -#define vsprintf_s vsnprintf -#define sprintf_s snprintf -#define _strdup strdup -#endif - -// ----------------------------------------------------------------------------------- Memory Debug - -#define DEBUG_MEMORY 0 - -#if DEBUG_MEMORY - -vector allocatedVars; -vector allocatedLinks; - -void mark_allocated(CScriptVar *v) { - allocatedVars.push_back(v); -} - -void mark_deallocated(CScriptVar *v) { - for (size_t i=0;igetRefs()); - allocatedVars[i]->trace(" "); - } - for (size_t i=0;iname.c_str(), allocatedLinks[i]->var->getRefs()); - allocatedLinks[i]->var->trace(" "); - } - allocatedVars.clear(); - allocatedLinks.clear(); -} -#endif - -// ----------------------------------------------------------------------------------- Utils -bool isWhitespace(char ch) { - return (ch==' ') || (ch=='\t') || (ch=='\n') || (ch=='\r'); -} - -bool isNumeric(char ch) { - return (ch>='0') && (ch<='9'); -} -bool isNumber(const string &str) { - for (size_t i=0;i='0') && (ch<='9')) || - ((ch>='a') && (ch<='f')) || - ((ch>='A') && (ch<='F')); -} -bool isAlpha(char ch) { - return ((ch>='a') && (ch<='z')) || ((ch>='A') && (ch<='Z')) || ch=='_'; -} - -bool isIDString(const char *s) { - if (!isAlpha(*s)) - return false; - while (*s) { - if (!(isAlpha(*s) || isNumeric(*s))) - return false; - s++; - } - return true; -} - -void replace(string &str, char textFrom, const char *textTo) { - int sLen = strlen(textTo); - size_t p = str.find(textFrom); - while (p != string::npos) { - str = str.substr(0, p) + textTo + str.substr(p+1); - p = str.find(textFrom, p+sLen); - } -} - -/// convert the given string into a quoted string suitable for javascript -std::string getJSString(const std::string &str) { - std::string nStr = str; - for (size_t i=0;idata; - dataOwned = false; - dataStart = startChar; - dataEnd = endChar; - reset(); -} - -CScriptLex::~CScriptLex(void) -{ - if (dataOwned) - free((void*)data); -} - -void CScriptLex::reset() { - dataPos = dataStart; - tokenStart = 0; - tokenEnd = 0; - tokenLastEnd = 0; - tk = 0; - tkStr = ""; - getNextCh(); - getNextCh(); - getNextToken(); -} - -void CScriptLex::match(int expected_tk) { - if (tk!=expected_tk) { - ostringstream errorString; - errorString << "Got " << getTokenStr(tk) << " expected " << getTokenStr(expected_tk) - << " at " << getPosition(tokenStart) << " in '" << data << "'"; - throw new CScriptException(errorString.str()); - } - getNextToken(); -} - -string CScriptLex::getTokenStr(int token) { - if (token>32 && token<128) { - char buf[4] = "' '"; - buf[1] = (char)token; - return buf; - } - switch (token) { - case LEX_EOF : return "EOF"; - case LEX_ID : return "ID"; - case LEX_INT : return "INT"; - case LEX_FLOAT : return "FLOAT"; - case LEX_STR : return "STRING"; - case LEX_EQUAL : return "=="; - case LEX_TYPEEQUAL : return "==="; - case LEX_NEQUAL : return "!="; - case LEX_NTYPEEQUAL : return "!=="; - case LEX_LEQUAL : return "<="; - case LEX_LSHIFT : return "<<"; - case LEX_LSHIFTEQUAL : return "<<="; - case LEX_GEQUAL : return ">="; - case LEX_RSHIFT : return ">>"; - case LEX_RSHIFTEQUAL : return ">>="; - case LEX_PLUSEQUAL : return "+="; - case LEX_MINUSEQUAL : return "-="; - case LEX_PLUSPLUS : return "++"; - case LEX_MINUSMINUS : return "--"; - case LEX_ANDEQUAL : return "&="; - case LEX_ANDAND : return "&&"; - case LEX_OREQUAL : return "|="; - case LEX_OROR : return "||"; - case LEX_XOREQUAL : return "^="; - // reserved words - case LEX_R_IF : return "if"; - case LEX_R_ELSE : return "else"; - case LEX_R_DO : return "do"; - case LEX_R_WHILE : return "while"; - case LEX_R_FOR : return "for"; - case LEX_R_BREAK : return "break"; - case LEX_R_CONTINUE : return "continue"; - case LEX_R_FUNCTION : return "function"; - case LEX_R_RETURN : return "return"; - case LEX_R_VAR : return "var"; - case LEX_R_TRUE : return "true"; - case LEX_R_FALSE : return "false"; - case LEX_R_NULL : return "null"; - case LEX_R_UNDEFINED : return "undefined"; - case LEX_R_NEW : return "new"; - } - - ostringstream msg; - msg << "?[" << token << "]"; - return msg.str(); -} - -void CScriptLex::getNextCh() { - currCh = nextCh; - if (dataPos < dataEnd) - nextCh = data[dataPos]; - else - nextCh = 0; - dataPos++; -} - -void CScriptLex::getNextToken() { - tk = LEX_EOF; - tkStr.clear(); - while (currCh && isWhitespace(currCh)) getNextCh(); - // newline comments - if (currCh=='/' && nextCh=='/') { - while (currCh && currCh!='\n') getNextCh(); - getNextCh(); - getNextToken(); - return; - } - // block comments - if (currCh=='/' && nextCh=='*') { - while (currCh && (currCh!='*' || nextCh!='/')) getNextCh(); - getNextCh(); - getNextCh(); - getNextToken(); - return; - } - // record beginning of this token - tokenStart = dataPos-2; - // tokens - if (isAlpha(currCh)) { // IDs - while (isAlpha(currCh) || isNumeric(currCh)) { - tkStr += currCh; - getNextCh(); - } - tk = LEX_ID; - if (tkStr=="if") tk = LEX_R_IF; - else if (tkStr=="else") tk = LEX_R_ELSE; - else if (tkStr=="do") tk = LEX_R_DO; - else if (tkStr=="while") tk = LEX_R_WHILE; - else if (tkStr=="for") tk = LEX_R_FOR; - else if (tkStr=="break") tk = LEX_R_BREAK; - else if (tkStr=="continue") tk = LEX_R_CONTINUE; - else if (tkStr=="function") tk = LEX_R_FUNCTION; - else if (tkStr=="return") tk = LEX_R_RETURN; - else if (tkStr=="var") tk = LEX_R_VAR; - else if (tkStr=="true") tk = LEX_R_TRUE; - else if (tkStr=="false") tk = LEX_R_FALSE; - else if (tkStr=="null") tk = LEX_R_NULL; - else if (tkStr=="undefined") tk = LEX_R_UNDEFINED; - else if (tkStr=="new") tk = LEX_R_NEW; - } else if (isNumeric(currCh)) { // Numbers - bool isHex = false; - if (currCh=='0') { tkStr += currCh; getNextCh(); } - if (currCh=='x') { - isHex = true; - tkStr += currCh; getNextCh(); - } - tk = LEX_INT; - while (isNumeric(currCh) || (isHex && isHexadecimal(currCh))) { - tkStr += currCh; - getNextCh(); - } - if (!isHex && currCh=='.') { - tk = LEX_FLOAT; - tkStr += '.'; - getNextCh(); - while (isNumeric(currCh)) { - tkStr += currCh; - getNextCh(); - } - } - // do fancy e-style floating point - if (!isHex && currCh=='e') { - tk = LEX_FLOAT; - tkStr += currCh; getNextCh(); - if (currCh=='-') { tkStr += currCh; getNextCh(); } - while (isNumeric(currCh)) { - tkStr += currCh; getNextCh(); - } - } - } else if (currCh=='"') { - // strings... - getNextCh(); - while (currCh && currCh!='"') { - if (currCh == '\\') { - getNextCh(); - switch (currCh) { - case 'n' : tkStr += '\n'; break; - case '"' : tkStr += '"'; break; - case '\\' : tkStr += '\\'; break; - default: tkStr += currCh; - } - } else { - tkStr += currCh; - } - getNextCh(); - } - getNextCh(); - tk = LEX_STR; - } else if (currCh=='\'') { - // strings again... - getNextCh(); - while (currCh && currCh!='\'') { - if (currCh == '\\') { - getNextCh(); - switch (currCh) { - case 'n' : tkStr += '\n'; break; - case '\'' : tkStr += '\''; break; - case '\\' : tkStr += '\\'; break; - default: tkStr += currCh; - } - } else { - tkStr += currCh; - } - getNextCh(); - } - getNextCh(); - tk = LEX_STR; - } else { - // single chars - tk = currCh; - if (currCh) getNextCh(); - if (tk=='=' && currCh=='=') { // == - tk = LEX_EQUAL; - getNextCh(); - if (currCh=='=') { // === - tk = LEX_TYPEEQUAL; - getNextCh(); - } - } else if (tk=='!' && currCh=='=') { // != - tk = LEX_NEQUAL; - getNextCh(); - if (currCh=='=') { // !== - tk = LEX_NTYPEEQUAL; - getNextCh(); - } - } else if (tk=='<' && currCh=='=') { - tk = LEX_LEQUAL; - getNextCh(); - } else if (tk=='<' && currCh=='<') { - tk = LEX_LSHIFT; - getNextCh(); - if (currCh=='=') { // <<= - tk = LEX_LSHIFTEQUAL; - getNextCh(); - } - } else if (tk=='>' && currCh=='=') { - tk = LEX_GEQUAL; - getNextCh(); - } else if (tk=='>' && currCh=='>') { - tk = LEX_RSHIFT; - getNextCh(); - if (currCh=='=') { // <<= - tk = LEX_RSHIFTEQUAL; - getNextCh(); - } - } else if (tk=='+' && currCh=='=') { - tk = LEX_PLUSEQUAL; - getNextCh(); - } else if (tk=='-' && currCh=='=') { - tk = LEX_MINUSEQUAL; - getNextCh(); - } else if (tk=='+' && currCh=='+') { - tk = LEX_PLUSPLUS; - getNextCh(); - } else if (tk=='-' && currCh=='-') { - tk = LEX_MINUSMINUS; - getNextCh(); - } else if (tk=='&' && currCh=='=') { - tk = LEX_ANDEQUAL; - getNextCh(); - } else if (tk=='&' && currCh=='&') { - tk = LEX_ANDAND; - getNextCh(); - } else if (tk=='|' && currCh=='=') { - tk = LEX_OREQUAL; - getNextCh(); - } else if (tk=='|' && currCh=='|') { - tk = LEX_OROR; - getNextCh(); - } else if (tk=='^' && currCh=='=') { - tk = LEX_XOREQUAL; - getNextCh(); - } - } - /* This isn't quite right yet */ - tokenLastEnd = tokenEnd; - tokenEnd = dataPos-3; -} - -string CScriptLex::getSubString(int lastPosition) { - int lastCharIdx = tokenLastEnd+1; - if (lastCharIdx < dataEnd) { - /* save a memory alloc by using our data array to create the - substring */ - char old = data[lastCharIdx]; - data[lastCharIdx] = 0; - std::string value = &data[lastPosition]; - data[lastCharIdx] = old; - return value; - } else { - return std::string(&data[lastPosition]); - } -} - - -CScriptLex *CScriptLex::getSubLex(int lastPosition) { - int lastCharIdx = tokenLastEnd+1; - if (lastCharIdx < dataEnd) - return new CScriptLex( this, lastPosition, lastCharIdx); - else - return new CScriptLex( this, lastPosition, dataEnd ); -} - -string CScriptLex::getPosition(int pos) { - if (pos<0) pos=tokenLastEnd; - int line = 1,col = 1; - for (int i=0;iname = name; - this->nextSibling = 0; - this->prevSibling = 0; - this->var = var->ref(); - this->owned = false; -} - -CScriptVarLink::CScriptVarLink(const CScriptVarLink &link) { - // Copy constructor -#if DEBUG_MEMORY - mark_allocated(this); -#endif - this->name = link.name; - this->nextSibling = 0; - this->prevSibling = 0; - this->var = link.var->ref(); - this->owned = false; -} - -CScriptVarLink::~CScriptVarLink() { -#if DEBUG_MEMORY - mark_deallocated(this); -#endif - var->unref(); -} - -void CScriptVarLink::replaceWith(CScriptVar *newVar) { - CScriptVar *oldVar = var; - var = newVar->ref(); - oldVar->unref(); -} - -void CScriptVarLink::replaceWith(CScriptVarLink *newVar) { - if (newVar) - replaceWith(newVar->var); - else - replaceWith(new CScriptVar()); -} - -// ----------------------------------------------------------------------------------- CSCRIPTVAR - -CScriptVar::CScriptVar() { - refs = 0; -#if DEBUG_MEMORY - mark_allocated(this); -#endif - init(); - flags = SCRIPTVAR_UNDEFINED; -} - -CScriptVar::CScriptVar(const string &str) { - refs = 0; -#if DEBUG_MEMORY - mark_allocated(this); -#endif - init(); - flags = SCRIPTVAR_STRING; - data = str; -} - - -CScriptVar::CScriptVar(const string &varData, int varFlags) { - refs = 0; -#if DEBUG_MEMORY - mark_allocated(this); -#endif - init(); - flags = varFlags; - if (varFlags & SCRIPTVAR_INTEGER) { - intData = strtol(varData.c_str(),0,0); - } else if (varFlags & SCRIPTVAR_DOUBLE) { - doubleData = strtod(varData.c_str(),0); - } else - data = varData; -} - -CScriptVar::CScriptVar(double val) { - refs = 0; -#if DEBUG_MEMORY - mark_allocated(this); -#endif - init(); - setDouble(val); -} - -CScriptVar::CScriptVar(int val) { - refs = 0; -#if DEBUG_MEMORY - mark_allocated(this); -#endif - init(); - setInt(val); -} - -CScriptVar::~CScriptVar(void) { -#if DEBUG_MEMORY - mark_deallocated(this); -#endif - removeAllChildren(); -} - -void CScriptVar::init() { - firstChild = 0; - lastChild = 0; - flags = 0; - jsCallback = 0; - jsCallbackUserData = 0; - data = TINYJS_BLANK_DATA; - intData = 0; - doubleData = 0; -} - -CScriptVar *CScriptVar::getReturnVar() { - return getParameter(TINYJS_RETURN_VAR); -} - -void CScriptVar::setReturnVar(CScriptVar *var) { - findChildOrCreate(TINYJS_RETURN_VAR)->replaceWith(var); -} - - -CScriptVar *CScriptVar::getParameter(const std::string &name) { - return findChildOrCreate(name)->var; -} - -CScriptVarLink *CScriptVar::findChild(const string &childName) { - CScriptVarLink *v = firstChild; - while (v) { - if (v->name.compare(childName)==0) - return v; - v = v->nextSibling; - } - return 0; -} - -CScriptVarLink *CScriptVar::findChildOrCreate(const string &childName, int varFlags) { - CScriptVarLink *l = findChild(childName); - if (l) return l; - - return addChild(childName, new CScriptVar(TINYJS_BLANK_DATA, varFlags)); -} - -CScriptVarLink *CScriptVar::findChildOrCreateByPath(const std::string &path) { - size_t p = path.find('.'); - if (p == string::npos) - return findChildOrCreate(path); - - return findChildOrCreate(path.substr(0,p), SCRIPTVAR_OBJECT)->var-> - findChildOrCreateByPath(path.substr(p+1)); -} - -CScriptVarLink *CScriptVar::addChild(const std::string &childName, CScriptVar *child) { - if (isUndefined()) { - flags = SCRIPTVAR_OBJECT; - } - // if no child supplied, create one - if (!child) - child = new CScriptVar(); - - CScriptVarLink *link = new CScriptVarLink(child, childName); - link->owned = true; - if (lastChild) { - lastChild->nextSibling = link; - link->prevSibling = lastChild; - lastChild = link; - } else { - firstChild = link; - lastChild = link; - } - return link; -} - -CScriptVarLink *CScriptVar::addChildNoDup(const std::string &childName, CScriptVar *child) { - // if no child supplied, create one - if (!child) - child = new CScriptVar(); - - CScriptVarLink *v = findChild(childName); - if (v) { - v->replaceWith(child); - } else { - v = addChild(childName, child); - } - - return v; -} - -void CScriptVar::removeChild(CScriptVar *child) { - CScriptVarLink *link = firstChild; - while (link) { - if (link->var == child) - break; - link = link->nextSibling; - } - ASSERT(link); - removeLink(link); -} - -void CScriptVar::removeLink(CScriptVarLink *link) { - if (!link) return; - if (link->nextSibling) - link->nextSibling->prevSibling = link->prevSibling; - if (link->prevSibling) - link->prevSibling->nextSibling = link->nextSibling; - if (lastChild == link) - lastChild = link->prevSibling; - if (firstChild == link) - firstChild = link->nextSibling; - delete link; -} - -void CScriptVar::removeAllChildren() { - CScriptVarLink *c = firstChild; - while (c) { - CScriptVarLink *t = c->nextSibling; - delete c; - c = t; - } - firstChild = 0; - lastChild = 0; -} - -CScriptVar *CScriptVar::getArrayIndex(int idx) { - char sIdx[64]; - sprintf_s(sIdx, sizeof(sIdx), "%d", idx); - CScriptVarLink *link = findChild(sIdx); - if (link) return link->var; - else return new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_NULL); // undefined -} - -void CScriptVar::setArrayIndex(int idx, CScriptVar *value) { - char sIdx[64]; - sprintf_s(sIdx, sizeof(sIdx), "%d", idx); - CScriptVarLink *link = findChild(sIdx); - - if (link) { - if (value->isUndefined()) - removeLink(link); - else - link->replaceWith(value); - } else { - if (!value->isUndefined()) - addChild(sIdx, value); - } -} - -int CScriptVar::getArrayLength() { - int highest = -1; - if (!isArray()) return 0; - - CScriptVarLink *link = firstChild; - while (link) { - if (isNumber(link->name)) { - int val = atoi(link->name.c_str()); - if (val > highest) highest = val; - } - link = link->nextSibling; - } - return highest+1; -} - -int CScriptVar::getChildren() { - int n = 0; - CScriptVarLink *link = firstChild; - while (link) { - n++; - link = link->nextSibling; - } - return n; -} - -int CScriptVar::getInt() { - /* strtol understands about hex and octal */ - if (isInt()) return intData; - if (isNull()) return 0; - if (isUndefined()) return 0; - if (isDouble()) return (int)doubleData; - return 0; -} - -double CScriptVar::getDouble() { - if (isDouble()) return doubleData; - if (isInt()) return intData; - if (isNull()) return 0; - if (isUndefined()) return 0; - return 0; /* or NaN? */ -} - -const string &CScriptVar::getString() { - /* Because we can't return a string that is generated on demand. - * I should really just use char* :) */ - static string s_null = "null"; - static string s_undefined = "undefined"; - if (isInt()) { - char buffer[32]; - sprintf_s(buffer, sizeof(buffer), "%ld", intData); - data = buffer; - return data; - } - if (isDouble()) { - char buffer[32]; - sprintf_s(buffer, sizeof(buffer), "%lf", doubleData); - data = buffer; - return data; - } - if (isNull()) return s_null; - if (isUndefined()) return s_undefined; - // are we just a string here? - return data; -} - -void CScriptVar::setInt(int val) { - flags = (flags&~SCRIPTVAR_VARTYPEMASK) | SCRIPTVAR_INTEGER; - intData = val; - doubleData = 0; - data = TINYJS_BLANK_DATA; -} - -void CScriptVar::setDouble(double val) { - flags = (flags&~SCRIPTVAR_VARTYPEMASK) | SCRIPTVAR_DOUBLE; - doubleData = val; - intData = 0; - data = TINYJS_BLANK_DATA; -} - -void CScriptVar::setString(const string &str) { - // name sure it's not still a number or integer - flags = (flags&~SCRIPTVAR_VARTYPEMASK) | SCRIPTVAR_STRING; - data = str; - intData = 0; - doubleData = 0; -} - -void CScriptVar::setUndefined() { - // name sure it's not still a number or integer - flags = (flags&~SCRIPTVAR_VARTYPEMASK) | SCRIPTVAR_UNDEFINED; - data = TINYJS_BLANK_DATA; - intData = 0; - doubleData = 0; - removeAllChildren(); -} - -CScriptVar *CScriptVar::mathsOp(CScriptVar *b, int op) { - CScriptVar *a = this; - // Type equality check - if (op == LEX_TYPEEQUAL || op == LEX_NTYPEEQUAL) { - // check type first, then call again to check data - bool eql = ((a->flags & SCRIPTVAR_VARTYPEMASK) == - (b->flags & SCRIPTVAR_VARTYPEMASK)) && - a->mathsOp(b, LEX_EQUAL); - if (op == LEX_TYPEEQUAL) - return new CScriptVar(eql); - else - return new CScriptVar(!eql); - } - // do maths... - if (a->isUndefined() && b->isUndefined()) { - if (op == LEX_EQUAL) return new CScriptVar(true); - else if (op == LEX_NEQUAL) return new CScriptVar(false); - else return new CScriptVar(); // undefined - } else if ((a->isNumeric() || a->isUndefined()) && - (b->isNumeric() || b->isUndefined())) { - if (!a->isDouble() && !b->isDouble()) { - // use ints - int da = a->getInt(); - int db = b->getInt(); - switch (op) { - case '+': return new CScriptVar(da+db); - case '-': return new CScriptVar(da-db); - case '*': return new CScriptVar(da*db); - case '/': return new CScriptVar(da/db); - case '&': return new CScriptVar(da&db); - case '|': return new CScriptVar(da|db); - case '^': return new CScriptVar(da^db); - case '%': return new CScriptVar(da%db); - case LEX_EQUAL: return new CScriptVar(da==db); - case LEX_NEQUAL: return new CScriptVar(da!=db); - case '<': return new CScriptVar(da': return new CScriptVar(da>db); - case LEX_GEQUAL: return new CScriptVar(da>=db); - default: throw new CScriptException("This operation not supported on the Int datatype"); - } - } else { - // use doubles - double da = a->getDouble(); - double db = b->getDouble(); - switch (op) { - case '+': return new CScriptVar(da+db); - case '-': return new CScriptVar(da-db); - case '*': return new CScriptVar(da*db); - case '/': return new CScriptVar(da/db); - case LEX_EQUAL: return new CScriptVar(da==db); - case LEX_NEQUAL: return new CScriptVar(da!=db); - case '<': return new CScriptVar(da': return new CScriptVar(da>db); - case LEX_GEQUAL: return new CScriptVar(da>=db); - default: throw new CScriptException("This operation not supported on the Double datatype"); - } - } - } else if (a->isArray()) { - /* Just check pointers */ - switch (op) { - case LEX_EQUAL: return new CScriptVar(a==b); - case LEX_NEQUAL: return new CScriptVar(a!=b); - default: throw new CScriptException("This operation not supported on the Array datatype"); - } - } else if (a->isObject()) { - /* Just check pointers */ - switch (op) { - case LEX_EQUAL: return new CScriptVar(a==b); - case LEX_NEQUAL: return new CScriptVar(a!=b); - default: throw new CScriptException("This operation not supported on the Object datatype"); - } - } else { - string da = a->getString(); - string db = b->getString(); - // use strings - switch (op) { - case '+': return new CScriptVar(da+db, SCRIPTVAR_STRING); - case LEX_EQUAL: return new CScriptVar(da==db); - case LEX_NEQUAL: return new CScriptVar(da!=db); - case '<': return new CScriptVar(da': return new CScriptVar(da>db); - case LEX_GEQUAL: return new CScriptVar(da>=db); - default: throw new CScriptException("This operation not supported on the string datatype"); - } - } - ASSERT(0); - return 0; -} - -void CScriptVar::copySimpleData(CScriptVar *val) { - data = val->data; - intData = val->intData; - doubleData = val->doubleData; - flags = (flags & ~SCRIPTVAR_VARTYPEMASK) | (val->flags & SCRIPTVAR_VARTYPEMASK); -} - -void CScriptVar::copyValue(CScriptVar *val) { - if (val) { - copySimpleData(val); - // remove all current children - removeAllChildren(); - // copy children of 'val' - CScriptVarLink *child = val->firstChild; - while (child) { - CScriptVar *copied; - // don't copy the 'parent' object... - if (child->name != TINYJS_PROTOTYPE_CLASS) - copied = child->var->deepCopy(); - else - copied = child->var; - - addChild(child->name, copied); - - child = child->nextSibling; - } - } else { - setUndefined(); - } -} - -CScriptVar *CScriptVar::deepCopy() { - CScriptVar *newVar = new CScriptVar(); - newVar->copySimpleData(this); - // copy children - CScriptVarLink *child = firstChild; - while (child) { - CScriptVar *copied; - // don't copy the 'parent' object... - if (child->name != TINYJS_PROTOTYPE_CLASS) - copied = child->var->deepCopy(); - else - copied = child->var; - - newVar->addChild(child->name, copied); - child = child->nextSibling; - } - return newVar; -} - -void CScriptVar::trace(string indentStr, const string &name) { - TRACE("%s'%s' = '%s' %s\n", - indentStr.c_str(), - name.c_str(), - getString().c_str(), - getFlagsAsString().c_str()); - string indent = indentStr+" "; - CScriptVarLink *link = firstChild; - while (link) { - link->var->trace(indent, link->name); - link = link->nextSibling; - } -} - -string CScriptVar::getFlagsAsString() { - string flagstr = ""; - if (flags&SCRIPTVAR_FUNCTION) flagstr = flagstr + "FUNCTION "; - if (flags&SCRIPTVAR_OBJECT) flagstr = flagstr + "OBJECT "; - if (flags&SCRIPTVAR_ARRAY) flagstr = flagstr + "ARRAY "; - if (flags&SCRIPTVAR_NATIVE) flagstr = flagstr + "NATIVE "; - if (flags&SCRIPTVAR_DOUBLE) flagstr = flagstr + "DOUBLE "; - if (flags&SCRIPTVAR_INTEGER) flagstr = flagstr + "INTEGER "; - if (flags&SCRIPTVAR_STRING) flagstr = flagstr + "STRING "; - return flagstr; -} - -string CScriptVar::getParsableString() { - // Numbers can just be put in directly - if (isNumeric()) - return getString(); - if (isFunction()) { - ostringstream funcStr; - funcStr << "function ("; - // get list of parameters - CScriptVarLink *link = firstChild; - while (link) { - funcStr << link->name; - if (link->nextSibling) funcStr << ","; - link = link->nextSibling; - } - // add function body - funcStr << ") " << getString(); - return funcStr.str(); - } - // if it is a string then we quote it - if (isString()) - return getJSString(getString()); - if (isNull()) - return "null"; - return "undefined"; -} - -void CScriptVar::getJSON(ostringstream &destination, const string linePrefix) { - if (isObject()) { - string indentedLinePrefix = linePrefix+" "; - // children - handle with bracketed list - destination << "{ \n"; - CScriptVarLink *link = firstChild; - while (link) { - destination << indentedLinePrefix; - if (isAlphaNum(link->name)) - destination << link->name; - else - destination << getJSString(link->name); - destination << " : "; - link->var->getJSON(destination, indentedLinePrefix); - link = link->nextSibling; - if (link) { - destination << ",\n"; - } - } - destination << "\n" << linePrefix << "}"; - } else if (isArray()) { - string indentedLinePrefix = linePrefix+" "; - destination << "[\n"; - int len = getArrayLength(); - if (len>10000) len=10000; // we don't want to get stuck here! - - for (int i=0;igetJSON(destination, indentedLinePrefix); - if (iref(); - // Add built-in classes - stringClass = (new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT))->ref(); - arrayClass = (new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT))->ref(); - objectClass = (new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT))->ref(); - root->addChild("String", stringClass); - root->addChild("Array", arrayClass); - root->addChild("Object", objectClass); -} - -CTinyJS::~CTinyJS() { - ASSERT(!l); - scopes.clear(); - stringClass->unref(); - arrayClass->unref(); - objectClass->unref(); - root->unref(); - -#if DEBUG_MEMORY - show_allocated(); -#endif -} - -void CTinyJS::trace() { - root->trace(); -} - -void CTinyJS::execute(const string &code) { - CScriptLex *oldLex = l; - vector oldScopes = scopes; - l = new CScriptLex(code); - scopes.clear(); - scopes.push_back(root); - try { - bool execute = true; - while (l->tk) statement(execute); - } catch (CScriptException *e) { - ostringstream msg; - msg << "Error " << e->text << " at " << l->getPosition(); - delete l; - l = oldLex; - throw new CScriptException(msg.str()); - } - delete l; - l = oldLex; - scopes = oldScopes; -} - -CScriptVarLink CTinyJS::evaluateComplex(const string &code) { - CScriptLex *oldLex = l; - vector oldScopes = scopes; - - l = new CScriptLex(code); - scopes.clear(); - scopes.push_back(root); - CScriptVarLink *v = 0; - try { - bool execute = true; - do { - CLEAN(v); - v = base(execute); - if (l->tk!=LEX_EOF) l->match(';'); - } while (l->tk!=LEX_EOF); - } catch (CScriptException *e) { - ostringstream msg; - msg << "Error " << e->text << " at " << l->getPosition(); - delete l; - l = oldLex; - throw new CScriptException(msg.str()); - } - delete l; - l = oldLex; - scopes = oldScopes; - - if (v) { - CScriptVarLink r = *v; - CLEAN(v); - return r; - } - // return undefined... - return CScriptVarLink(new CScriptVar()); -} - -string CTinyJS::evaluate(const string &code) { - return evaluateComplex(code).var->getString(); -} - -void CTinyJS::parseFunctionArguments(CScriptVar *funcVar) { - l->match('('); - while (l->tk!=')') { - funcVar->addChildNoDup(l->tkStr); - l->match(LEX_ID); - if (l->tk!=')') l->match(','); - } - l->match(')'); -} - -void CTinyJS::addNative(const string &funcDesc, JSCallback ptr, void *userdata) { - CScriptLex *oldLex = l; - l = new CScriptLex(funcDesc); - - CScriptVar *base = root; - - l->match(LEX_R_FUNCTION); - string funcName = l->tkStr; - l->match(LEX_ID); - /* Check for dots, we might want to do something like function String.substring ... */ - while (l->tk == '.') { - l->match('.'); - CScriptVarLink *link = base->findChild(funcName); - // if it doesn't exist, make an object class - if (!link) link = base->addChild(funcName, new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT)); - base = link->var; - funcName = l->tkStr; - l->match(LEX_ID); - } - - CScriptVar *funcVar = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_FUNCTION | SCRIPTVAR_NATIVE); - funcVar->setCallback(ptr, userdata); - parseFunctionArguments(funcVar); - delete l; - l = oldLex; - - base->addChild(funcName, funcVar); -} - -CScriptVarLink *CTinyJS::parseFunctionDefinition() { - // actually parse a function... - l->match(LEX_R_FUNCTION); - string funcName = TINYJS_TEMP_NAME; - /* we can have functions without names */ - if (l->tk==LEX_ID) { - funcName = l->tkStr; - l->match(LEX_ID); - } - CScriptVarLink *funcVar = new CScriptVarLink(new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_FUNCTION), funcName); - parseFunctionArguments(funcVar->var); - int funcBegin = l->tokenStart; - bool noexecute = false; - block(noexecute); - funcVar->var->data = l->getSubString(funcBegin); - return funcVar; -} - -CScriptVarLink *CTinyJS::factor(bool &execute) { - if (l->tk=='(') { - l->match('('); - CScriptVarLink *a = base(execute); - l->match(')'); - return a; - } - if (l->tk==LEX_R_TRUE) { - l->match(LEX_R_TRUE); - return new CScriptVarLink(new CScriptVar(1)); - } - if (l->tk==LEX_R_FALSE) { - l->match(LEX_R_FALSE); - return new CScriptVarLink(new CScriptVar(0)); - } - if (l->tk==LEX_R_NULL) { - l->match(LEX_R_NULL); - return new CScriptVarLink(new CScriptVar(TINYJS_BLANK_DATA,SCRIPTVAR_NULL)); - } - if (l->tk==LEX_R_UNDEFINED) { - l->match(LEX_R_UNDEFINED); - return new CScriptVarLink(new CScriptVar(TINYJS_BLANK_DATA,SCRIPTVAR_UNDEFINED)); - } - if (l->tk==LEX_ID) { - CScriptVarLink *a = execute ? findInScopes(l->tkStr) : new CScriptVarLink(new CScriptVar()); - //printf("0x%08X for %s at %s\n", (unsigned int)a, l->tkStr.c_str(), l->getPosition().c_str()); - /* The parent if we're executing a method call */ - CScriptVar *parent = 0; - - if (execute && !a) { - /* Variable doesn't exist! JavaScript says we should create it - * (we won't add it here. This is done in the assignment operator)*/ - a = new CScriptVarLink(new CScriptVar(), l->tkStr); - } - l->match(LEX_ID); - while (l->tk=='(' || l->tk=='.' || l->tk=='[') { - if (l->tk=='(') { // ------------------------------------- Function Call - if (execute) { - if (!a->var->isFunction()) { - string errorMsg = "Expecting '"; - errorMsg = errorMsg + a->name + "' to be a function"; - throw new CScriptException(errorMsg.c_str()); - } - l->match('('); - // create a new symbol table entry for execution of this function - CScriptVar *functionRoot = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_FUNCTION); - if (parent) - functionRoot->addChildNoDup("this", parent); - // grab in all parameters - CScriptVarLink *v = a->var->firstChild; - while (v) { - CScriptVarLink *value = base(execute); - if (execute) { - if (value->var->isBasic()) { - // pass by value - functionRoot->addChild(v->name, value->var->deepCopy()); - } else { - // pass by reference - functionRoot->addChild(v->name, value->var); - } - } - CLEAN(value); - if (l->tk!=')') l->match(','); - v = v->nextSibling; - } - l->match(')'); - // setup a return variable - CScriptVarLink *returnVar = NULL; - // execute function! - // add the function's execute space to the symbol table so we can recurse - CScriptVarLink *returnVarLink = functionRoot->addChild(TINYJS_RETURN_VAR); - scopes.push_back(functionRoot); - - if (a->var->isNative()) { - ASSERT(a->var->jsCallback); - a->var->jsCallback(functionRoot, a->var->jsCallbackUserData); - } else { - /* we just want to execute the block, but something could - * have messed up and left us with the wrong ScriptLex, so - * we want to be careful here... */ - CScriptException *exception = 0; - CScriptLex *oldLex = l; - CScriptLex *newLex = new CScriptLex(a->var->getString()); - l = newLex; - try { - block(execute); - // because return will probably have called this, and set execute to false - execute = true; - } catch (CScriptException *e) { - exception = e; - } - delete newLex; - l = oldLex; - - if (exception) - throw exception; - } - - scopes.pop_back(); - /* get the real return var before we remove it from our function */ - returnVar = new CScriptVarLink(returnVarLink->var); - functionRoot->removeLink(returnVarLink); - delete functionRoot; - if (returnVar) - a = returnVar; - else - a = new CScriptVarLink(new CScriptVar()); - } else { - // function, but not executing - just parse args and be done - l->match('('); - while (l->tk != ')') { - CScriptVarLink *value = base(execute); - CLEAN(value); - if (l->tk!=')') l->match(','); - } - l->match(')'); - if (l->tk == '{') { - block(execute); - } - } - } else if (l->tk == '.') { // ------------------------------------- Record Access - l->match('.'); - if (execute) { - const string &name = l->tkStr; - CScriptVarLink *child = a->var->findChild(name); - if (!child) child = findInParentClasses(a->var, name); - if (!child) { - /* if we haven't found this defined yet, use the built-in - 'length' properly */ - if (a->var->isArray() && name == "length") { - int l = a->var->getArrayLength(); - child = new CScriptVarLink(new CScriptVar(l)); - } else if (a->var->isString() && name == "length") { - int l = a->var->getString().size(); - child = new CScriptVarLink(new CScriptVar(l)); - } else { - child = a->var->addChild(name); - } - } - parent = a->var; - a = child; - } - l->match(LEX_ID); - } else if (l->tk == '[') { // ------------------------------------- Array Access - l->match('['); - CScriptVarLink *index = expression(execute); - l->match(']'); - if (execute) { - CScriptVarLink *child = a->var->findChildOrCreate(index->var->getString()); - parent = a->var; - a = child; - } - CLEAN(index); - } else ASSERT(0); - } - return a; - } - if (l->tk==LEX_INT || l->tk==LEX_FLOAT) { - CScriptVar *a = new CScriptVar(l->tkStr, - ((l->tk==LEX_INT)?SCRIPTVAR_INTEGER:SCRIPTVAR_DOUBLE)); - l->match(l->tk); - return new CScriptVarLink(a); - } - if (l->tk==LEX_STR) { - CScriptVar *a = new CScriptVar(l->tkStr, SCRIPTVAR_STRING); - l->match(LEX_STR); - return new CScriptVarLink(a); - } - if (l->tk=='{') { - CScriptVar *contents = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT); - /* JSON-style object definition */ - l->match('{'); - while (l->tk != '}') { - string id = l->tkStr; - // we only allow strings or IDs on the left hand side of an initialisation - if (l->tk==LEX_STR) l->match(LEX_STR); - else l->match(LEX_ID); - l->match(':'); - if (execute) { - CScriptVarLink *a = base(execute); - contents->addChild(id, a->var); - CLEAN(a); - } - // no need to clean here, as it will definitely be used - if (l->tk != '}') l->match(','); - } - - l->match('}'); - return new CScriptVarLink(contents); - } - if (l->tk=='[') { - CScriptVar *contents = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_ARRAY); - /* JSON-style array */ - l->match('['); - int idx = 0; - while (l->tk != ']') { - if (execute) { - char idx_str[16]; // big enough for 2^32 - sprintf_s(idx_str, sizeof(idx_str), "%d",idx); - - CScriptVarLink *a = base(execute); - contents->addChild(idx_str, a->var); - CLEAN(a); - } - // no need to clean here, as it will definitely be used - if (l->tk != ']') l->match(','); - idx++; - } - l->match(']'); - return new CScriptVarLink(contents); - } - if (l->tk==LEX_R_FUNCTION) { - CScriptVarLink *funcVar = parseFunctionDefinition(); - if (funcVar->name != TINYJS_TEMP_NAME) - TRACE("Functions not defined at statement-level are not meant to have a name"); - return funcVar; - } - if (l->tk==LEX_R_NEW) { - // new -> create a new object - l->match(LEX_R_NEW); - const string &className = l->tkStr; - if (execute) { - CScriptVarLink *objClass = findInScopes(className); - if (!objClass) { - TRACE("%s is not a valid class name", className.c_str()); - return new CScriptVarLink(new CScriptVar()); - } - l->match(LEX_ID); - CScriptVar *obj = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT); - obj->addChild(TINYJS_PROTOTYPE_CLASS, objClass->var); - if (l->tk == '(') { - l->match('('); - l->match(')'); - } - // TODO: Object constructors - return new CScriptVarLink(obj); - } else { - l->match(LEX_ID); - if (l->tk == '(') { - l->match('('); - l->match(')'); - } - } - } - // Nothing we can do here... just hope it's the end... - l->match(LEX_EOF); - return 0; -} - -CScriptVarLink *CTinyJS::unary(bool &execute) { - CScriptVarLink *a; - if (l->tk=='!') { - l->match('!'); // binary not - a = factor(execute); - if (execute) { - CScriptVar zero(0); - CScriptVar *res = a->var->mathsOp(&zero, LEX_EQUAL); - CREATE_LINK(a, res); - } - } else - a = factor(execute); - return a; -} - -CScriptVarLink *CTinyJS::term(bool &execute) { - CScriptVarLink *a = unary(execute); - while (l->tk=='*' || l->tk=='/' || l->tk=='%') { - int op = l->tk; - l->match(l->tk); - CScriptVarLink *b = unary(execute); - if (execute) { - CScriptVar *res = a->var->mathsOp(b->var, op); - CREATE_LINK(a, res); - } - CLEAN(b); - } - return a; -} - -CScriptVarLink *CTinyJS::expression(bool &execute) { - bool negate = false; - if (l->tk=='-') { - l->match('-'); - negate = true; - } - CScriptVarLink *a = term(execute); - if (negate) { - CScriptVar zero(0); - CScriptVar *res = zero.mathsOp(a->var, '-'); - CREATE_LINK(a, res); - } - - while (l->tk=='+' || l->tk=='-' || - l->tk==LEX_PLUSPLUS || l->tk==LEX_MINUSMINUS) { - int op = l->tk; - l->match(l->tk); - if (op==LEX_PLUSPLUS || op==LEX_MINUSMINUS) { - if (execute) { - CScriptVar one(1); - CScriptVar *res = a->var->mathsOp(&one, op==LEX_PLUSPLUS ? '+' : '-'); - // in-place add/subtract - a->replaceWith(res); - } - } else { - CScriptVarLink *b = term(execute); - if (execute) { - // not in-place, so just replace - CScriptVar *res = a->var->mathsOp(b->var, op); - CREATE_LINK(a, res); - } - CLEAN(b); - } - } - return a; -} - -CScriptVarLink *CTinyJS::condition(bool &execute) { - CScriptVarLink *a = expression(execute); - CScriptVarLink *b; - while (l->tk==LEX_EQUAL || l->tk==LEX_NEQUAL || - l->tk==LEX_TYPEEQUAL || l->tk==LEX_NTYPEEQUAL || - l->tk==LEX_LEQUAL || l->tk==LEX_GEQUAL || - l->tk=='<' || l->tk=='>') { - int op = l->tk; - l->match(l->tk); - b = expression(execute); - if (execute) { - CScriptVar *res = a->var->mathsOp(b->var, op); - CREATE_LINK(a,res); - } - CLEAN(b); - } - return a; -} - -CScriptVarLink *CTinyJS::logic(bool &execute) { - CScriptVarLink *a = condition(execute); - CScriptVarLink *b; - while (l->tk=='&' || l->tk=='|' || l->tk=='^' || l->tk==LEX_ANDAND || l->tk==LEX_OROR) { - bool noexecute = false; - int op = l->tk; - l->match(l->tk); - bool shortCircuit = false; - bool boolean = false; - // if we have short-circuit ops, then if we know the outcome - // we don't bother to execute the other op. Even if not - // we need to tell mathsOp it's an & or | - if (op==LEX_ANDAND) { - op = '&'; - shortCircuit = !a->var->getBool(); - boolean = true; - } else if (op==LEX_OROR) { - op = '|'; - shortCircuit = a->var->getBool(); - boolean = true; - } - b = condition(shortCircuit ? noexecute : execute); - if (execute && !shortCircuit) { - if (boolean) { - CScriptVar *newa = new CScriptVar(a->var->getBool()); - CScriptVar *newb = new CScriptVar(b->var->getBool()); - CREATE_LINK(a, newa); - CREATE_LINK(b, newb); - } - CScriptVar *res = a->var->mathsOp(b->var, op); - CREATE_LINK(a, res); - } - CLEAN(b); - } - return a; -} - -CScriptVarLink *CTinyJS::base(bool &execute) { - CScriptVarLink *lhs = logic(execute); - if (l->tk=='=' || l->tk==LEX_PLUSEQUAL || l->tk==LEX_MINUSEQUAL) { - /* If we're assigning to this and we don't have a parent, - * add it to the symbol table root as per JavaScript. */ - if (execute && !lhs->owned) { - if (lhs->name.length()>0) { - CScriptVarLink *realLhs = root->addChildNoDup(lhs->name, lhs->var); - CLEAN(lhs); - lhs = realLhs; - } else - TRACE("Trying to assign to an un-named type\n"); - } - - int op = l->tk; - l->match(l->tk); - CScriptVarLink *rhs = base(execute); - if (execute) { - if (op=='=') { - lhs->replaceWith(rhs); - } else if (op==LEX_PLUSEQUAL) { - CScriptVar *res = lhs->var->mathsOp(rhs->var, '+'); - lhs->replaceWith(res); - } else if (op==LEX_MINUSEQUAL) { - CScriptVar *res = lhs->var->mathsOp(rhs->var, '-'); - lhs->replaceWith(res); - } else ASSERT(0); - } - CLEAN(rhs); - } - return lhs; -} - -void CTinyJS::block(bool &execute) { - l->match('{'); - if (execute) { - while (l->tk && l->tk!='}') - statement(execute); - l->match('}'); - } else { - // fast skip of blocks - int brackets = 1; - while (l->tk && brackets) { - if (l->tk == '{') brackets++; - if (l->tk == '}') brackets--; - l->match(l->tk); - } - } - -} - -void CTinyJS::statement(bool &execute) { - if (l->tk==LEX_ID || - l->tk==LEX_INT || - l->tk==LEX_FLOAT || - l->tk==LEX_STR || - l->tk=='-') { - /* Execute a simple statement that only contains basic arithmetic... */ - CLEAN(base(execute)); - l->match(';'); - } else if (l->tk=='{') { - /* A block of code */ - block(execute); - } else if (l->tk==';') { - /* Empty statement - to allow things like ;;; */ - l->match(';'); - } else if (l->tk==LEX_R_VAR) { - /* variable creation. TODO - we need a better way of parsing the left - * hand side. Maybe just have a flag called can_create_var that we - * set and then we parse as if we're doing a normal equals.*/ - l->match(LEX_R_VAR); - CScriptVarLink *a = 0; - if (execute) - a = scopes.back()->findChildOrCreate(l->tkStr); - l->match(LEX_ID); - // now do stuff defined with dots - while (l->tk == '.') { - l->match('.'); - if (execute) { - CScriptVarLink *lastA = a; - a = lastA->var->findChildOrCreate(l->tkStr); - } - l->match(LEX_ID); - } - // sort out initialiser - if (l->tk == '=') { - l->match('='); - CScriptVarLink *var = base(execute); - if (execute) - a->replaceWith(var); - CLEAN(var); - } - l->match(';'); - } else if (l->tk==LEX_R_IF) { - l->match(LEX_R_IF); - l->match('('); - CScriptVarLink *var = base(execute); - l->match(')'); - bool cond = execute && var->var->getBool(); - CLEAN(var); - bool noexecute = false; // because we need to be abl;e to write to it - statement(cond ? execute : noexecute); - if (l->tk==LEX_R_ELSE) { - l->match(LEX_R_ELSE); - statement(cond ? noexecute : execute); - } - } else if (l->tk==LEX_R_WHILE) { - // We do repetition by pulling out the string representing our statement - // there's definitely some opportunity for optimisation here - l->match(LEX_R_WHILE); - l->match('('); - int whileCondStart = l->tokenStart; - bool noexecute = false; - CScriptVarLink *cond = base(execute); - bool loopCond = execute && cond->var->getBool(); - CLEAN(cond); - CScriptLex *whileCond = l->getSubLex(whileCondStart); - l->match(')'); - int whileBodyStart = l->tokenStart; - statement(loopCond ? execute : noexecute); - CScriptLex *whileBody = l->getSubLex(whileBodyStart); - CScriptLex *oldLex = l; - int loopCount = TINYJS_LOOP_MAX_ITERATIONS; - while (loopCond && loopCount-->0) { - whileCond->reset(); - l = whileCond; - cond = base(execute); - loopCond = execute && cond->var->getBool(); - CLEAN(cond); - if (loopCond) { - whileBody->reset(); - l = whileBody; - statement(execute); - } - } - l = oldLex; - delete whileCond; - delete whileBody; - - if (loopCount<=0) { - root->trace(); - TRACE("WHILE Loop exceeded %d iterations at %s\n", TINYJS_LOOP_MAX_ITERATIONS, l->getPosition().c_str()); - throw new CScriptException("LOOP_ERROR"); - } - } else if (l->tk==LEX_R_FOR) { - l->match(LEX_R_FOR); - l->match('('); - statement(execute); // initialisation - //l->match(';'); - int forCondStart = l->tokenStart; - bool noexecute = false; - CScriptVarLink *cond = base(execute); // condition - bool loopCond = execute && cond->var->getBool(); - CLEAN(cond); - CScriptLex *forCond = l->getSubLex(forCondStart); - l->match(';'); - int forIterStart = l->tokenStart; - CLEAN(base(noexecute)); // iterator - CScriptLex *forIter = l->getSubLex(forIterStart); - l->match(')'); - int forBodyStart = l->tokenStart; - statement(loopCond ? execute : noexecute); - CScriptLex *forBody = l->getSubLex(forBodyStart); - CScriptLex *oldLex = l; - if (loopCond) { - forIter->reset(); - l = forIter; - CLEAN(base(execute)); - } - int loopCount = TINYJS_LOOP_MAX_ITERATIONS; - while (execute && loopCond && loopCount-->0) { - forCond->reset(); - l = forCond; - cond = base(execute); - loopCond = cond->var->getBool(); - CLEAN(cond); - if (execute && loopCond) { - forBody->reset(); - l = forBody; - statement(execute); - } - if (execute && loopCond) { - forIter->reset(); - l = forIter; - CLEAN(base(execute)); - } - } - l = oldLex; - delete forCond; - delete forIter; - delete forBody; - if (loopCount<=0) { - root->trace(); - TRACE("FOR Loop exceeded %d iterations at %s\n", TINYJS_LOOP_MAX_ITERATIONS, l->getPosition().c_str()); - throw new CScriptException("LOOP_ERROR"); - } - } else if (l->tk==LEX_R_RETURN) { - l->match(LEX_R_RETURN); - CScriptVarLink *result = 0; - if (l->tk != ';') - result = base(execute); - if (execute) { - CScriptVarLink *resultVar = scopes.back()->findChild(TINYJS_RETURN_VAR); - if (resultVar) - resultVar->replaceWith(result); - else - TRACE("RETURN statement, but not in a function.\n"); - execute = false; - } - CLEAN(result); - l->match(';'); - } else if (l->tk==LEX_R_FUNCTION) { - CScriptVarLink *funcVar = parseFunctionDefinition(); - if (execute) { - if (funcVar->name == TINYJS_TEMP_NAME) - TRACE("Functions defined at statement-level are meant to have a name\n"); - else - scopes.back()->addChildNoDup(funcVar->name, funcVar->var); - } - CLEAN(funcVar); - } else l->match(LEX_EOF); -} - -/// Get the value of the given variable, or return 0 -const string *CTinyJS::getVariable(const string &path) { - // traverse path - size_t prevIdx = 0; - size_t thisIdx = path.find('.'); - if (thisIdx == string::npos) thisIdx = path.length(); - CScriptVar *var = root; - while (var && prevIdxfindChild(el); - var = varl?varl->var:0; - prevIdx = thisIdx+1; - thisIdx = path.find('.', prevIdx); - if (thisIdx == string::npos) thisIdx = path.length(); - } - // return result - if (var) - return &var->getString(); - else - return 0; -} - -/// Finds a child, looking recursively up the scopes -CScriptVarLink *CTinyJS::findInScopes(const std::string &childName) { - for (int s=scopes.size()-1;s>=0;s--) { - CScriptVarLink *v = scopes[s]->findChild(childName); - if (v) return v; - } - return NULL; - -} - -/// Look up in any parent classes of the given object -CScriptVarLink *CTinyJS::findInParentClasses(CScriptVar *object, const std::string &name) { - // Look for links to actual parent classes - CScriptVarLink *parentClass = object->findChild(TINYJS_PROTOTYPE_CLASS); - while (parentClass) { - CScriptVarLink *implementation = parentClass->var->findChild(name); - if (implementation) return implementation; - parentClass = parentClass->var->findChild(TINYJS_PROTOTYPE_CLASS); - } - // else fake it for strings and finally objects - if (object->isString()) { - CScriptVarLink *implementation = stringClass->findChild(name); - if (implementation) return implementation; - } - if (object->isArray()) { - CScriptVarLink *implementation = arrayClass->findChild(name); - if (implementation) return implementation; - } - CScriptVarLink *implementation = objectClass->findChild(name); - if (implementation) return implementation; - - return 0; -} +/* + * TinyJS + * + * A single-file Javascript-alike engine + * + * Authored By Gordon Williams + * + * Copyright (C) 2009 Pur3 Ltd + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* Version 0.1 : (gw) First published on Google Code + Version 0.11 : Making sure the 'root' variable never changes + 'symbol_base' added for the current base of the sybmbol table + Version 0.12 : Added findChildOrCreate, changed string passing to use references + Fixed broken string encoding in getJSString() + Removed getInitCode and added getJSON instead + Added nil + Added rough JSON parsing + Improved example app + Version 0.13 : Added tokenEnd/tokenLastEnd to lexer to avoid parsing whitespace + Ability to define functions without names + Can now do "var mine = function(a,b) { ... };" + Slightly better 'trace' function + Added findChildOrCreateByPath function + Added simple test suite + Added skipping of blocks when not executing + Version 0.14 : Added parsing of more number types + Added parsing of string defined with ' + Changed nil to null as per spec, added 'undefined' + Now set variables with the correct scope, and treat unknown + as 'undefined' rather than failing + Added proper (I hope) handling of null and undefined + Added === check + Version 0.15 : Fix for possible memory leaks + Version 0.16 : Removal of un-needed findRecursive calls + symbol_base removed and replaced with 'scopes' stack + Added reference counting a proper tree structure + (Allowing pass by reference) + Allowed JSON output to output IDs, not strings + Added get/set for array indices + Changed Callbacks to include user data pointer + Added some support for objects + Added more Java-esque builtin functions + Version 0.17 : Now we don't deepCopy the parent object of the class + Added JSON.stringify and eval() + Nicer JSON indenting + Fixed function output in JSON + Added evaluateComplex + Fixed some reentrancy issues with evaluate/execute + Version 0.18 : Fixed some issues with code being executed when it shouldn't + Version 0.19 : Added array.length + Changed '__parent' to 'prototype' to bring it more in line with javascript + Version 0.20 : Added '%' operator + Version 0.21 : Added array type + String.length() no more - now String.length + Added extra constructors to reduce confusion + Fixed checks against undefined + Forked to 42tiny-js by Armin diedering + R3 Added boolean datatype (in string operators its shown as 'true'/'false' but in math as '1'/'0' + Added '~' operator + Added bit-shift operators '<<' '>>' + Added assignment operators '<<=' '>>=' '|=' '&=' '^=' + Addet comma operator like this 'var i=1,j,k=9;' or 'for(i=0,j=12; i<10; i++, j++)' works + Added Conditional operator ( ? : ) + Added automatic cast from doubles to integer e.G. by logic and binary operators + Added pre-increment/-decrement like '++i'/'--i' operator + Fixed post-increment/decrement now returns the previous value + Fixed throws an Error when invalid using of post/pre-increment/decrement (like this 5++ works no more) + Fixed memoryleak (unref arrayClass at deconstructor of JS CTinyJS) + Fixed unary operator handling (like this '-~-5' works now) + Fixed operator prority order -> ',' + -> '=' '+=' '-=' '<<=' '>>=' '&=' '^=' '|=' + -> '? :' -> '||' -> '&&' -> '|' -> '^' -> '&' + -> ['==' '===' '!=' '!=='] + -> [ '<' '<=' '=>' '>'] + -> ['<<' '>>'] -> [ '*' '/' '%'] + -> ['!' '~' '-' '++' '--'] + Added do-while-loop ( do .... while(..); ) + Added break and continue statements for loops + R4 Changed "owned"-member of CScriptVarLink from bool to a pointer of CScriptVarowned + Added some Visual Studio Preprocessor stuff + Added now alowed stuff like this (function (v) {print(v);})(12); + Remove unneeded and wrong deepCopy by assignment operator '=' + + + NOTE: This doesn't support constructors for objects + Recursive loops of data such as a.foo = a; fail to be garbage collected + 'length' cannot be set + There is no ternary operator implemented yet + */ + + +#ifdef _MSC_VER +# include "targetver.h" +# include +#endif +#include +#include +#include +#include +#include +#include +#include "TinyJS.h" + +#ifdef _DEBUG +# ifdef _MSC_VER +# define new DEBUG_NEW +# else +# define DEBUG_MEMORY 1 +# endif +#endif + + +#ifndef ASSERT +# define ASSERT(X) assert(X) +#endif +/* Frees the given link IF it isn't owned by anything else */ +#define CLEAN(x) do{ CScriptVarLink *__v = x; if (__v && !__v->owned) { delete __v; } }while(0) +/* Create a LINK to point to VAR and free the old link. + * BUT this is more clever - it tries to keep the old link if it's not owned to save allocations */ +#define CREATE_LINK(LINK, VAR) { if (!LINK || LINK->owned) LINK = new CScriptVarLink(VAR); else LINK->replaceWith(VAR); } + + +using namespace std; + +#ifdef __GNUC__ +#define vsprintf_s vsnprintf +#define sprintf_s snprintf +#define _strdup strdup +#endif + +// ----------------------------------------------------------------------------------- Memory Debug + +//#define DEBUG_MEMORY 1 + +#if DEBUG_MEMORY + +vector allocatedVars; +vector allocatedLinks; + +void mark_allocated(CScriptVar *v) { + allocatedVars.push_back(v); +} + +void mark_deallocated(CScriptVar *v) { + for (size_t i=0;igetRefs()); + allocatedVars[i]->trace(" "); + } + for (size_t i=0;iname.c_str(), allocatedLinks[i]->var->getRefs()); + allocatedLinks[i]->var->trace(" "); + } + allocatedVars.clear(); + allocatedLinks.clear(); +} +#endif + +// ----------------------------------------------------------------------------------- Utils +bool isWhitespace(char ch) { + return (ch==' ') || (ch=='\t') || (ch=='\n') || (ch=='\r'); +} + +bool isNumeric(char ch) { + return (ch>='0') && (ch<='9'); +} +bool isNumber(const string &str) { + for (size_t i=0;i='0') && (ch<='9')) || + ((ch>='a') && (ch<='f')) || + ((ch>='A') && (ch<='F')); +} +bool isAlpha(char ch) { + return ((ch>='a') && (ch<='z')) || ((ch>='A') && (ch<='Z')) || ch=='_'; +} + +bool isIDString(const char *s) { + if (!isAlpha(*s)) + return false; + while (*s) { + if (!(isAlpha(*s) || isNumeric(*s))) + return false; + s++; + } + return true; +} + +void replace(string &str, char textFrom, const char *textTo) { + int sLen = strlen(textTo); + size_t p = str.find(textFrom); + while (p != string::npos) { + str = str.substr(0, p) + textTo + str.substr(p+1); + p = str.find(textFrom, p+sLen); + } +} + +/// convert the given string into a quoted string suitable for javascript +std::string getJSString(const std::string &str) { + std::string nStr = str; + for (size_t i=0;idata; + dataOwned = false; + dataStart = startChar; + dataEnd = endChar; + reset(); +} + +CScriptLex::~CScriptLex(void) +{ + if (dataOwned) + free((void*)data); +} + +void CScriptLex::reset() { + dataPos = dataStart; + tokenStart = 0; + tokenEnd = 0; + tokenLastEnd = 0; + tk = 0; + tkStr = ""; + getNextCh(); + getNextCh(); + getNextToken(); +} + +void CScriptLex::match(int expected_tk) { + if (tk!=expected_tk) { + ostringstream errorString; + errorString << "Got " << getTokenStr(tk) << " expected " << getTokenStr(expected_tk) + << " at " << getPosition(tokenStart) << " in '" << data << "'"; + throw new CScriptException(errorString.str()); + } + getNextToken(); +} + +string CScriptLex::getTokenStr(int token) { + if (token>32 && token<128) { + char buf[4] = "' '"; + buf[1] = (char)token; + return buf; + } + switch (token) { + case LEX_EOF : return "EOF"; + case LEX_ID : return "ID"; + case LEX_INT : return "INT"; + case LEX_FLOAT : return "FLOAT"; + case LEX_STR : return "STRING"; + case LEX_EQUAL : return "=="; + case LEX_TYPEEQUAL : return "==="; + case LEX_NEQUAL : return "!="; + case LEX_NTYPEEQUAL : return "!=="; + case LEX_LEQUAL : return "<="; + case LEX_LSHIFT : return "<<"; + case LEX_LSHIFTEQUAL : return "<<="; + case LEX_GEQUAL : return ">="; + case LEX_RSHIFT : return ">>"; + case LEX_RSHIFTEQUAL : return ">>="; + case LEX_PLUSEQUAL : return "+="; + case LEX_MINUSEQUAL : return "-="; + case LEX_PLUSPLUS : return "++"; + case LEX_MINUSMINUS : return "--"; + case LEX_ANDEQUAL : return "&="; + case LEX_ANDAND : return "&&"; + case LEX_OREQUAL : return "|="; + case LEX_OROR : return "||"; + case LEX_XOREQUAL : return "^="; + // reserved words + case LEX_R_IF : return "if"; + case LEX_R_ELSE : return "else"; + case LEX_R_DO : return "do"; + case LEX_R_WHILE : return "while"; + case LEX_R_FOR : return "for"; + case LEX_R_BREAK : return "break"; + case LEX_R_CONTINUE : return "continue"; + case LEX_R_FUNCTION : return "function"; + case LEX_R_RETURN : return "return"; + case LEX_R_VAR : return "var"; + case LEX_R_TRUE : return "true"; + case LEX_R_FALSE : return "false"; + case LEX_R_NULL : return "null"; + case LEX_R_UNDEFINED : return "undefined"; + case LEX_R_NEW : return "new"; + } + + ostringstream msg; + msg << "?[" << token << "]"; + return msg.str(); +} + +void CScriptLex::getNextCh() { + currCh = nextCh; + if (dataPos < dataEnd) + nextCh = data[dataPos]; + else + nextCh = 0; + dataPos++; +} + +void CScriptLex::getNextToken() { + tk = LEX_EOF; + tkStr.clear(); + while (currCh && isWhitespace(currCh)) getNextCh(); + // newline comments + if (currCh=='/' && nextCh=='/') { + while (currCh && currCh!='\n') getNextCh(); + getNextCh(); + getNextToken(); + return; + } + // block comments + if (currCh=='/' && nextCh=='*') { + while (currCh && (currCh!='*' || nextCh!='/')) getNextCh(); + getNextCh(); + getNextCh(); + getNextToken(); + return; + } + // record beginning of this token + tokenStart = dataPos-2; + // tokens + if (isAlpha(currCh)) { // IDs + while (isAlpha(currCh) || isNumeric(currCh)) { + tkStr += currCh; + getNextCh(); + } + tk = LEX_ID; + if (tkStr=="if") tk = LEX_R_IF; + else if (tkStr=="else") tk = LEX_R_ELSE; + else if (tkStr=="do") tk = LEX_R_DO; + else if (tkStr=="while") tk = LEX_R_WHILE; + else if (tkStr=="for") tk = LEX_R_FOR; + else if (tkStr=="break") tk = LEX_R_BREAK; + else if (tkStr=="continue") tk = LEX_R_CONTINUE; + else if (tkStr=="function") tk = LEX_R_FUNCTION; + else if (tkStr=="return") tk = LEX_R_RETURN; + else if (tkStr=="var") tk = LEX_R_VAR; + else if (tkStr=="true") tk = LEX_R_TRUE; + else if (tkStr=="false") tk = LEX_R_FALSE; + else if (tkStr=="null") tk = LEX_R_NULL; + else if (tkStr=="undefined") tk = LEX_R_UNDEFINED; + else if (tkStr=="new") tk = LEX_R_NEW; + } else if (isNumeric(currCh)) { // Numbers + bool isHex = false; + if (currCh=='0') { tkStr += currCh; getNextCh(); } + if (currCh=='x') { + isHex = true; + tkStr += currCh; getNextCh(); + } + tk = LEX_INT; + while (isNumeric(currCh) || (isHex && isHexadecimal(currCh))) { + tkStr += currCh; + getNextCh(); + } + if (!isHex && currCh=='.') { + tk = LEX_FLOAT; + tkStr += '.'; + getNextCh(); + while (isNumeric(currCh)) { + tkStr += currCh; + getNextCh(); + } + } + // do fancy e-style floating point + if (!isHex && currCh=='e') { + tk = LEX_FLOAT; + tkStr += currCh; getNextCh(); + if (currCh=='-') { tkStr += currCh; getNextCh(); } + while (isNumeric(currCh)) { + tkStr += currCh; getNextCh(); + } + } + } else if (currCh=='"') { + // strings... + getNextCh(); + while (currCh && currCh!='"') { + if (currCh == '\\') { + getNextCh(); + switch (currCh) { + case 'n' : tkStr += '\n'; break; + case '"' : tkStr += '"'; break; + case '\\' : tkStr += '\\'; break; + default: tkStr += currCh; + } + } else { + tkStr += currCh; + } + getNextCh(); + } + getNextCh(); + tk = LEX_STR; + } else if (currCh=='\'') { + // strings again... + getNextCh(); + while (currCh && currCh!='\'') { + if (currCh == '\\') { + getNextCh(); + switch (currCh) { + case 'n' : tkStr += '\n'; break; + case '\'' : tkStr += '\''; break; + case '\\' : tkStr += '\\'; break; + default: tkStr += currCh; + } + } else { + tkStr += currCh; + } + getNextCh(); + } + getNextCh(); + tk = LEX_STR; + } else { + // single chars + tk = currCh; + if (currCh) getNextCh(); + if (tk=='=' && currCh=='=') { // == + tk = LEX_EQUAL; + getNextCh(); + if (currCh=='=') { // === + tk = LEX_TYPEEQUAL; + getNextCh(); + } + } else if (tk=='!' && currCh=='=') { // != + tk = LEX_NEQUAL; + getNextCh(); + if (currCh=='=') { // !== + tk = LEX_NTYPEEQUAL; + getNextCh(); + } + } else if (tk=='<' && currCh=='=') { + tk = LEX_LEQUAL; + getNextCh(); + } else if (tk=='<' && currCh=='<') { + tk = LEX_LSHIFT; + getNextCh(); + if (currCh=='=') { // <<= + tk = LEX_LSHIFTEQUAL; + getNextCh(); + } + } else if (tk=='>' && currCh=='=') { + tk = LEX_GEQUAL; + getNextCh(); + } else if (tk=='>' && currCh=='>') { + tk = LEX_RSHIFT; + getNextCh(); + if (currCh=='=') { // <<= + tk = LEX_RSHIFTEQUAL; + getNextCh(); + } + } else if (tk=='+' && currCh=='=') { + tk = LEX_PLUSEQUAL; + getNextCh(); + } else if (tk=='-' && currCh=='=') { + tk = LEX_MINUSEQUAL; + getNextCh(); + } else if (tk=='+' && currCh=='+') { + tk = LEX_PLUSPLUS; + getNextCh(); + } else if (tk=='-' && currCh=='-') { + tk = LEX_MINUSMINUS; + getNextCh(); + } else if (tk=='&' && currCh=='=') { + tk = LEX_ANDEQUAL; + getNextCh(); + } else if (tk=='&' && currCh=='&') { + tk = LEX_ANDAND; + getNextCh(); + } else if (tk=='|' && currCh=='=') { + tk = LEX_OREQUAL; + getNextCh(); + } else if (tk=='|' && currCh=='|') { + tk = LEX_OROR; + getNextCh(); + } else if (tk=='^' && currCh=='=') { + tk = LEX_XOREQUAL; + getNextCh(); + } + } + /* This isn't quite right yet */ + tokenLastEnd = tokenEnd; + tokenEnd = dataPos-3; +} + +string CScriptLex::getSubString(int lastPosition) { + int lastCharIdx = tokenLastEnd+1; + if (lastCharIdx < dataEnd) { + /* save a memory alloc by using our data array to create the + substring */ + char old = data[lastCharIdx]; + data[lastCharIdx] = 0; + std::string value = &data[lastPosition]; + data[lastCharIdx] = old; + return value; + } else { + return std::string(&data[lastPosition]); + } +} + + +CScriptLex *CScriptLex::getSubLex(int lastPosition) { + int lastCharIdx = tokenLastEnd+1; + if (lastCharIdx < dataEnd) + return new CScriptLex( this, lastPosition, lastCharIdx); + else + return new CScriptLex( this, lastPosition, dataEnd ); +} + +string CScriptLex::getPosition(int pos) { + if (pos<0) pos=tokenLastEnd; + int line = 1,col = 1; + for (int i=0;iname = name; + this->nextSibling = 0; + this->prevSibling = 0; + this->var = var->ref(); + this->owned = NULL; +} + +CScriptVarLink::CScriptVarLink(const CScriptVarLink &link) { + // Copy constructor +#if DEBUG_MEMORY + mark_allocated(this); +#endif + this->name = link.name; + this->nextSibling = 0; + this->prevSibling = 0; + this->var = link.var->ref(); + this->owned = NULL; +} + +CScriptVarLink::~CScriptVarLink() { +#if DEBUG_MEMORY + mark_deallocated(this); +#endif + var->unref(); +} + +void CScriptVarLink::replaceWith(CScriptVar *newVar) { + CScriptVar *oldVar = var; + var = newVar->ref(); + oldVar->unref(); +} + +void CScriptVarLink::replaceWith(CScriptVarLink *newVar) { + if (newVar) + replaceWith(newVar->var); + else + replaceWith(new CScriptVar()); +} + +// ----------------------------------------------------------------------------------- CSCRIPTVAR + +CScriptVar::CScriptVar() { + refs = 0; + __proto__ = NULL; +#if DEBUG_MEMORY + mark_allocated(this); +#endif + init(); + flags = SCRIPTVAR_UNDEFINED; +} + +CScriptVar::CScriptVar(const string &str) { + refs = 0; + __proto__ = NULL; +#if DEBUG_MEMORY + mark_allocated(this); +#endif + init(); + flags = SCRIPTVAR_STRING; + data = str; +} + + +CScriptVar::CScriptVar(const string &varData, int varFlags) { + refs = 0; + __proto__ = NULL; +#if DEBUG_MEMORY + mark_allocated(this); +#endif + init(); + flags = varFlags; + if (varFlags & SCRIPTVAR_INTEGER) { + intData = strtol(varData.c_str(),0,0); + } else if (varFlags & SCRIPTVAR_DOUBLE) { + doubleData = strtod(varData.c_str(),0); + } else + data = varData; +} + +CScriptVar::CScriptVar(double val) { + refs = 0; + __proto__ = NULL; +#if DEBUG_MEMORY + mark_allocated(this); +#endif + init(); + setDouble(val); +} + +CScriptVar::CScriptVar(int val) { + refs = 0; + __proto__ = NULL; +#if DEBUG_MEMORY + mark_allocated(this); +#endif + init(); + setInt(val); +} + +CScriptVar::CScriptVar(bool val) { + refs = 0; + __proto__ = NULL; +#if DEBUG_MEMORY + mark_allocated(this); +#endif + init(); + setBool(val); +} + +CScriptVar::~CScriptVar(void) { +#if DEBUG_MEMORY + mark_deallocated(this); +#endif + removeAllChildren(); + if(flags & SCRIPTVAR_NATIVE_MFNC) + delete jsCallbackClass; +} + +void CScriptVar::init() { + firstChild = 0; + lastChild = 0; + flags = 0; + jsCallback = 0; + jsCallbackUserData = 0; + data = TINYJS_BLANK_DATA; + intData = 0; + doubleData = 0; +} + +CScriptVar *CScriptVar::getReturnVar() { + return getParameter(TINYJS_RETURN_VAR); +} + +void CScriptVar::setReturnVar(CScriptVar *var) { + findChildOrCreate(TINYJS_RETURN_VAR)->replaceWith(var); +} + + +CScriptVar *CScriptVar::getParameter(const std::string &name) { + return findChildOrCreate(name)->var; +} + +CScriptVarLink *CScriptVar::findChild(const string &childName) { + CScriptVarLink *v = firstChild; + while (v) { + if (v->name.compare(childName)==0) + return v; + v = v->nextSibling; + } + return 0; +} + +CScriptVarLink *CScriptVar::findChildOrCreate(const string &childName, int varFlags) { + CScriptVarLink *l = findChild(childName); + if (l) return l; + + return addChild(childName, new CScriptVar(TINYJS_BLANK_DATA, varFlags)); +} + +CScriptVarLink *CScriptVar::findChildOrCreateByPath(const std::string &path) { + size_t p = path.find('.'); + if (p == string::npos) + return findChildOrCreate(path); + + return findChildOrCreate(path.substr(0,p), SCRIPTVAR_OBJECT)->var-> + findChildOrCreateByPath(path.substr(p+1)); +} + +CScriptVarLink *CScriptVar::addChild(const std::string &childName, CScriptVar *child) { + if (isUndefined()) { + flags = SCRIPTVAR_OBJECT; + } + // if no child supplied, create one + if (!child) + child = new CScriptVar(); + + CScriptVarLink *link = new CScriptVarLink(child, childName); + link->owned = this; + if (lastChild) { + lastChild->nextSibling = link; + link->prevSibling = lastChild; + lastChild = link; + } else { + firstChild = link; + lastChild = link; + } + return link; +} + +CScriptVarLink *CScriptVar::addChildNoDup(const std::string &childName, CScriptVar *child) { + // if no child supplied, create one + if (!child) + child = new CScriptVar(); + + CScriptVarLink *v = findChild(childName); + if (v) { + v->replaceWith(child); + } else { + v = addChild(childName, child); + } + + return v; +} + +void CScriptVar::removeChild(CScriptVar *child) { + CScriptVarLink *link = firstChild; + while (link) { + if (link->var == child) + break; + link = link->nextSibling; + } + ASSERT(link); + removeLink(link); +} + +void CScriptVar::removeLink(CScriptVarLink *link) { + if (!link) return; + if (link->nextSibling) + link->nextSibling->prevSibling = link->prevSibling; + if (link->prevSibling) + link->prevSibling->nextSibling = link->nextSibling; + if (lastChild == link) + lastChild = link->prevSibling; + if (firstChild == link) + firstChild = link->nextSibling; + delete link; +} + +void CScriptVar::removeAllChildren() { + CScriptVarLink *c = firstChild; + while (c) { + CScriptVarLink *t = c->nextSibling; + delete c; + c = t; + } + firstChild = 0; + lastChild = 0; +} + +CScriptVar *CScriptVar::getArrayIndex(int idx) { + char sIdx[64]; + sprintf_s(sIdx, sizeof(sIdx), "%d", idx); + CScriptVarLink *link = findChild(sIdx); + if (link) return link->var; + else return new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_NULL); // undefined +} + +void CScriptVar::setArrayIndex(int idx, CScriptVar *value) { + char sIdx[64]; + sprintf_s(sIdx, sizeof(sIdx), "%d", idx); + CScriptVarLink *link = findChild(sIdx); + + if (link) { + if (value->isUndefined()) + removeLink(link); + else + link->replaceWith(value); + } else { + if (!value->isUndefined()) + addChild(sIdx, value); + } +} + +int CScriptVar::getArrayLength() { + int highest = -1; + if (!isArray()) return 0; + + CScriptVarLink *link = firstChild; + while (link) { + if (isNumber(link->name)) { + int val = atoi(link->name.c_str()); + if (val > highest) highest = val; + } + link = link->nextSibling; + } + return highest+1; +} + +int CScriptVar::getChildren() { + int n = 0; + CScriptVarLink *link = firstChild; + while (link) { + n++; + link = link->nextSibling; + } + return n; +} + +int CScriptVar::getInt() { + /* strtol understands about hex and octal */ + if (isInt()) return intData; +// if (isNull()) return 0; +// if (isUndefined()) return 0; + if (isDouble()) return (int)doubleData; + return 0; +} + +bool CScriptVar::getBool() { + if (isInt()) return intData!=0; + if (isNull()) return 0; + if (isUndefined()) return 0; + if (isDouble()) return doubleData!=0; + if (isString()) return data.length()!=0; + return 0; +} + +double CScriptVar::getDouble() { + if (isDouble()) return doubleData; + if (isInt() || isBool()) intData; + if (isNull()) return 0; + if (isUndefined()) return 0; + return 0; /* or NaN? */ +} + +const string &CScriptVar::getString() { + /* Because we can't return a string that is generated on demand. + * I should really just use char* :) */ + static string s_null = "null"; + static string s_undefined = "undefined"; + static string s_true = "true"; + static string s_false = "false"; + if (isBool()) return getInt() ? s_true : s_false; + if (isInt()) { + char buffer[32]; + sprintf_s(buffer, sizeof(buffer), "%ld", intData); + data = buffer; + return data; + } + if (isDouble()) { + char buffer[32]; + sprintf_s(buffer, sizeof(buffer), "%lf", doubleData); + data = buffer; + return data; + } + if (isNull()) return s_null; + if (isUndefined()) return s_undefined; + // are we just a string here? + return data; +} + +void CScriptVar::setInt(int val) { + flags = (flags&~SCRIPTVAR_VARTYPEMASK) | SCRIPTVAR_INTEGER; + intData = val; + doubleData = 0; + data = TINYJS_BLANK_DATA; +} + +void CScriptVar::setBool(bool val) { + flags = (flags&~SCRIPTVAR_VARTYPEMASK) | SCRIPTVAR_BOOLEAN; + intData = val?1:0; + doubleData = 0; + data = TINYJS_BLANK_DATA; +} + +void CScriptVar::setDouble(double val) { + flags = (flags&~SCRIPTVAR_VARTYPEMASK) | SCRIPTVAR_DOUBLE; + doubleData = val; + intData = 0; + data = TINYJS_BLANK_DATA; +} + +void CScriptVar::setString(const string &str) { + // name sure it's not still a number or integer + flags = (flags&~SCRIPTVAR_VARTYPEMASK) | SCRIPTVAR_STRING; + data = str; + intData = 0; + doubleData = 0; +} + +void CScriptVar::setUndefined() { + // name sure it's not still a number or integer + flags = (flags&~SCRIPTVAR_VARTYPEMASK) | SCRIPTVAR_UNDEFINED; + data = TINYJS_BLANK_DATA; + intData = 0; + doubleData = 0; + removeAllChildren(); +} + +template +CScriptVar *DoMaths(CScriptVar *a, CScriptVar *b, int op) +{ + int dai = a->getInt();// use int when needed + int dbi = b->getInt(); + T da = (T)(a->isDouble()?a->getDouble():dai); + T db = (T)(b->isDouble()?b->getDouble():dbi); + switch (op) { + case '+': return new CScriptVar(da+db); + case '-': return new CScriptVar(da-db); + case '*': return new CScriptVar(da*db); + case '/': return new CScriptVar(da/db); + case '&': return new CScriptVar(dai&dbi); + case '|': return new CScriptVar(dai|dbi); + case '^': return new CScriptVar(dai^dbi); + case '%': return new CScriptVar(dai%dbi); + case '~': return new CScriptVar(~dai); + case LEX_LSHIFT: return new CScriptVar(dai<>dbi); + case LEX_EQUAL: return new CScriptVar(da==db); + case LEX_NEQUAL: return new CScriptVar(da!=db); + case '<': return new CScriptVar(da': return new CScriptVar(da>db); + case LEX_GEQUAL: return new CScriptVar(da>=db); + default: throw new CScriptException("This operation not supported on the int datatype"); + } +} + +CScriptVar *CScriptVar::mathsOp(CScriptVar *b, int op) { + CScriptVar *a = this; + // TODO Equality checks on classes/structures + // Type equality check + if (op == LEX_TYPEEQUAL || op == LEX_NTYPEEQUAL) { + // check type first, then call again to check data + bool eql = ((a->flags & SCRIPTVAR_VARTYPEMASK) == + (b->flags & SCRIPTVAR_VARTYPEMASK)) && + a->mathsOp(b, LEX_EQUAL); + if (op == LEX_TYPEEQUAL) + return new CScriptVar(eql); + else + return new CScriptVar(!eql); + } + // do maths... + if (a->isUndefined() && b->isUndefined()) { + if (op == LEX_EQUAL) return new CScriptVar(true); + else if (op == LEX_NEQUAL) return new CScriptVar(false); + else return new CScriptVar(); // undefined + } else if ((a->isNumeric() || a->isUndefined()) && + (b->isNumeric() || b->isUndefined())) { + if (!a->isDouble() && !b->isDouble()) { + // use ints + return DoMaths(a, b, op); + } else { + // use doubles + return DoMaths(a, b, op); + } + + } else if (a->isArray()) { + /* Just check pointers */ + switch (op) { + case LEX_EQUAL: return new CScriptVar(a==b); + case LEX_NEQUAL: return new CScriptVar(a!=b); + default: throw new CScriptException("This operation not supported on the Array datatype"); + } + } else if (a->isObject()) { + /* Just check pointers */ + switch (op) { + case LEX_EQUAL: return new CScriptVar(a==b); + case LEX_NEQUAL: return new CScriptVar(a!=b); + default: throw new CScriptException("This operation not supported on the Object datatype"); + } + } else { + string da = a->getString(); + string db = b->getString(); + // use strings + switch (op) { + case '+': return new CScriptVar(da+db, SCRIPTVAR_STRING); + case LEX_EQUAL: return new CScriptVar(da==db); + case LEX_NEQUAL: return new CScriptVar(da!=db); + case '<': return new CScriptVar(da': return new CScriptVar(da>db); + case LEX_GEQUAL: return new CScriptVar(da>=db); + default: throw new CScriptException("This operation not supported on the string datatype"); + } + } + ASSERT(0); + return 0; +} + +void CScriptVar::copySimpleData(CScriptVar *val) { + data = val->data; + intData = val->intData; + doubleData = val->doubleData; + flags = (flags & ~SCRIPTVAR_VARTYPEMASK) | (val->flags & SCRIPTVAR_VARTYPEMASK); +} + +void CScriptVar::copyValue(CScriptVar *val) { + if (val) { + copySimpleData(val); + // remove all current children + removeAllChildren(); + // copy children of 'val' + CScriptVarLink *child = val->firstChild; + while (child) { + CScriptVar *copied; + // don't copy the 'parent' object... + if (child->name != TINYJS_PROTOTYPE_CLASS) + copied = child->var->deepCopy(); + else + copied = child->var; + + addChild(child->name, copied); + + child = child->nextSibling; + } + } else { + setUndefined(); + } +} + +CScriptVar *CScriptVar::deepCopy() { + CScriptVar *newVar = new CScriptVar(); + newVar->copySimpleData(this); + // copy children + CScriptVarLink *child = firstChild; + while (child) { + CScriptVar *copied; + // don't copy the 'parent' object... + if (child->name != TINYJS_PROTOTYPE_CLASS) + copied = child->var->deepCopy(); + else + copied = child->var; + + newVar->addChild(child->name, copied); + child = child->nextSibling; + } + return newVar; +} + +void CScriptVar::trace(string indentStr, const string &name) { + TRACE("%s'%s' = '%s' %s\n", + indentStr.c_str(), + name.c_str(), + getString().c_str(), + getFlagsAsString().c_str()); + string indent = indentStr+" "; + CScriptVarLink *link = firstChild; + while (link) { + link->var->trace(indent, link->name); + link = link->nextSibling; + } +} + +string CScriptVar::getFlagsAsString() { + string flagstr = ""; + if (flags&SCRIPTVAR_FUNCTION) flagstr = flagstr + "FUNCTION "; + if (flags&SCRIPTVAR_OBJECT) flagstr = flagstr + "OBJECT "; + if (flags&SCRIPTVAR_ARRAY) flagstr = flagstr + "ARRAY "; + if (flags&SCRIPTVAR_NATIVE) flagstr = flagstr + "NATIVE "; + if (flags&SCRIPTVAR_DOUBLE) flagstr = flagstr + "DOUBLE "; + if (flags&SCRIPTVAR_INTEGER) flagstr = flagstr + "INTEGER "; + if (flags&SCRIPTVAR_BOOLEAN) flagstr = flagstr + "BOOLEAN "; + if (flags&SCRIPTVAR_STRING) flagstr = flagstr + "STRING "; + return flagstr; +} + +string CScriptVar::getParsableString() { + // Numbers can just be put in directly + if (isNumeric()) + return getString(); + if (isFunction()) { + ostringstream funcStr; + funcStr << "function ("; + // get list of parameters + CScriptVarLink *link = firstChild; + while (link) { + funcStr << link->name; + if (link->nextSibling) funcStr << ","; + link = link->nextSibling; + } + // add function body + funcStr << ") " << getString(); + return funcStr.str(); + } + // if it is a string then we quote it + if (isString()) + return getJSString(getString()); + if (isNull()) + return "null"; + return "undefined"; +} + +void CScriptVar::getJSON(ostringstream &destination, const string linePrefix) { + if (isObject()) { + string indentedLinePrefix = linePrefix+" "; + // children - handle with bracketed list + destination << "{ \n"; + CScriptVarLink *link = firstChild; + while (link) { + destination << indentedLinePrefix; + if (isAlphaNum(link->name)) + destination << link->name; + else + destination << getJSString(link->name); + destination << " : "; + link->var->getJSON(destination, indentedLinePrefix); + link = link->nextSibling; + if (link) { + destination << ",\n"; + } + } + destination << "\n" << linePrefix << "}"; + } else if (isArray()) { + string indentedLinePrefix = linePrefix+" "; + destination << "[\n"; + int len = getArrayLength(); + if (len>10000) len=10000; // we don't want to get stuck here! + + for (int i=0;igetJSON(destination, indentedLinePrefix); + if (iref(); + // Add built-in classes + stringClass = (new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT))->ref(); + arrayClass = (new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT))->ref(); + objectClass = (new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT))->ref(); + root->addChild("String", stringClass); + root->addChild("Array", arrayClass); + root->addChild("Object", objectClass); + addNative("function eval(jsCode)", this, &CTinyJS::scEval); // execute the given string and return the resul +} + +CTinyJS::~CTinyJS() { + ASSERT(!l); + scopes.clear(); + stringClass->unref(); + arrayClass->unref(); + objectClass->unref(); + root->unref(); + +#if DEBUG_MEMORY + show_allocated(); +#endif +} + +void CTinyJS::trace() { + root->trace(); +} + +void CTinyJS::execute(const string &code) { + CScriptLex *oldLex = l; + vector oldScopes = scopes; + l = new CScriptLex(code); + scopes.clear(); + scopes.push_back(root); + try { + bool execute = true; + while (l->tk) statement(execute); + } catch (CScriptException *e) { + ostringstream msg; + msg << "Error " << e->text << " at " << l->getPosition(); + delete l; + l = oldLex; + throw new CScriptException(msg.str()); + } + delete l; + l = oldLex; + scopes = oldScopes; +} + +CScriptVarLink CTinyJS::evaluateComplex(const string &code) { + CScriptLex *oldLex = l; + vector oldScopes = scopes; + + l = new CScriptLex(code); + scopes.clear(); + scopes.push_back(root); + CScriptVarLink *v = 0; + try { + bool execute = true; + do { + CLEAN(v); + v = base(execute); + if (l->tk!=LEX_EOF) l->match(';'); + } while (l->tk!=LEX_EOF); + } catch (CScriptException *e) { + ostringstream msg; + msg << "Error " << e->text << " at " << l->getPosition(); + delete l; + l = oldLex; + throw new CScriptException(msg.str()); + } + delete l; + l = oldLex; + scopes = oldScopes; + + if (v) { + CScriptVarLink r = *v; + CLEAN(v); + return r; + } + // return undefined... + return CScriptVarLink(new CScriptVar()); +} + +string CTinyJS::evaluate(const string &code) { + return evaluateComplex(code).var->getString(); +} + +void CTinyJS::parseFunctionArguments(CScriptVar *funcVar) { + l->match('('); + while (l->tk!=')') { + funcVar->addChildNoDup(l->tkStr); + l->match(LEX_ID); + if (l->tk!=')') l->match(','); + } + l->match(')'); +} +void CTinyJS::addNative(const string &funcDesc, JSCallback ptr, void *userdata) { + CScriptVar *funcVar = addNative(funcDesc); + funcVar->setCallback(ptr, userdata); +} +void CTinyJS::addNative(const string &funcDesc, NativeFncBase *ptr, void *userdata) { + CScriptVar *funcVar = addNative(funcDesc); + funcVar->setCallback(ptr, userdata); +} +CScriptVar *CTinyJS::addNative(const string &funcDesc) { + CScriptLex *oldLex = l; + l = new CScriptLex(funcDesc); + CScriptVar *base = root; + + l->match(LEX_R_FUNCTION); + string funcName = l->tkStr; + l->match(LEX_ID); + /* Check for dots, we might want to do something like function String.substring ... */ + while (l->tk == '.') { + l->match('.'); + CScriptVarLink *link = base->findChild(funcName); + // if it doesn't exist, make an object class + if (!link) link = base->addChild(funcName, new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT)); + base = link->var; + funcName = l->tkStr; + l->match(LEX_ID); + } + + CScriptVar *funcVar = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_FUNCTION); + parseFunctionArguments(funcVar); + delete l; + l = oldLex; + + base->addChild(funcName, funcVar); + return funcVar; +} + +CScriptVarLink *CTinyJS::parseFunctionDefinition() { + // actually parse a function... + l->match(LEX_R_FUNCTION); + string funcName = TINYJS_TEMP_NAME; + /* we can have functions without names */ + if (l->tk==LEX_ID) { + funcName = l->tkStr; + l->match(LEX_ID); + } + CScriptVarLink *funcVar = new CScriptVarLink(new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_FUNCTION), funcName); + parseFunctionArguments(funcVar->var); + int funcBegin = l->tokenStart; + bool noexecute = false; + block(noexecute); + funcVar->var->data = l->getSubString(funcBegin); + return funcVar; +} + +CScriptVarLink *CTinyJS::factor(bool &execute) { + if (l->tk==LEX_R_TRUE) { + l->match(LEX_R_TRUE); + return new CScriptVarLink(new CScriptVar(true)); + } + if (l->tk==LEX_R_FALSE) { + l->match(LEX_R_FALSE); + return new CScriptVarLink(new CScriptVar(false)); + } + if (l->tk==LEX_R_NULL) { + l->match(LEX_R_NULL); + return new CScriptVarLink(new CScriptVar(TINYJS_BLANK_DATA,SCRIPTVAR_NULL)); + } + if (l->tk==LEX_R_UNDEFINED) { + l->match(LEX_R_UNDEFINED); + return new CScriptVarLink(new CScriptVar(TINYJS_BLANK_DATA,SCRIPTVAR_UNDEFINED)); + } + if (l->tk==LEX_ID || l->tk=='(') { + CScriptVarLink *a; + int op = l->tk; + if(l->tk=='(') + { + l->match('('); + a = base(execute); + l->match(')'); + } + else + a = execute ? findInScopes(l->tkStr) : new CScriptVarLink(new CScriptVar()); + /* The parent if we're executing a method call */ + CScriptVar *parent = 0; + + if (execute && !a) { + /* Variable doesn't exist! JavaScript says we should create it + * (we won't add it here. This is done in the assignment operator)*/ + a = new CScriptVarLink(new CScriptVar(), l->tkStr); + } + if(op==LEX_ID) + l->match(LEX_ID); + while (l->tk=='(' || l->tk=='.' || l->tk=='[') { + if (l->tk=='(') { // ------------------------------------- Function Call + if (execute) { + if (!a->var->isFunction()) { + string errorMsg = "Expecting '"; + errorMsg = errorMsg + a->name + "' to be a function"; + throw new CScriptException(errorMsg.c_str()); + } + l->match('('); + // create a new symbol table entry for execution of this function + CScriptVar *functionRoot = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_FUNCTION); + if (parent) + functionRoot->addChildNoDup("this", parent); + else + functionRoot->addChildNoDup("this", new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT)); // always add a this-Object + // grab in all parameters + CScriptVarLink *v = a->var->firstChild; + while (v) { + CScriptVarLink *value = base(execute); + if (execute) { + if (value->var->isBasic()) { + // pass by value + functionRoot->addChild(v->name, value->var->deepCopy()); + } else { + // pass by reference + functionRoot->addChild(v->name, value->var); + } + } + CLEAN(value); + if (l->tk!=')') l->match(','); + v = v->nextSibling; + } + l->match(')'); + // setup a return variable + CScriptVarLink *returnVar = NULL; + // execute function! + // add the function's execute space to the symbol table so we can recurse + CScriptVarLink *returnVarLink = functionRoot->addChild(TINYJS_RETURN_VAR); + scopes.push_back(functionRoot); + + if (a->var->isNative()) { + ASSERT(a->var->jsCallback); + if (a->var->isNative_ClassMemberFnc()) + (*a->var->jsCallbackClass)(functionRoot, a->var->jsCallbackUserData); + else + a->var->jsCallback(functionRoot, a->var->jsCallbackUserData); + } else { + /* we just want to execute the block, but something could + * have messed up and left us with the wrong ScriptLex, so + * we want to be careful here... */ + CScriptException *exception = 0; + CScriptLex *oldLex = l; + CScriptLex *newLex = new CScriptLex(a->var->getString()); + l = newLex; + try { + block(execute); + // because return will probably have called this, and set execute to false + execute = true; + } catch (CScriptException *e) { + exception = e; + } + delete newLex; + l = oldLex; + + if (exception) + throw exception; + } + + scopes.pop_back(); + /* get the real return var before we remove it from our function */ + returnVar = new CScriptVarLink(returnVarLink->var); + functionRoot->removeLink(returnVarLink); + delete functionRoot; + if (returnVar) + a = returnVar; + else + a = new CScriptVarLink(new CScriptVar()); + } else { + // function, but not executing - just parse args and be done + l->match('('); + while (l->tk != ')') { + CScriptVarLink *value = base(execute); + CLEAN(value); + if (l->tk!=')') l->match(','); + } + l->match(')'); + if (l->tk == '{') { + block(execute); + } + } + } else if (l->tk == '.') { // ------------------------------------- Record Access + l->match('.'); + if (execute) { + const string &name = l->tkStr; + CScriptVarLink *child = a->var->findChild(name); + if (!child) child = findInParentClasses(a->var, name); + if (!child) { + /* if we haven't found this defined yet, use the built-in + 'length' properly */ + if (a->var->isArray() && name == "length") { + int l = a->var->getArrayLength(); + child = new CScriptVarLink(new CScriptVar(l)); + } else if (a->var->isString() && name == "length") { + int l = a->var->getString().size(); + child = new CScriptVarLink(new CScriptVar(l)); + } else { + child = a->var->addChild(name); + } + } + parent = a->var; + a = child; + } + l->match(LEX_ID); + } else if (l->tk == '[') { // ------------------------------------- Array Access + l->match('['); + CScriptVarLink *index = expression(execute); + l->match(']'); + if (execute) { + CScriptVarLink *child = a->var->findChildOrCreate(index->var->getString()); + parent = a->var; + a = child; + } + CLEAN(index); + } else ASSERT(0); + } + return a; + } + if (l->tk==LEX_INT || l->tk==LEX_FLOAT) { + CScriptVar *a = new CScriptVar(l->tkStr, + ((l->tk==LEX_INT)?SCRIPTVAR_INTEGER:SCRIPTVAR_DOUBLE)); + l->match(l->tk); + return new CScriptVarLink(a); + } + if (l->tk==LEX_STR) { + CScriptVar *a = new CScriptVar(l->tkStr, SCRIPTVAR_STRING); + l->match(LEX_STR); + return new CScriptVarLink(a); + } + if (l->tk=='{') { + CScriptVar *contents = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT); + /* JSON-style object definition */ + l->match('{'); + while (l->tk != '}') { + string id = l->tkStr; + // we only allow strings or IDs on the left hand side of an initialisation + if (l->tk==LEX_STR) l->match(LEX_STR); + else l->match(LEX_ID); + l->match(':'); + if (execute) { + CScriptVarLink *a = base(execute); + contents->addChild(id, a->var); + CLEAN(a); + } + // no need to clean here, as it will definitely be used + if (l->tk != '}') l->match(','); + } + + l->match('}'); + return new CScriptVarLink(contents); + } + if (l->tk=='[') { + CScriptVar *contents = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_ARRAY); + /* JSON-style array */ + l->match('['); + int idx = 0; + while (l->tk != ']') { + if (execute) { + char idx_str[16]; // big enough for 2^32 + sprintf_s(idx_str, sizeof(idx_str), "%d",idx); + + CScriptVarLink *a = base(execute); + contents->addChild(idx_str, a->var); + CLEAN(a); + } + // no need to clean here, as it will definitely be used + if (l->tk != ']') l->match(','); + idx++; + } + l->match(']'); + return new CScriptVarLink(contents); + } + if (l->tk==LEX_R_FUNCTION) { + CScriptVarLink *funcVar = parseFunctionDefinition(); + if (funcVar->name != TINYJS_TEMP_NAME) + TRACE("Functions not defined at statement-level are not meant to have a name"); + return funcVar; + } + if (l->tk==LEX_R_NEW) { + // new -> create a new object + l->match(LEX_R_NEW); + const string &className = l->tkStr; + if (execute) { + CScriptVarLink *objClass = findInScopes(className); + if (!objClass) { + TRACE("%s is not a valid class name", className.c_str()); + return new CScriptVarLink(new CScriptVar()); + } + l->match(LEX_ID); + CScriptVar *obj = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT); + obj->addChild(TINYJS_PROTOTYPE_CLASS, objClass->var); + if (l->tk == '(') { + l->match('('); + l->match(')'); + } + // TODO: Object constructors + return new CScriptVarLink(obj); + } else { + l->match(LEX_ID); + if (l->tk == '(') { + l->match('('); + while(l->tk != ')') l->match(l->tk); + l->match(')'); + } + } + } + // Nothing we can do here... just hope it's the end... + l->match(LEX_EOF); + return 0; +} +CScriptVarLink *CTinyJS::unary(bool &execute) { + CScriptVarLink *a; + if (l->tk=='-') { + l->match('-'); + a = unary(execute); + if (execute) { + CScriptVar zero(0); + CScriptVar *res = zero.mathsOp(a->var, '-'); + CREATE_LINK(a, res); + } + } else + if (l->tk=='+') { + l->match('+'); + a = unary(execute); + } else + if (l->tk=='!') { + l->match('!'); // binary not + a = unary(execute); + if (execute) { + CScriptVar zero(0); + CScriptVar *res = a->var->mathsOp(&zero, LEX_EQUAL); + CREATE_LINK(a, res); + } + } else + if (l->tk=='~') { + l->match('~'); // binary neg + a = unary(execute); + if (execute) { + CScriptVar *res = a->var->mathsOp(NULL, '~'); + CREATE_LINK(a, res); + } + } else + // pre increment/decrement + if (l->tk==LEX_PLUSPLUS || l->tk==LEX_MINUSMINUS) { + int op = l->tk; + l->match(l->tk); + a = factor(execute); + if (execute) { + if(a->owned) + { + CScriptVar one(1); + CScriptVar *res = a->var->mathsOp(&one, op==LEX_PLUSPLUS ? '+' : '-'); + // in-place add/subtract + a->replaceWith(res); + CREATE_LINK(a, res); + } + } + } + else + a = factor(execute); + + // post increment/decrement + if (l->tk==LEX_PLUSPLUS || l->tk==LEX_MINUSMINUS) { + int op = l->tk; + l->match(l->tk); + if (execute) { + if(a->owned) + { +// TRACE("post-increment of %s and a is %sthe owner\n", a->name.c_str(), a->owned?"":"not "); + CScriptVar one(1); + CScriptVar *res = a->var->ref(); + CScriptVar *new_a = a->var->mathsOp(&one, op==LEX_PLUSPLUS ? '+' : '-'); + a->replaceWith(new_a); + CREATE_LINK(a, res); + res->unref(); + } + else + { + ostringstream errorString; + errorString << "Post-" << (l->tk==LEX_PLUSPLUS ? "Increment" : "Decrement") << ": "; + if(a->name.length()) + errorString << a->name << " is not defined"; + else + errorString << "invalid " << (l->tk==LEX_PLUSPLUS ? "increment" : "decrement") << " operand"; +// errorString << " at " << (l->getPosition(l->tokenStart-2)); + throw new CScriptException(errorString.str()); + } + } + } + + + return a; +} + +CScriptVarLink *CTinyJS::term(bool &execute) { + CScriptVarLink *a = unary(execute); + while (l->tk=='*' || l->tk=='/' || l->tk=='%') { + int op = l->tk; + l->match(l->tk); + CScriptVarLink *b = unary(execute); + if (execute) { + CScriptVar *res = a->var->mathsOp(b->var, op); + CREATE_LINK(a, res); + } + CLEAN(b); + } + return a; +} + +CScriptVarLink *CTinyJS::expression(bool &execute) { + CScriptVarLink *a = term(execute); + while (l->tk=='+' || l->tk=='-') { + int op = l->tk; + l->match(l->tk); + CScriptVarLink *b = term(execute); + if (execute) { + // not in-place, so just replace + CScriptVar *res = a->var->mathsOp(b->var, op); + CREATE_LINK(a, res); + } + CLEAN(b); + } + return a; +} + +CScriptVarLink *CTinyJS::binary_shift(bool &execute) { + CScriptVarLink *a = expression(execute); + while (l->tk==LEX_LSHIFT || l->tk==LEX_RSHIFT) { + int op = l->tk; + l->match(l->tk); + + CScriptVarLink *b = expression(execute); + if (execute) { + // not in-place, so just replace + CScriptVar *res = a->var->mathsOp(b->var, op); + CREATE_LINK(a, res); + } + CLEAN(b); + } + return a; +} + +CScriptVarLink *CTinyJS::relation(bool &execute, int set, int set_n) { + CScriptVarLink *a = set_n ? relation(execute, set_n, 0) : binary_shift(execute); +// CScriptVarLink *a = expression(execute); + CScriptVarLink *b; + while ((set==LEX_EQUAL && (l->tk==LEX_EQUAL || l->tk==LEX_NEQUAL || + l->tk==LEX_TYPEEQUAL || l->tk==LEX_NTYPEEQUAL)) + || + (set=='<' && (l->tk==LEX_LEQUAL || l->tk==LEX_GEQUAL || + l->tk=='<' || l->tk=='>'))) { + int op = l->tk; + l->match(l->tk); + b = set_n ? relation(execute, set_n, 0) : binary_shift(execute); + if (execute) { + CScriptVar *res = a->var->mathsOp(b->var, op); + CREATE_LINK(a,res); + } + CLEAN(b); + } + return a; +} + +CScriptVarLink *CTinyJS::logic_binary(bool &execute, int op, int op_n1, int op_n2) { + CScriptVarLink *a = op_n1 ? logic_binary(execute, op_n1, op_n2, 0) : relation(execute); + CScriptVarLink *b; + while (l->tk==op) { + l->match(l->tk); + b = op_n1 ? logic_binary(execute, op_n1, op_n2, 0) : relation(execute); + if (execute) { + CScriptVar *res = a->var->mathsOp(b->var, op); + CREATE_LINK(a, res); + } + CLEAN(b); + } + return a; +} + +CScriptVarLink *CTinyJS::logic(bool &execute, int op, int op_n) { + CScriptVarLink *a = op_n ? logic(execute, op_n, 0) : logic_binary(execute); + CScriptVarLink *b; + while (l->tk==op) { + bool noexecute = false; + int bop = l->tk; + l->match(l->tk); + bool shortCircuit = false; + bool boolean = false; + // if we have short-circuit ops, then if we know the outcome + // we don't bother to execute the other op. Even if not + // we need to tell mathsOp it's an & or | + if (op==LEX_ANDAND) { + bop = '&'; + shortCircuit = !a->var->getBool(); + boolean = true; + } else if (op==LEX_OROR) { + bop = '|'; + shortCircuit = a->var->getBool(); + boolean = true; + } + b = op_n ? logic(shortCircuit ? noexecute : execute, op_n, 0) : logic_binary(shortCircuit ? noexecute : execute); + if (execute && !shortCircuit) { + if (boolean) { + CScriptVar *newa = new CScriptVar(a->var->getBool()); + CScriptVar *newb = new CScriptVar(b->var->getBool()); + CREATE_LINK(a, newa); + CREATE_LINK(b, newb); + } + CScriptVar *res = a->var->mathsOp(b->var, bop); + CREATE_LINK(a, res); + } + CLEAN(b); + } + return a; +} +CScriptVarLink *CTinyJS::condition(bool &execute) +{ + CScriptVarLink *a = logic(execute); + if (l->tk=='?') + { + l->match(l->tk); + bool cond = a->var->getBool(); + CLEAN(a); + CScriptVarLink *b; + bool noexecute=false; + a = condition(cond ? execute : noexecute ); + l->match(':'); + b = condition(cond ? noexecute : execute); + if(cond) + CLEAN(b); + else + { + CLEAN(a); + a=b; + } + } + return a; +} + +CScriptVarLink *CTinyJS::assignment(bool &execute) { + CScriptVarLink *lhs = condition(execute); + if (l->tk=='=' || l->tk==LEX_PLUSEQUAL || l->tk==LEX_MINUSEQUAL || + l->tk==LEX_LSHIFTEQUAL || l->tk==LEX_RSHIFTEQUAL || + l->tk==LEX_ANDEQUAL || l->tk==LEX_OREQUAL || l->tk==LEX_XOREQUAL) { + /* If we're assigning to this and we don't have a parent, + * add it to the symbol table root as per JavaScript. */ + if (execute && !lhs->owned) { + if (lhs->name.length()>0) { + CScriptVarLink *realLhs = root->addChildNoDup(lhs->name, lhs->var); + CLEAN(lhs); + lhs = realLhs; + } else + TRACE("Trying to assign to an un-named type\n"); + } + + int op = l->tk; + l->match(l->tk); + CScriptVarLink *rhs = assignment(execute); + if (execute) { + if (op=='=') { + lhs->replaceWith(rhs); + } else if (op==LEX_PLUSEQUAL) { + CScriptVar *res = lhs->var->mathsOp(rhs->var, '+'); + lhs->replaceWith(res); + } else if (op==LEX_MINUSEQUAL) { + CScriptVar *res = lhs->var->mathsOp(rhs->var, '-'); + lhs->replaceWith(res); + } else if (op==LEX_LSHIFTEQUAL) { + CScriptVar *res = lhs->var->mathsOp(rhs->var, LEX_LSHIFT); + lhs->replaceWith(res); + } else if (op==LEX_RSHIFTEQUAL) { + CScriptVar *res = lhs->var->mathsOp(rhs->var, LEX_RSHIFT); + lhs->replaceWith(res); + } else if (op==LEX_ANDEQUAL) { + CScriptVar *res = lhs->var->mathsOp(rhs->var, '&'); + lhs->replaceWith(res); + } else if (op==LEX_OREQUAL) { + CScriptVar *res = lhs->var->mathsOp(rhs->var, '|'); + lhs->replaceWith(res); + } else if (op==LEX_XOREQUAL) { + CScriptVar *res = lhs->var->mathsOp(rhs->var, '^'); + lhs->replaceWith(res); + } + } + CLEAN(rhs); + } + return lhs; +} +CScriptVarLink *CTinyJS::base(bool &execute) { + CScriptVarLink *a; + for(;;) + { + a = assignment(execute); + if (l->tk == ',') { + l->match(','); + CLEAN(a); + } else + break; + } + return a; +} +void CTinyJS::block(bool &execute) { + // TODO: fast skip of blocks + l->match('{'); + if (execute) { + while (l->tk && l->tk!='}') + statement(execute); + l->match('}'); + } else { + int brackets = 1; + while (l->tk && brackets) { + if (l->tk == '{') brackets++; + if (l->tk == '}') brackets--; + l->match(l->tk); + } + } + +} + +void CTinyJS::statement(bool &execute) { + if (l->tk==LEX_ID || + l->tk==LEX_INT || + l->tk==LEX_FLOAT || + l->tk==LEX_STR || + l->tk==LEX_PLUSPLUS || + l->tk==LEX_MINUSMINUS || + l->tk=='+' || + l->tk=='-' || + l->tk=='(') { + /* Execute a simple statement that only contains basic arithmetic... */ + CLEAN(base(execute)); + l->match(';'); + } else if (l->tk=='{') { + /* A block of code */ + block(execute); + } else if (l->tk==';') { + /* Empty statement - to allow things like ;;; */ + l->match(';'); + } else if (l->tk==LEX_R_VAR) { + /* variable creation. TODO - we need a better way of parsing the left + * hand side. Maybe just have a flag called can_create_var that we + * set and then we parse as if we're doing a normal equals.*/ + l->match(LEX_R_VAR); + for(;;) + { + CScriptVarLink *a = 0; + if (execute) + a = scopes.back()->findChildOrCreate(l->tkStr); + l->match(LEX_ID); + // now do stuff defined with dots + while (l->tk == '.') { + l->match('.'); + if (execute) { + CScriptVarLink *lastA = a; + a = lastA->var->findChildOrCreate(l->tkStr); + } + l->match(LEX_ID); + } + // sort out initialiser + if (l->tk == '=') { + l->match('='); + CScriptVarLink *var = assignment(execute); + if (execute) + a->replaceWith(var); + CLEAN(var); + } + if (l->tk == ',') { + l->match(','); + } else + break; + } + l->match(';'); + } else if (l->tk==LEX_R_IF) { + l->match(LEX_R_IF); + l->match('('); + CScriptVarLink *var = base(execute); + l->match(')'); + bool cond = execute && var->var->getBool(); + CLEAN(var); + bool noexecute = false; // because we need to be abl;e to write to it + statement(cond ? execute : noexecute); + if (l->tk==LEX_R_ELSE) { + l->match(LEX_R_ELSE); + statement(cond ? noexecute : execute); + } + } else if (l->tk==LEX_R_DO) { + // We do repetition by pulling out the string representing our statement + // there's definitely some opportunity for optimisation here + l->match(LEX_R_DO); + bool loopCond = true; + bool old_execute = execute; + int old_runtimeFlags = runtimeFlags; + runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | RUNTIME_CANBREAK | RUNTIME_CANCONTINUE; + int whileBodyStart = l->tokenStart; + int whileCondStart = 0; + CScriptLex *oldLex = NULL; + CScriptLex *whileBody = NULL; + CScriptLex *whileCond = NULL; + int loopCount = TINYJS_LOOP_MAX_ITERATIONS; + while (loopCond && loopCount-->0) + { + if(whileBody) + { + whileBody->reset(); + l = whileBody; + } + statement(execute); + if(!whileBody) whileBody = l->getSubLex(whileBodyStart); + if(old_execute && !execute) + { + // break; + if(runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE)) + { + execute = old_execute; + if(runtimeFlags & RUNTIME_BREAK) + loopCond = false; + runtimeFlags &= ~(RUNTIME_BREAK | RUNTIME_CONTINUE); + } + // return, break or continue; + } + + if(!whileCond) + { + l->match(LEX_R_WHILE); + l->match('('); + whileCondStart = l->tokenStart; + } + else + { + whileCond->reset(); + l = whileCond; + } + CScriptVarLink *cond = base(execute); + loopCond = execute && cond->var->getBool(); + CLEAN(cond); + if(!whileCond) + { + whileCond = l->getSubLex(whileCondStart); + l->match(')'); + l->match(';'); + oldLex = l; + } + } + runtimeFlags = old_runtimeFlags; + l = oldLex; + delete whileCond; + delete whileBody; + + if (loopCount<=0) { + root->trace(); + TRACE("WHILE Loop exceeded %d iterations at %s\n", TINYJS_LOOP_MAX_ITERATIONS, l->getPosition(l->tokenLastEnd).c_str()); + throw new CScriptException("LOOP_ERROR"); + } + } else if (l->tk==LEX_R_WHILE) { + // We do repetition by pulling out the string representing our statement + // there's definitely some opportunity for optimisation here + l->match(LEX_R_WHILE); + l->match('('); + int whileCondStart = l->tokenStart; + bool noexecute = false; + CScriptVarLink *cond = base(execute); + bool loopCond = execute && cond->var->getBool(); + CLEAN(cond); + CScriptLex *whileCond = l->getSubLex(whileCondStart); + l->match(')'); + int whileBodyStart = l->tokenStart; + + bool old_execute = execute; + int old_runtimeFlags = runtimeFlags; + runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | RUNTIME_CANBREAK | RUNTIME_CANCONTINUE; + statement(loopCond ? execute : noexecute); + CScriptLex *whileBody = l->getSubLex(whileBodyStart); + CScriptLex *oldLex = l; + if(loopCond && old_execute != execute) + { + // break; + if(runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE)) + { + execute = old_execute; + if(runtimeFlags & RUNTIME_BREAK) + loopCond = false; + runtimeFlags &= ~(RUNTIME_BREAK | RUNTIME_CONTINUE); + } + // return, break or continue; + } + + int loopCount = TINYJS_LOOP_MAX_ITERATIONS; + while (loopCond && loopCount-->0) { + whileCond->reset(); + l = whileCond; + cond = base(execute); + loopCond = execute && cond->var->getBool(); + CLEAN(cond); + if (loopCond) { + whileBody->reset(); + l = whileBody; + statement(execute); + if(!execute) + { + // break; + if(runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE)) + { + execute = old_execute; + if(runtimeFlags & RUNTIME_BREAK) + loopCond = false; + runtimeFlags &= ~(RUNTIME_BREAK | RUNTIME_CONTINUE); + } + // return, break or continue; + } + } + } + runtimeFlags = old_runtimeFlags; + l = oldLex; + delete whileCond; + delete whileBody; + + if (loopCount<=0) { + root->trace(); + TRACE("WHILE Loop exceeded %d iterations at %s\n", TINYJS_LOOP_MAX_ITERATIONS, l->getPosition(l->tokenLastEnd).c_str()); + throw new CScriptException("LOOP_ERROR"); + } + } else if (l->tk==LEX_R_FOR) { + l->match(LEX_R_FOR); + l->match('('); + statement(execute); // initialisation + //l->match(';'); + bool noexecute = false; + int forCondStart = l->tokenStart; + bool cond_empty = true; + bool loopCond = true; // Empty Condition -->always true + CScriptVarLink *cond; + if(l->tk != ';') + { + cond_empty = false; + cond = base(execute); // condition + loopCond = execute && cond->var->getBool(); + CLEAN(cond); + } + CScriptLex *forCond = l->getSubLex(forCondStart); + l->match(';'); + bool iter_empty = true; + CScriptLex *forIter=NULL; + if(l->tk != ')') + { + iter_empty = false; + int forIterStart = l->tokenStart; + base(noexecute); // iterator + forIter = l->getSubLex(forIterStart); + } + l->match(')'); + int forBodyStart = l->tokenStart; + bool old_execute = execute; + int old_runtimeFlags = runtimeFlags; + runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | RUNTIME_CANBREAK | RUNTIME_CANCONTINUE; + statement(loopCond ? execute : noexecute); + CScriptLex *forBody = l->getSubLex(forBodyStart); + CScriptLex *oldLex = l; + if(loopCond && old_execute != execute) + { + // break; + if(runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE)) + { + execute = old_execute; + if(runtimeFlags & RUNTIME_BREAK) + loopCond = false; + runtimeFlags &= ~(RUNTIME_BREAK | RUNTIME_CONTINUE); + } + // return, break or continue; + } + + if (loopCond && !iter_empty) { + forIter->reset(); + l = forIter; + base(execute); + } + int loopCount = TINYJS_LOOP_MAX_ITERATIONS; + while (execute && loopCond && loopCount-->0) { + if(!cond_empty) + { + forCond->reset(); + l = forCond; + cond = base(execute); + loopCond = cond->var->getBool(); + CLEAN(cond); + } + if (execute && loopCond) { + forBody->reset(); + l = forBody; + statement(execute); + if(!execute) + { + // break or continue; + if(runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE)) + { + execute = old_execute; + if(runtimeFlags & RUNTIME_BREAK) + loopCond = false; + runtimeFlags &= ~(RUNTIME_BREAK | RUNTIME_CONTINUE); + } + // return nothing to do + } + } + if (execute && loopCond && !iter_empty) { + forIter->reset(); + l = forIter; + base(execute); + } + } + runtimeFlags = old_runtimeFlags; + l = oldLex; + delete forCond; + delete forIter; + delete forBody; + if (loopCount<=0) { + root->trace(); + TRACE("FOR Loop exceeded %d iterations at %s\n", TINYJS_LOOP_MAX_ITERATIONS, l->getPosition(l->tokenLastEnd).c_str()); + throw new CScriptException("LOOP_ERROR"); + } + } else if (l->tk==LEX_R_BREAK) { + l->match(LEX_R_BREAK); + if(runtimeFlags & RUNTIME_CANBREAK) + { + runtimeFlags |= RUNTIME_BREAK; + execute = false; + } + l->match(';'); + } else if (l->tk==LEX_R_CONTINUE) { + l->match(LEX_R_CONTINUE); + if(runtimeFlags & RUNTIME_CANCONTINUE) + { + runtimeFlags |= RUNTIME_CONTINUE; + execute = false; + } + l->match(';'); + } else if (l->tk==LEX_R_RETURN) { + l->match(LEX_R_RETURN); + CScriptVarLink *result = 0; + if (l->tk != ';') + result = base(execute); + if (execute) { + CScriptVarLink *resultVar = scopes.back()->findChild(TINYJS_RETURN_VAR); + if (resultVar) + resultVar->replaceWith(result); + else + TRACE("RETURN statement, but not in a function.\n"); + execute = false; + } + CLEAN(result); + l->match(';'); + } else if (l->tk==LEX_R_FUNCTION) { + CScriptVarLink *funcVar = parseFunctionDefinition(); + if (execute) { + if (funcVar->name == TINYJS_TEMP_NAME) + TRACE("Functions defined at statement-level are meant to have a name\n"); + else + scopes.back()->addChildNoDup(funcVar->name, funcVar->var); + } + CLEAN(funcVar); + } else l->match(LEX_EOF); +} + +/// Get the value of the given variable, or return 0 +const string *CTinyJS::getVariable(const string &path) { + // traverse path + size_t prevIdx = 0; + size_t thisIdx = path.find('.'); + if (thisIdx == string::npos) thisIdx = path.length(); + CScriptVar *var = root; + while (var && prevIdxfindChild(el); + var = varl?varl->var:0; + prevIdx = thisIdx+1; + thisIdx = path.find('.', prevIdx); + if (thisIdx == string::npos) thisIdx = path.length(); + } + // return result + if (var) + return &var->getString(); + else + return 0; +} + +/// Finds a child, looking recursively up the scopes +CScriptVarLink *CTinyJS::findInScopes(const std::string &childName) { + for (int s=scopes.size()-1;s>=0;s--) { + CScriptVarLink *v = scopes[s]->findChild(childName); + if (v) return v; + } + return NULL; + +} + +/// Look up in any parent classes of the given object +CScriptVarLink *CTinyJS::findInParentClasses(CScriptVar *object, const std::string &name) { + // Look for links to actual parent classes + CScriptVarLink *parentClass = object->findChild(TINYJS_PROTOTYPE_CLASS); + while (parentClass) { + CScriptVarLink *implementation = parentClass->var->findChild(name); + if (implementation) return implementation; + parentClass = parentClass->var->findChild(TINYJS_PROTOTYPE_CLASS); + } + // else fake it for strings and finally objects + if (object->isString()) { + CScriptVarLink *implementation = stringClass->findChild(name); + if (implementation) return implementation; + } + if (object->isArray()) { + CScriptVarLink *implementation = arrayClass->findChild(name); + if (implementation) return implementation; + } + CScriptVarLink *implementation = objectClass->findChild(name); + if (implementation) return implementation; + + return 0; +} + +void CTinyJS::scEval(CScriptVar *c, void *data) { + std::string str = c->getParameter("jsCode")->getString(); + c->setReturnVar(evaluateComplex(str).var); +} diff --git a/TinyJS.h b/TinyJS.h index 2cfc166..be69d0b 100755 --- a/TinyJS.h +++ b/TinyJS.h @@ -1,337 +1,388 @@ -/* - * TinyJS - * - * A single-file Javascript-alike engine - * - * Authored By Gordon Williams - * - * Copyright (C) 2009 Pur3 Ltd - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef TINYJS_H -#define TINYJS_H - -#ifdef _WIN32 -#ifdef _DEBUG -#define _CRTDBG_MAP_ALLOC -#include -#include -#endif -#endif -#include -#include - -#ifndef TRACE -#define TRACE printf -#endif // TRACE - -const int TINYJS_LOOP_MAX_ITERATIONS = 8192; - -enum LEX_TYPES { - LEX_EOF = 0, - LEX_ID = 256, - LEX_INT, - LEX_FLOAT, - LEX_STR, - - LEX_EQUAL, - LEX_TYPEEQUAL, - LEX_NEQUAL, - LEX_NTYPEEQUAL, - LEX_LEQUAL, - LEX_LSHIFT, - LEX_LSHIFTEQUAL, - LEX_GEQUAL, - LEX_RSHIFT, - LEX_RSHIFTEQUAL, - LEX_PLUSEQUAL, - LEX_MINUSEQUAL, - LEX_PLUSPLUS, - LEX_MINUSMINUS, - LEX_ANDEQUAL, - LEX_ANDAND, - LEX_OREQUAL, - LEX_OROR, - LEX_XOREQUAL, - // reserved words -#define LEX_R_LIST_START LEX_R_IF - LEX_R_IF, - LEX_R_ELSE, - LEX_R_DO, - LEX_R_WHILE, - LEX_R_FOR, - LEX_R_BREAK, - LEX_R_CONTINUE, - LEX_R_FUNCTION, - LEX_R_RETURN, - LEX_R_VAR, - LEX_R_TRUE, - LEX_R_FALSE, - LEX_R_NULL, - LEX_R_UNDEFINED, - LEX_R_NEW, - - LEX_R_LIST_END /* always the last entry */ -}; - -enum SCRIPTVAR_FLAGS { - SCRIPTVAR_UNDEFINED = 0, - SCRIPTVAR_FUNCTION = 1, - SCRIPTVAR_OBJECT = 2, - SCRIPTVAR_ARRAY = 4, - SCRIPTVAR_DOUBLE = 8, // floating point double - SCRIPTVAR_INTEGER = 16, // integer number - SCRIPTVAR_STRING = 32, // string - SCRIPTVAR_NULL = 64, // it seems null is its own data type - - SCRIPTVAR_NATIVE = 128, // to specify this is a native function - SCRIPTVAR_NUMERICMASK = SCRIPTVAR_NULL | - SCRIPTVAR_DOUBLE | - SCRIPTVAR_INTEGER, - SCRIPTVAR_VARTYPEMASK = SCRIPTVAR_DOUBLE | - SCRIPTVAR_INTEGER | - SCRIPTVAR_STRING | - SCRIPTVAR_FUNCTION | - SCRIPTVAR_OBJECT | - SCRIPTVAR_ARRAY | - SCRIPTVAR_NULL, - -}; - -#define TINYJS_RETURN_VAR "return" -#define TINYJS_PROTOTYPE_CLASS "prototype" -#define TINYJS_TEMP_NAME "" -#define TINYJS_BLANK_DATA "" - -/// convert the given string into a quoted string suitable for javascript -std::string getJSString(const std::string &str); - -class CScriptException { -public: - std::string text; - CScriptException(const std::string &exceptionText); -}; - -class CScriptLex -{ -public: - CScriptLex(const std::string &input); - CScriptLex(CScriptLex *owner, int startChar, int endChar); - ~CScriptLex(void); - - char currCh, nextCh; - int tk; ///< The type of the token that we have - int tokenStart; ///< Position in the data at the beginning of the token we have here - int tokenEnd; ///< Position in the data at the last character of the token we have here - int tokenLastEnd; ///< Position in the data at the last character of the last token - std::string tkStr; ///< Data contained in the token we have here - - void match(int expected_tk); ///< Lexical match wotsit - static std::string getTokenStr(int token); ///< Get the string representation of the given token - void reset(); ///< Reset this lex so we can start again - - std::string getSubString(int pos); ///< Return a sub-string from the given position up until right now - CScriptLex *getSubLex(int lastPosition); ///< Return a sub-lexer from the given position up until right now - +/* + * TinyJS + * + * A single-file Javascript-alike engine + * + * Authored By Gordon Williams + * + * Copyright (C) 2009 Pur3 Ltd + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef TINYJS_H +#define TINYJS_H + +#include +#include + +#undef TRACE +#ifndef TRACE +#define TRACE printf +#endif // TRACE + +const int TINYJS_LOOP_MAX_ITERATIONS = 8192; + +enum LEX_TYPES { + LEX_EOF = 0, + LEX_ID = 256, + LEX_INT, + LEX_FLOAT, + LEX_STR, + + LEX_EQUAL, + LEX_TYPEEQUAL, + LEX_NEQUAL, + LEX_NTYPEEQUAL, + LEX_LEQUAL, + LEX_LSHIFT, + LEX_LSHIFTEQUAL, + LEX_GEQUAL, + LEX_RSHIFT, + LEX_RSHIFTEQUAL, + LEX_PLUSEQUAL, + LEX_MINUSEQUAL, + LEX_PLUSPLUS, + LEX_MINUSMINUS, + LEX_ANDEQUAL, + LEX_ANDAND, + LEX_OREQUAL, + LEX_OROR, + LEX_XOREQUAL, + // reserved words +#define LEX_R_LIST_START LEX_R_IF + LEX_R_IF, + LEX_R_ELSE, + LEX_R_DO, + LEX_R_WHILE, + LEX_R_FOR, + LEX_R_BREAK, + LEX_R_CONTINUE, + LEX_R_FUNCTION, + LEX_R_RETURN, + LEX_R_VAR, + LEX_R_TRUE, + LEX_R_FALSE, + LEX_R_NULL, + LEX_R_UNDEFINED, + LEX_R_NEW, + + LEX_R_LIST_END /* always the last entry */ +}; + +enum SCRIPTVAR_FLAGS { + SCRIPTVAR_UNDEFINED = 0, + SCRIPTVAR_FUNCTION = 1, + SCRIPTVAR_OBJECT = 2, + SCRIPTVAR_ARRAY = 4, + SCRIPTVAR_DOUBLE = 8, // floating point double + SCRIPTVAR_INTEGER = 16, // integer number + SCRIPTVAR_BOOLEAN = 32, // boolean + SCRIPTVAR_STRING = 64, // string + SCRIPTVAR_NULL = 128, // it seems null is its own data type + + SCRIPTVAR_NATIVE_FNC = 256, // to specify this is a native function + SCRIPTVAR_NATIVE_MFNC = 512, // to specify this is a native function from class->memberFunc + SCRIPTVAR_NUMERICMASK = SCRIPTVAR_NULL | + SCRIPTVAR_DOUBLE | + SCRIPTVAR_INTEGER | + SCRIPTVAR_BOOLEAN, + SCRIPTVAR_VARTYPEMASK = SCRIPTVAR_DOUBLE | + SCRIPTVAR_INTEGER | + SCRIPTVAR_BOOLEAN | + SCRIPTVAR_STRING | + SCRIPTVAR_FUNCTION | + SCRIPTVAR_OBJECT | + SCRIPTVAR_ARRAY | + SCRIPTVAR_NULL, + SCRIPTVAR_NATIVE = SCRIPTVAR_NATIVE_FNC | + SCRIPTVAR_NATIVE_MFNC, +}; +enum RUNTIME_FLAGS { + RUNTIME_CANBREAK = 1, + RUNTIME_BREAK = 2, + RUNTIME_CANCONTINUE = 4, + RUNTIME_CONTINUE = 8, +}; + +#define RUNTIME_LOOP_MASK (RUNTIME_CANBREAK | RUNTIME_BREAK | RUNTIME_CANCONTINUE | RUNTIME_CONTINUE) + +#define TINYJS_RETURN_VAR "return" +#define TINYJS_PROTOTYPE_CLASS "prototype" +#define TINYJS_TEMP_NAME "" +#define TINYJS_BLANK_DATA "" + +/// convert the given string into a quoted string suitable for javascript +std::string getJSString(const std::string &str); + +class CScriptException { +public: + std::string text; + CScriptException(const std::string &exceptionText); +}; + +class CScriptLex +{ +public: + CScriptLex(const std::string &input); + CScriptLex(CScriptLex *owner, int startChar, int endChar); + ~CScriptLex(void); + + char currCh, nextCh; + int tk; ///< The type of the token that we have + int tokenStart; ///< Position in the data at the beginning of the token we have here + int tokenEnd; ///< Position in the data at the last character of the token we have here + int tokenLastEnd; ///< Position in the data at the last character of the last token + std::string tkStr; ///< Data contained in the token we have here + + void match(int expected_tk); ///< Lexical match wotsit + static std::string getTokenStr(int token); ///< Get the string representation of the given token + void reset(); ///< Reset this lex so we can start again + + std::string getSubString(int pos); ///< Return a sub-string from the given position up until right now + CScriptLex *getSubLex(int lastPosition); ///< Return a sub-lexer from the given position up until right now + std::string getPosition(int pos=-1); ///< Return a string representing the position in lines and columns of the character pos given - -protected: - /* When we go into a loop, we use getSubLex to get a lexer for just the sub-part of the - relevant string. This doesn't re-allocate and copy the string, but instead copies - the data pointer and sets dataOwned to false, and dataStart/dataEnd to the relevant things. */ - char *data; ///< Data string to get tokens from - int dataStart, dataEnd; ///< Start and end position in data string - bool dataOwned; ///< Do we own this data string? - - int dataPos; ///< Position in data (we CAN go past the end of the string here) - - void getNextCh(); - void getNextToken(); ///< Get the text token from our text string -}; - -class CScriptVar; - -typedef void (*JSCallback)(CScriptVar *var, void *userdata); - -class CScriptVarLink -{ -public: - std::string name; - CScriptVarLink *nextSibling; - CScriptVarLink *prevSibling; - CScriptVar *var; - bool owned; - - CScriptVarLink(CScriptVar *var, const std::string &name = TINYJS_TEMP_NAME); - CScriptVarLink(const CScriptVarLink &link); ///< Copy constructor - ~CScriptVarLink(); - void replaceWith(CScriptVar *newVar); ///< Replace the Variable pointed to - void replaceWith(CScriptVarLink *newVar); ///< Replace the Variable pointed to (just dereferences) -}; - -/// Variable class (containing a doubly-linked list of children) -class CScriptVar -{ -public: - CScriptVar(); ///< Create undefined - CScriptVar(const std::string &varData, int varFlags); ///< User defined - CScriptVar(const std::string &str); ///< Create a string - CScriptVar(double varData); - CScriptVar(int val); - ~CScriptVar(void); - - CScriptVar *getReturnVar(); ///< If this is a function, get the result value (for use by native functions) - void setReturnVar(CScriptVar *var); ///< Set the result value. Use this when setting complex return data as it avoids a deepCopy() - CScriptVar *getParameter(const std::string &name); ///< If this is a function, get the parameter with the given name (for use by native functions) - - CScriptVarLink *findChild(const std::string &childName); ///< Tries to find a child with the given name, may return 0 - CScriptVarLink *findChildOrCreate(const std::string &childName, int varFlags=SCRIPTVAR_UNDEFINED); ///< Tries to find a child with the given name, or will create it with the given flags - CScriptVarLink *findChildOrCreateByPath(const std::string &path); ///< Tries to find a child with the given path (separated by dots) - CScriptVarLink *addChild(const std::string &childName, CScriptVar *child=NULL); - CScriptVarLink *addChildNoDup(const std::string &childName, CScriptVar *child=NULL); ///< add a child overwriting any with the same name - void removeChild(CScriptVar *child); - void removeLink(CScriptVarLink *link); ///< Remove a specific link (this is faster than finding via a child) - void removeAllChildren(); - CScriptVar *getArrayIndex(int idx); ///< The the value at an array index - void setArrayIndex(int idx, CScriptVar *value); ///< Set the value at an array index - int getArrayLength(); ///< If this is an array, return the number of items in it (else 0) - int getChildren(); ///< Get the number of children - - int getInt(); - bool getBool() { return getInt() != 0; } - double getDouble(); - const std::string &getString(); - std::string getParsableString(); ///< get Data as a parsable javascript string - void setInt(int num); - void setDouble(double val); - void setString(const std::string &str); - void setUndefined(); - - bool isInt() { return (flags&SCRIPTVAR_INTEGER)!=0; } - bool isDouble() { return (flags&SCRIPTVAR_DOUBLE)!=0; } - bool isString() { return (flags&SCRIPTVAR_STRING)!=0; } - bool isNumeric() { return (flags&SCRIPTVAR_NUMERICMASK)!=0; } - bool isFunction() { return (flags&SCRIPTVAR_FUNCTION)!=0; } - bool isObject() { return (flags&SCRIPTVAR_OBJECT)!=0; } - bool isArray() { return (flags&SCRIPTVAR_ARRAY)!=0; } - bool isNative() { return (flags&SCRIPTVAR_NATIVE)!=0; } - bool isUndefined() { return (flags & SCRIPTVAR_VARTYPEMASK) == SCRIPTVAR_UNDEFINED; } - bool isNull() { return (flags & SCRIPTVAR_NULL)!=0; } - bool isBasic() { return firstChild==0; } ///< Is this *not* an array/object/etc - - CScriptVar *mathsOp(CScriptVar *b, int op); ///< do a maths op with another script variable - void copyValue(CScriptVar *val); ///< copy the value from the value given - CScriptVar *deepCopy(); ///< deep copy this node and return the result - - void trace(std::string indentStr = "", const std::string &name = ""); ///< Dump out the contents of this using trace - std::string getFlagsAsString(); ///< For debugging - just dump a string version of the flags - void getJSON(std::ostringstream &destination, const std::string linePrefix=""); ///< Write out all the JS code needed to recreate this script variable to the stream (as JSON) - void setCallback(JSCallback callback, void *userdata); ///< Set the callback for native functions - - CScriptVarLink *firstChild; - CScriptVarLink *lastChild; - - /// For memory management/garbage collection - CScriptVar *ref(); ///< Add reference to this variable - void unref(); ///< Remove a reference, and delete this variable if required - int getRefs(); ///< Get the number of references to this script variable -protected: - int refs; ///< The number of references held to this - used for garbage collection - - std::string data; ///< The contents of this variable if it is a string - long intData; ///< The contents of this variable if it is an int - double doubleData; ///< The contents of this variable if it is a double - int flags; ///< the flags determine the type of the variable - int/double/string/etc - JSCallback jsCallback; ///< Callback for native functions - void *jsCallbackUserData; ///< user data passed as second argument to native functions - - void init(); ///< initialisation of data members - - /** Copy the basic data and flags from the variable given, with no - * children. Should be used internally only - by copyValue and deepCopy */ - void copySimpleData(CScriptVar *val); - - friend class CTinyJS; -}; - -class CTinyJS { -public: - CTinyJS(); - ~CTinyJS(); - - void execute(const std::string &code); - /** Evaluate the given code and return a link to a javascript object, - * useful for (dangerous) JSON parsing. If nothing to return, will return - * 'undefined' variable type. CScriptVarLink is returned as this will - * automatically unref the result as it goes out of scope. If you want to - * keep it, you must use ref() and unref() */ - CScriptVarLink evaluateComplex(const std::string &code); - /** Evaluate the given code and return a string. If nothing to return, will return - * 'undefined' */ - std::string evaluate(const std::string &code); - - /// add a native function to be called from TinyJS - /** example: - \code - void scRandInt(CScriptVar *c, void *userdata) { ... } - tinyJS->addNative("function randInt(min, max)", scRandInt, 0); - \endcode - - or - - \code - void scSubstring(CScriptVar *c, void *userdata) { ... } - tinyJS->addNative("function String.substring(lo, hi)", scSubstring, 0); - \endcode - */ - void addNative(const std::string &funcDesc, JSCallback ptr, void *userdata); - - /// Get the value of the given variable, or return 0 - const std::string *getVariable(const std::string &path); - - /// Send all variables to stdout - void trace(); - - CScriptVar *root; /// root of symbol table -private: - CScriptLex *l; /// current lexer - std::vector scopes; /// stack of scopes when parsing - CScriptVar *stringClass; /// Built in string class - CScriptVar *objectClass; /// Built in object class - CScriptVar *arrayClass; /// Built in array class - - // parsing - in order of precedence - CScriptVarLink *factor(bool &execute); - CScriptVarLink *unary(bool &execute); - CScriptVarLink *term(bool &execute); - CScriptVarLink *expression(bool &execute); - CScriptVarLink *condition(bool &execute); - CScriptVarLink *logic(bool &execute); - CScriptVarLink *base(bool &execute); - void block(bool &execute); - void statement(bool &execute); - // parsing utility functions - CScriptVarLink *parseFunctionDefinition(); - void parseFunctionArguments(CScriptVar *funcVar); - - CScriptVarLink *findInScopes(const std::string &childName); ///< Finds a child, looking recursively up the scopes - /// Look up in any parent classes of the given object - CScriptVarLink *findInParentClasses(CScriptVar *object, const std::string &name); -}; - -#endif + +protected: + /* When we go into a loop, we use getSubLex to get a lexer for just the sub-part of the + relevant string. This doesn't re-allocate and copy the string, but instead copies + the data pointer and sets dataOwned to false, and dataStart/dataEnd to the relevant things. */ + char *data; ///< Data string to get tokens from + int dataStart, dataEnd; ///< Start and end position in data string + bool dataOwned; ///< Do we own this data string? + + int dataPos; ///< Position in data (we CAN go past the end of the string here) + + void getNextCh(); + void getNextToken(); ///< Get the text token from our text string +}; + +class CScriptVar; + +typedef void (*JSCallback)(CScriptVar *var, void *userdata); + +class CScriptVarLink +{ +public: + std::string name; + CScriptVarLink *nextSibling; + CScriptVarLink *prevSibling; + CScriptVar *var; + CScriptVar *owned; // pointer to the owner CScriptVar + + CScriptVarLink(CScriptVar *var, const std::string &name = TINYJS_TEMP_NAME); + CScriptVarLink(const CScriptVarLink &link); ///< Copy constructor + ~CScriptVarLink(); + void replaceWith(CScriptVar *newVar); ///< Replace the Variable pointed to + void replaceWith(CScriptVarLink *newVar); ///< Replace the Variable pointed to (just dereferences) +}; + +class NativeFncBase; +/// Variable class (containing a doubly-linked list of children) +class CScriptVar +{ +public: + CScriptVar(); ///< Create undefined + CScriptVar(const std::string &varData, int varFlags); ///< User defined + CScriptVar(const std::string &str); ///< Create a string + CScriptVar(double varData); + CScriptVar(int val); + CScriptVar(bool val); + ~CScriptVar(void); + + CScriptVar *getReturnVar(); ///< If this is a function, get the result value (for use by native functions) + void setReturnVar(CScriptVar *var); ///< Set the result value. Use this when setting complex return data as it avoids a deepCopy() + CScriptVar *getParameter(const std::string &name); ///< If this is a function, get the parameter with the given name (for use by native functions) + + CScriptVarLink *findChild(const std::string &childName); ///< Tries to find a child with the given name, may return 0 + CScriptVarLink *findChildOrCreate(const std::string &childName, int varFlags=SCRIPTVAR_UNDEFINED); ///< Tries to find a child with the given name, or will create it with the given flags + CScriptVarLink *findChildOrCreateByPath(const std::string &path); ///< Tries to find a child with the given path (separated by dots) + CScriptVarLink *addChild(const std::string &childName, CScriptVar *child=NULL); + CScriptVarLink *addChildNoDup(const std::string &childName, CScriptVar *child=NULL); ///< add a child overwriting any with the same name + void removeChild(CScriptVar *child); + void removeLink(CScriptVarLink *link); ///< Remove a specific link (this is faster than finding via a child) + void removeAllChildren(); + CScriptVar *getArrayIndex(int idx); ///< The the value at an array index + void setArrayIndex(int idx, CScriptVar *value); ///< Set the value at an array index + int getArrayLength(); ///< If this is an array, return the number of items in it (else 0) + int getChildren(); ///< Get the number of children + + int getInt(); + bool getBool(); + double getDouble(); + const std::string &getString(); + std::string getParsableString(); ///< get Data as a parsable javascript string + void setInt(int num); + void setBool(bool val); + void setDouble(double val); + void setString(const std::string &str); + void setUndefined(); + + bool isInt() { return (flags&SCRIPTVAR_INTEGER)!=0; } + bool isBool() { return (flags&SCRIPTVAR_BOOLEAN)!=0; } + bool isDouble() { return (flags&SCRIPTVAR_DOUBLE)!=0; } + bool isString() { return (flags&SCRIPTVAR_STRING)!=0; } + bool isNumeric() { return (flags&SCRIPTVAR_NUMERICMASK)!=0; } + bool isFunction() { return (flags&SCRIPTVAR_FUNCTION)!=0; } + bool isObject() { return (flags&SCRIPTVAR_OBJECT)!=0; } + bool isArray() { return (flags&SCRIPTVAR_ARRAY)!=0; } + bool isNative() { return (flags&SCRIPTVAR_NATIVE)!=0; } + bool isNative_ClassMemberFnc() { return (flags&SCRIPTVAR_NATIVE_MFNC)!=0; } + bool isUndefined() { return (flags & SCRIPTVAR_VARTYPEMASK) == SCRIPTVAR_UNDEFINED; } + bool isNull() { return (flags & SCRIPTVAR_NULL)!=0; } + bool isBasic() { return firstChild==0; } ///< Is this *not* an array/object/etc + + CScriptVar *mathsOp(CScriptVar *b, int op); ///< do a maths op with another script variable + void copyValue(CScriptVar *val); ///< copy the value from the value given + CScriptVar *deepCopy(); ///< deep copy this node and return the result + + void trace(std::string indentStr = "", const std::string &name = ""); ///< Dump out the contents of this using trace + std::string getFlagsAsString(); ///< For debugging - just dump a string version of the flags + void getJSON(std::ostringstream &destination, const std::string linePrefix=""); ///< Write out all the JS code needed to recreate this script variable to the stream (as JSON) + void setCallback(JSCallback callback, void *userdata); + void setCallback(NativeFncBase *callback, void *userdata); + + CScriptVarLink *firstChild; + CScriptVarLink *lastChild; + + /// For memory management/garbage collection + CScriptVar *ref(); ///< Add reference to this variable + void unref(); ///< Remove a reference, and delete this variable if required + int getRefs(); ///< Get the number of references to this script variable +protected: + int refs; ///< The number of references held to this - used for garbage collection + CScriptVarLink *__proto__; + std::string data; ///< The contents of this variable if it is a string + long intData; ///< The contents of this variable if it is an int + double doubleData; ///< The contents of this variable if it is a double + int flags; ///< the flags determine the type of the variable - int/double/string/etc + union + { + JSCallback jsCallback; ///< Callback for native functions + NativeFncBase *jsCallbackClass; ///< Wrapper for Class-Member-Functions as Callback for native functions + }; + void *jsCallbackUserData; ///< user data passed as second argument to native functions + + void init(); ///< initialisation of data members + + /** Copy the basic data and flags from the variable given, with no + * children. Should be used internally only - by copyValue and deepCopy */ + void copySimpleData(CScriptVar *val); + + friend class CTinyJS; +}; + +class NativeFncBase +{ +public: + virtual void operator()(CScriptVar *v, void *userdata)=0; +}; +template +class NativeFnc : public NativeFncBase +{ +public: + NativeFnc(native *ClassPtr, void (native::*ClassFnc)(CScriptVar *, void *)) + : classPtr(ClassPtr), classFnc(ClassFnc){} + void operator()(CScriptVar *v, void *userdata) + { + (classPtr->*classFnc)(v, userdata); + } + native *classPtr; + void (native::*classFnc)(CScriptVar *v, void *userdata); +}; + +class CTinyJS { +public: + CTinyJS(); + ~CTinyJS(); + + void execute(const std::string &code); + /** Evaluate the given code and return a link to a javascript object, + * useful for (dangerous) JSON parsing. If nothing to return, will return + * 'undefined' variable type. CScriptVarLink is returned as this will + * automatically unref the result as it goes out of scope. If you want to + * keep it, you must use ref() and unref() */ + CScriptVarLink evaluateComplex(const std::string &code); + /** Evaluate the given code and return a string. If nothing to return, will return + * 'undefined' */ + std::string evaluate(const std::string &code); + + /// add a native function to be called from TinyJS + /** example: + \code + void scRandInt(CScriptVar *c, void *userdata) { ... } + tinyJS->addNative("function randInt(min, max)", scRandInt, 0); + \endcode + + or + + \code + void scSubstring(CScriptVar *c, void *userdata) { ... } + tinyJS->addNative("function String.substring(lo, hi)", scSubstring, 0); + \endcode + */ + void addNative(const std::string &funcDesc, JSCallback ptr, void *userdata=0); + void addNative(const std::string &funcDesc, NativeFncBase *ptr, void *userdata=0); + template + void addNative(const std::string &funcDesc, C *class_ptr, void(C::*class_fnc)(CScriptVar *, void *), void *userdata=0) + { + addNative(funcDesc, new NativeFnc(class_ptr, class_fnc), userdata); + } + + /// Get the value of the given variable, or return 0 + const std::string *getVariable(const std::string &path); + + /// Send all variables to stdout + void trace(); + + CScriptVar *root; /// root of symbol table +private: + CScriptLex *l; /// current lexer + int runtimeFlags; + std::vector scopes; /// stack of scopes when parsing + CScriptVar *stringClass; /// Built in string class + CScriptVar *objectClass; /// Built in object class + CScriptVar *arrayClass; /// Built in array class + + // parsing - in order of precedence + CScriptVarLink *factor(bool &execute); + CScriptVarLink *unary(bool &execute); + CScriptVarLink *term(bool &execute); + CScriptVarLink *expression(bool &execute); + CScriptVarLink *binary_shift(bool &execute); + CScriptVarLink *relation(bool &execute, int set=LEX_EQUAL, int set_n='<'); + CScriptVarLink *logic_binary(bool &execute, int op='|', int op_n1='^', int op_n2='&'); + CScriptVarLink *logic(bool &execute, int op=LEX_OROR, int op_n=LEX_ANDAND); + CScriptVarLink *condition(bool &execute); + CScriptVarLink *assignment(bool &execute); + CScriptVarLink *base(bool &execute); + void block(bool &execute); + void statement(bool &execute); + // parsing utility functions + CScriptVarLink *parseFunctionDefinition(); + void parseFunctionArguments(CScriptVar *funcVar); + + CScriptVarLink *findInScopes(const std::string &childName); ///< Finds a child, looking recursively up the scopes + /// Look up in any parent classes of the given object + CScriptVarLink *findInParentClasses(CScriptVar *object, const std::string &name); + CScriptVar *addNative(const std::string &funcDesc); +public: // native Functions + void scEval(CScriptVar *c, void *data); + +}; + +#endif diff --git a/TinyJS_Functions.cpp b/TinyJS_Functions.cpp index 7fa9f47..8eb7961 100755 --- a/TinyJS_Functions.cpp +++ b/TinyJS_Functions.cpp @@ -23,15 +23,25 @@ * Boston, MA 02111-1307, USA. */ -#include "TinyJS_Functions.h" +#ifdef _MSC_VER +# include "targetver.h" +# include +#endif #include #include #include +#include "TinyJS_Functions.h" + +#ifdef _DEBUG +# ifdef _MSC_VER +# define new DEBUG_NEW +# endif +#endif using namespace std; // ----------------------------------------------- Actual Functions -void scTrace(CScriptVar *c, void *userdata) { +void scTrace(CScriptVar *UNUSED(c), void * userdata) { CTinyJS *js = (CTinyJS*)userdata; js->root->trace(); } @@ -123,7 +133,7 @@ void scEval(CScriptVar *c, void *data) { // ----------------------------------------------- Register Functions void registerFunctions(CTinyJS *tinyJS) { - tinyJS->addNative("function eval(jsCode)", scEval, tinyJS); // execute the given string and return the result +// tinyJS->addNative("function eval(jsCode)", scEval, tinyJS); // execute the given string and return the result tinyJS->addNative("function trace()", scTrace, tinyJS); tinyJS->addNative("function Object.dump()", scObjectDump, 0); tinyJS->addNative("function Object.clone()", scObjectClone, 0); diff --git a/lib-tiny-js.vcproj b/lib-tiny-js.vcproj new file mode 100644 index 0000000..7e4e4f4 --- /dev/null +++ b/lib-tiny-js.vcproj @@ -0,0 +1,187 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/targetver.h b/targetver.h new file mode 100644 index 0000000..d9cc11b --- /dev/null +++ b/targetver.h @@ -0,0 +1,24 @@ +#pragma once + +// Die folgenden Makros definieren die mindestens erforderliche Plattform. Die mindestens erforderliche Plattform +// ist die fr�heste Windows-, Internet Explorer-Version usw., die �ber die erforderlichen Features zur Ausf�hrung +// Ihrer Anwendung verf�gt. Die Makros aktivieren alle Funktionen, die auf den Plattformversionen bis +// einschlie�lich der angegebenen Version verf�gbar sind. + +// �ndern Sie folgende Definitionen f�r Plattformen, die �lter als die unten angegebenen sind. +// Unter MSDN finden Sie die neuesten Informationen �ber die entsprechenden Werte f�r die unterschiedlichen Plattformen. +#ifndef WINVER // Gibt an, dass Windows Vista die mindestens erforderliche Plattform ist. +#define WINVER 0x0600 // �ndern Sie den entsprechenden Wert, um auf andere Versionen von Windows abzuzielen. +#endif + +#ifndef _WIN32_WINNT // Gibt an, dass Windows Vista die mindestens erforderliche Plattform ist. +#define _WIN32_WINNT 0x0600 // �ndern Sie den entsprechenden Wert, um auf andere Versionen von Windows abzuzielen. +#endif + +#ifndef _WIN32_WINDOWS // Gibt an, dass Windows 98 die mindestens erforderliche Plattform ist. +#define _WIN32_WINDOWS 0x0410 // �ndern Sie den entsprechenden Wert, um auf mindestens Windows Me abzuzielen. +#endif + +#ifndef _WIN32_IE // Gibt an, dass Internet Explorer 7.0 die mindestens erforderliche Plattform ist. +#define _WIN32_IE 0x0700 // �ndern Sie den entsprechenden Wert, um auf andere Versionen von IE abzuzielen. +#endif diff --git a/tiny-js.sln b/tiny-js.sln new file mode 100644 index 0000000..c060999 --- /dev/null +++ b/tiny-js.sln @@ -0,0 +1,28 @@ +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Script", "Script.vcproj", "{70D538BA-867B-4564-8DEE-F2C78C5AD4C8}" + ProjectSection(ProjectDependencies) = postProject + {9751C465-0294-43CD-A5D9-BD038ABA3961} = {9751C465-0294-43CD-A5D9-BD038ABA3961} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lib-tiny-js", "lib-tiny-js.vcproj", "{9751C465-0294-43CD-A5D9-BD038ABA3961}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {70D538BA-867B-4564-8DEE-F2C78C5AD4C8}.Debug|Win32.ActiveCfg = Debug|Win32 + {70D538BA-867B-4564-8DEE-F2C78C5AD4C8}.Debug|Win32.Build.0 = Debug|Win32 + {70D538BA-867B-4564-8DEE-F2C78C5AD4C8}.Release|Win32.ActiveCfg = Release|Win32 + {70D538BA-867B-4564-8DEE-F2C78C5AD4C8}.Release|Win32.Build.0 = Release|Win32 + {9751C465-0294-43CD-A5D9-BD038ABA3961}.Debug|Win32.ActiveCfg = Debug|Win32 + {9751C465-0294-43CD-A5D9-BD038ABA3961}.Debug|Win32.Build.0 = Debug|Win32 + {9751C465-0294-43CD-A5D9-BD038ABA3961}.Release|Win32.ActiveCfg = Release|Win32 + {9751C465-0294-43CD-A5D9-BD038ABA3961}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal From 124d78d3280235c2ad1cda0bfe6aa98e4a7a2268 Mon Sep 17 00:00:00 2001 From: armin Date: Sat, 4 Sep 2010 14:26:46 +0000 Subject: [PATCH 03/43] - replace the linked list stuff of childs with a c++ map - little fixs --- Script.cpp | 3 +- Script.vcproj | 2 +- TinyJS.cpp | 3257 ++++++++++++++++++++++-------------------- TinyJS.h | 489 ++++--- TinyJS_Functions.cpp | 126 +- 5 files changed, 2017 insertions(+), 1860 deletions(-) diff --git a/Script.cpp b/Script.cpp index 19baf6e..ff43d00 100755 --- a/Script.cpp +++ b/Script.cpp @@ -35,7 +35,7 @@ #include "TinyJS_Functions.h" #include #include - + #if defined(_MSC_VER) && defined(_DEBUG) # define new DEBUG_NEW #endif @@ -97,6 +97,5 @@ int main(int UNUSED(argc), char **UNUSED(argv)) // by Visual Studio we use the DEBUG_NEW stuff _CrtDumpMemoryLeaks(); #endif - new int[10]; return 0; } diff --git a/Script.vcproj b/Script.vcproj index 633c130..415e972 100644 --- a/Script.vcproj +++ b/Script.vcproj @@ -123,7 +123,7 @@ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;_AFXDLL" RuntimeLibrary="2" EnableFunctionLevelLinking="true" - UsePrecompiledHeader="2" + UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" /> diff --git a/TinyJS.cpp b/TinyJS.cpp index f7b464e..23f5589 100755 --- a/TinyJS.cpp +++ b/TinyJS.cpp @@ -23,70 +23,72 @@ * Boston, MA 02111-1307, USA. */ -/* Version 0.1 : (gw) First published on Google Code - Version 0.11 : Making sure the 'root' variable never changes - 'symbol_base' added for the current base of the sybmbol table - Version 0.12 : Added findChildOrCreate, changed string passing to use references - Fixed broken string encoding in getJSString() - Removed getInitCode and added getJSON instead - Added nil - Added rough JSON parsing - Improved example app - Version 0.13 : Added tokenEnd/tokenLastEnd to lexer to avoid parsing whitespace - Ability to define functions without names - Can now do "var mine = function(a,b) { ... };" - Slightly better 'trace' function - Added findChildOrCreateByPath function - Added simple test suite - Added skipping of blocks when not executing - Version 0.14 : Added parsing of more number types - Added parsing of string defined with ' - Changed nil to null as per spec, added 'undefined' - Now set variables with the correct scope, and treat unknown - as 'undefined' rather than failing - Added proper (I hope) handling of null and undefined - Added === check - Version 0.15 : Fix for possible memory leaks - Version 0.16 : Removal of un-needed findRecursive calls - symbol_base removed and replaced with 'scopes' stack - Added reference counting a proper tree structure - (Allowing pass by reference) - Allowed JSON output to output IDs, not strings - Added get/set for array indices - Changed Callbacks to include user data pointer - Added some support for objects - Added more Java-esque builtin functions - Version 0.17 : Now we don't deepCopy the parent object of the class - Added JSON.stringify and eval() - Nicer JSON indenting - Fixed function output in JSON - Added evaluateComplex - Fixed some reentrancy issues with evaluate/execute - Version 0.18 : Fixed some issues with code being executed when it shouldn't - Version 0.19 : Added array.length - Changed '__parent' to 'prototype' to bring it more in line with javascript - Version 0.20 : Added '%' operator - Version 0.21 : Added array type - String.length() no more - now String.length - Added extra constructors to reduce confusion - Fixed checks against undefined - Forked to 42tiny-js by Armin diedering - R3 Added boolean datatype (in string operators its shown as 'true'/'false' but in math as '1'/'0' - Added '~' operator - Added bit-shift operators '<<' '>>' - Added assignment operators '<<=' '>>=' '|=' '&=' '^=' - Addet comma operator like this 'var i=1,j,k=9;' or 'for(i=0,j=12; i<10; i++, j++)' works - Added Conditional operator ( ? : ) - Added automatic cast from doubles to integer e.G. by logic and binary operators - Added pre-increment/-decrement like '++i'/'--i' operator - Fixed post-increment/decrement now returns the previous value - Fixed throws an Error when invalid using of post/pre-increment/decrement (like this 5++ works no more) - Fixed memoryleak (unref arrayClass at deconstructor of JS CTinyJS) - Fixed unary operator handling (like this '-~-5' works now) - Fixed operator prority order -> ',' +/* Version 0.1 : (gw) First published on Google Code + Version 0.11 : Making sure the 'root' variable never changes + 'symbol_base' added for the current base of the sybmbol table + Version 0.12 : Added findChildOrCreate, changed string passing to use references + Fixed broken string encoding in getJSString() + Removed getInitCode and added getJSON instead + Added nil + Added rough JSON parsing + Improved example app + Version 0.13 : Added tokenEnd/tokenLastEnd to lexer to avoid parsing whitespace + Ability to define functions without names + Can now do "var mine = function(a,b) { ... };" + Slightly better 'trace' function + Added findChildOrCreateByPath function + Added simple test suite + Added skipping of blocks when not executing + Version 0.14 : Added parsing of more number types + Added parsing of string defined with ' + Changed nil to null as per spec, added 'undefined' + Now set variables with the correct scope, and treat unknown + as 'undefined' rather than failing + Added proper (I hope) handling of null and undefined + Added === check + Version 0.15 : Fix for possible memory leaks + Version 0.16 : Removal of un-needed findRecursive calls + symbol_base removed and replaced with 'scopes' stack + Added reference counting a proper tree structure + (Allowing pass by reference) + Allowed JSON output to output IDs, not strings + Added get/set for array indices + Changed Callbacks to include user data pointer + Added some support for objects + Added more Java-esque builtin functions + Version 0.17 : Now we don't deepCopy the parent object of the class + Added JSON.stringify and eval() + Nicer JSON indenting + Fixed function output in JSON + Added evaluateComplex + Fixed some reentrancy issues with evaluate/execute + Version 0.18 : Fixed some issues with code being executed when it shouldn't + Version 0.19 : Added array.length + Changed '__parent' to 'prototype' to bring it more in line with javascript + Version 0.20 : Added '%' operator + Version 0.21 : Added array type + String.length() no more - now String.length + Added extra constructors to reduce confusion + Fixed checks against undefined + + + Forked to 42tiny-js by Armin diedering + R3 Added boolean datatype (in string operators its shown as 'true'/'false' but in math as '1'/'0' + Added '~' operator + Added bit-shift operators '<<' '>>' + Added assignment operators '<<=' '>>=' '|=' '&=' '^=' + Addet comma operator like this 'var i=1,j,k=9;' or 'for(i=0,j=12; i<10; i++, j++)' works + Added Conditional operator ( ? : ) + Added automatic cast from doubles to integer e.G. by logic and binary operators + Added pre-increment/-decrement like '++i'/'--i' operator + Fixed post-increment/decrement now returns the previous value + Fixed throws an Error when invalid using of post/pre-increment/decrement (like this 5++ works no more) + Fixed memoryleak (unref arrayClass at deconstructor of JS CTinyJS) + Fixed unary operator handling (like this '-~-5' works now) + Fixed operator prority order -> ',' -> '=' '+=' '-=' '<<=' '>>=' '&=' '^=' '|=' -> '? :' -> '||' -> '&&' -> '|' -> '^' -> '&' - -> ['==' '===' '!=' '!=='] + -> ['==' '===' '!=' '!=='] -> [ '<' '<=' '=>' '>'] -> ['<<' '>>'] -> [ '*' '/' '%'] -> ['!' '~' '-' '++' '--'] @@ -98,10 +100,10 @@ Remove unneeded and wrong deepCopy by assignment operator '=' - NOTE: This doesn't support constructors for objects - Recursive loops of data such as a.foo = a; fail to be garbage collected - 'length' cannot be set - There is no ternary operator implemented yet + NOTE: This doesn't support constructors for objects + Recursive loops of data such as a.foo = a; fail to be garbage collected + 'length' cannot be set + There is no ternary operator implemented yet */ @@ -154,843 +156,907 @@ vector allocatedVars; vector allocatedLinks; void mark_allocated(CScriptVar *v) { - allocatedVars.push_back(v); + allocatedVars.push_back(v); } void mark_deallocated(CScriptVar *v) { - for (size_t i=0;igetRefs()); - allocatedVars[i]->trace(" "); - } - for (size_t i=0;iname.c_str(), allocatedLinks[i]->var->getRefs()); - allocatedLinks[i]->var->trace(" "); - } - allocatedVars.clear(); - allocatedLinks.clear(); + for (size_t i=0;igetRefs()); + allocatedVars[i]->trace(" "); + } + for (size_t i=0;iname.c_str(), allocatedLinks[i]->var->getRefs()); + allocatedLinks[i]->var->trace(" "); + } + allocatedVars.clear(); + allocatedLinks.clear(); } #endif // ----------------------------------------------------------------------------------- Utils bool isWhitespace(char ch) { - return (ch==' ') || (ch=='\t') || (ch=='\n') || (ch=='\r'); + return (ch==' ') || (ch=='\t') || (ch=='\n') || (ch=='\r'); } bool isNumeric(char ch) { - return (ch>='0') && (ch<='9'); + return (ch>='0') && (ch<='9'); } bool isNumber(const string &str) { - for (size_t i=0;i='0') && (ch<='9')) || - ((ch>='a') && (ch<='f')) || - ((ch>='A') && (ch<='F')); + return ((ch>='0') && (ch<='9')) || + ((ch>='a') && (ch<='f')) || + ((ch>='A') && (ch<='F')); } bool isAlpha(char ch) { - return ((ch>='a') && (ch<='z')) || ((ch>='A') && (ch<='Z')) || ch=='_'; + return ((ch>='a') && (ch<='z')) || ((ch>='A') && (ch<='Z')) || ch=='_'; } bool isIDString(const char *s) { - if (!isAlpha(*s)) - return false; - while (*s) { - if (!(isAlpha(*s) || isNumeric(*s))) - return false; - s++; - } - return true; + if (!isAlpha(*s)) + return false; + while (*s) { + if (!(isAlpha(*s) || isNumeric(*s))) + return false; + s++; + } + return true; } void replace(string &str, char textFrom, const char *textTo) { - int sLen = strlen(textTo); - size_t p = str.find(textFrom); - while (p != string::npos) { - str = str.substr(0, p) + textTo + str.substr(p+1); - p = str.find(textFrom, p+sLen); - } + int sLen = strlen(textTo); + size_t p = str.find(textFrom); + while (p != string::npos) { + str = str.substr(0, p) + textTo + str.substr(p+1); + p = str.find(textFrom, p+sLen); + } } /// convert the given string into a quoted string suitable for javascript std::string getJSString(const std::string &str) { - std::string nStr = str; - for (size_t i=0;idata; - dataOwned = false; - dataStart = startChar; - dataEnd = endChar; - reset(); + data = owner->data; + dataOwned = false; + dataStart = startChar; + dataEnd = endChar; + reset(); } CScriptLex::~CScriptLex(void) { - if (dataOwned) - free((void*)data); + if (dataOwned) + free((void*)data); } void CScriptLex::reset() { - dataPos = dataStart; - tokenStart = 0; - tokenEnd = 0; - tokenLastEnd = 0; - tk = 0; - tkStr = ""; - getNextCh(); - getNextCh(); - getNextToken(); + dataPos = dataStart; + tokenStart = 0; + tokenEnd = 0; + tokenLastEnd = 0; + tk = 0; + tkStr = ""; + getNextCh(); + getNextCh(); + getNextToken(); } void CScriptLex::match(int expected_tk) { - if (tk!=expected_tk) { - ostringstream errorString; - errorString << "Got " << getTokenStr(tk) << " expected " << getTokenStr(expected_tk) - << " at " << getPosition(tokenStart) << " in '" << data << "'"; - throw new CScriptException(errorString.str()); - } - getNextToken(); + if (tk!=expected_tk) { + ostringstream errorString; + errorString << "Got " << getTokenStr(tk) << " expected " << getTokenStr(expected_tk) + << " at " << getPosition(tokenStart) << " in '" << data << "'"; + throw new CScriptException(errorString.str()); + } + getNextToken(); } string CScriptLex::getTokenStr(int token) { - if (token>32 && token<128) { - char buf[4] = "' '"; - buf[1] = (char)token; - return buf; - } - switch (token) { - case LEX_EOF : return "EOF"; - case LEX_ID : return "ID"; - case LEX_INT : return "INT"; - case LEX_FLOAT : return "FLOAT"; - case LEX_STR : return "STRING"; - case LEX_EQUAL : return "=="; - case LEX_TYPEEQUAL : return "==="; - case LEX_NEQUAL : return "!="; - case LEX_NTYPEEQUAL : return "!=="; - case LEX_LEQUAL : return "<="; - case LEX_LSHIFT : return "<<"; - case LEX_LSHIFTEQUAL : return "<<="; - case LEX_GEQUAL : return ">="; - case LEX_RSHIFT : return ">>"; - case LEX_RSHIFTEQUAL : return ">>="; - case LEX_PLUSEQUAL : return "+="; - case LEX_MINUSEQUAL : return "-="; - case LEX_PLUSPLUS : return "++"; - case LEX_MINUSMINUS : return "--"; - case LEX_ANDEQUAL : return "&="; - case LEX_ANDAND : return "&&"; - case LEX_OREQUAL : return "|="; - case LEX_OROR : return "||"; - case LEX_XOREQUAL : return "^="; - // reserved words - case LEX_R_IF : return "if"; - case LEX_R_ELSE : return "else"; - case LEX_R_DO : return "do"; - case LEX_R_WHILE : return "while"; - case LEX_R_FOR : return "for"; - case LEX_R_BREAK : return "break"; - case LEX_R_CONTINUE : return "continue"; - case LEX_R_FUNCTION : return "function"; - case LEX_R_RETURN : return "return"; - case LEX_R_VAR : return "var"; - case LEX_R_TRUE : return "true"; - case LEX_R_FALSE : return "false"; - case LEX_R_NULL : return "null"; - case LEX_R_UNDEFINED : return "undefined"; - case LEX_R_NEW : return "new"; - } - - ostringstream msg; - msg << "?[" << token << "]"; - return msg.str(); + if (token>32 && token<128) { + char buf[4] = "' '"; + buf[1] = (char)token; + return buf; + } + switch (token) { + case LEX_EOF : return "EOF"; + case LEX_ID : return "ID"; + case LEX_INT : return "INT"; + case LEX_FLOAT : return "FLOAT"; + case LEX_STR : return "STRING"; + case LEX_EQUAL : return "=="; + case LEX_TYPEEQUAL : return "==="; + case LEX_NEQUAL : return "!="; + case LEX_NTYPEEQUAL : return "!=="; + case LEX_LEQUAL : return "<="; + case LEX_LSHIFT : return "<<"; + case LEX_LSHIFTEQUAL : return "<<="; + case LEX_GEQUAL : return ">="; + case LEX_RSHIFT : return ">>"; + case LEX_RSHIFTEQUAL : return ">>="; + case LEX_PLUSEQUAL : return "+="; + case LEX_MINUSEQUAL : return "-="; + case LEX_PLUSPLUS : return "++"; + case LEX_MINUSMINUS : return "--"; + case LEX_ANDEQUAL : return "&="; + case LEX_ANDAND : return "&&"; + case LEX_OREQUAL : return "|="; + case LEX_OROR : return "||"; + case LEX_XOREQUAL : return "^="; + // reserved words + case LEX_R_IF : return "if"; + case LEX_R_ELSE : return "else"; + case LEX_R_DO : return "do"; + case LEX_R_WHILE : return "while"; + case LEX_R_FOR : return "for"; + case LEX_R_BREAK : return "break"; + case LEX_R_CONTINUE : return "continue"; + case LEX_R_FUNCTION : return "function"; + case LEX_R_RETURN : return "return"; + case LEX_R_VAR : return "var"; + case LEX_R_TRUE : return "true"; + case LEX_R_FALSE : return "false"; + case LEX_R_NULL : return "null"; + case LEX_R_UNDEFINED : return "undefined"; + case LEX_R_NEW : return "new"; + } + + ostringstream msg; + msg << "?[" << token << "]"; + return msg.str(); } void CScriptLex::getNextCh() { - currCh = nextCh; - if (dataPos < dataEnd) - nextCh = data[dataPos]; - else - nextCh = 0; - dataPos++; + currCh = nextCh; + if (dataPos < dataEnd) + nextCh = data[dataPos]; + else + nextCh = 0; + dataPos++; } void CScriptLex::getNextToken() { - tk = LEX_EOF; - tkStr.clear(); - while (currCh && isWhitespace(currCh)) getNextCh(); - // newline comments - if (currCh=='/' && nextCh=='/') { - while (currCh && currCh!='\n') getNextCh(); - getNextCh(); - getNextToken(); - return; - } - // block comments - if (currCh=='/' && nextCh=='*') { - while (currCh && (currCh!='*' || nextCh!='/')) getNextCh(); - getNextCh(); - getNextCh(); - getNextToken(); - return; - } - // record beginning of this token - tokenStart = dataPos-2; - // tokens - if (isAlpha(currCh)) { // IDs - while (isAlpha(currCh) || isNumeric(currCh)) { - tkStr += currCh; - getNextCh(); - } - tk = LEX_ID; - if (tkStr=="if") tk = LEX_R_IF; - else if (tkStr=="else") tk = LEX_R_ELSE; - else if (tkStr=="do") tk = LEX_R_DO; - else if (tkStr=="while") tk = LEX_R_WHILE; - else if (tkStr=="for") tk = LEX_R_FOR; - else if (tkStr=="break") tk = LEX_R_BREAK; - else if (tkStr=="continue") tk = LEX_R_CONTINUE; - else if (tkStr=="function") tk = LEX_R_FUNCTION; - else if (tkStr=="return") tk = LEX_R_RETURN; - else if (tkStr=="var") tk = LEX_R_VAR; - else if (tkStr=="true") tk = LEX_R_TRUE; - else if (tkStr=="false") tk = LEX_R_FALSE; - else if (tkStr=="null") tk = LEX_R_NULL; - else if (tkStr=="undefined") tk = LEX_R_UNDEFINED; - else if (tkStr=="new") tk = LEX_R_NEW; - } else if (isNumeric(currCh)) { // Numbers - bool isHex = false; - if (currCh=='0') { tkStr += currCh; getNextCh(); } - if (currCh=='x') { - isHex = true; - tkStr += currCh; getNextCh(); - } - tk = LEX_INT; - while (isNumeric(currCh) || (isHex && isHexadecimal(currCh))) { - tkStr += currCh; - getNextCh(); - } - if (!isHex && currCh=='.') { - tk = LEX_FLOAT; - tkStr += '.'; - getNextCh(); - while (isNumeric(currCh)) { - tkStr += currCh; - getNextCh(); - } - } - // do fancy e-style floating point - if (!isHex && currCh=='e') { - tk = LEX_FLOAT; - tkStr += currCh; getNextCh(); - if (currCh=='-') { tkStr += currCh; getNextCh(); } - while (isNumeric(currCh)) { - tkStr += currCh; getNextCh(); - } - } - } else if (currCh=='"') { - // strings... - getNextCh(); - while (currCh && currCh!='"') { - if (currCh == '\\') { - getNextCh(); - switch (currCh) { - case 'n' : tkStr += '\n'; break; - case '"' : tkStr += '"'; break; - case '\\' : tkStr += '\\'; break; - default: tkStr += currCh; - } - } else { - tkStr += currCh; - } - getNextCh(); - } - getNextCh(); - tk = LEX_STR; - } else if (currCh=='\'') { - // strings again... - getNextCh(); - while (currCh && currCh!='\'') { - if (currCh == '\\') { - getNextCh(); - switch (currCh) { - case 'n' : tkStr += '\n'; break; - case '\'' : tkStr += '\''; break; - case '\\' : tkStr += '\\'; break; - default: tkStr += currCh; - } - } else { - tkStr += currCh; - } - getNextCh(); - } - getNextCh(); - tk = LEX_STR; - } else { - // single chars - tk = currCh; - if (currCh) getNextCh(); - if (tk=='=' && currCh=='=') { // == - tk = LEX_EQUAL; - getNextCh(); - if (currCh=='=') { // === - tk = LEX_TYPEEQUAL; - getNextCh(); - } - } else if (tk=='!' && currCh=='=') { // != - tk = LEX_NEQUAL; - getNextCh(); - if (currCh=='=') { // !== - tk = LEX_NTYPEEQUAL; - getNextCh(); - } - } else if (tk=='<' && currCh=='=') { - tk = LEX_LEQUAL; - getNextCh(); - } else if (tk=='<' && currCh=='<') { - tk = LEX_LSHIFT; - getNextCh(); - if (currCh=='=') { // <<= - tk = LEX_LSHIFTEQUAL; - getNextCh(); - } - } else if (tk=='>' && currCh=='=') { - tk = LEX_GEQUAL; - getNextCh(); - } else if (tk=='>' && currCh=='>') { - tk = LEX_RSHIFT; - getNextCh(); - if (currCh=='=') { // <<= - tk = LEX_RSHIFTEQUAL; - getNextCh(); - } - } else if (tk=='+' && currCh=='=') { - tk = LEX_PLUSEQUAL; - getNextCh(); - } else if (tk=='-' && currCh=='=') { - tk = LEX_MINUSEQUAL; - getNextCh(); - } else if (tk=='+' && currCh=='+') { - tk = LEX_PLUSPLUS; - getNextCh(); - } else if (tk=='-' && currCh=='-') { - tk = LEX_MINUSMINUS; - getNextCh(); - } else if (tk=='&' && currCh=='=') { - tk = LEX_ANDEQUAL; - getNextCh(); - } else if (tk=='&' && currCh=='&') { - tk = LEX_ANDAND; - getNextCh(); - } else if (tk=='|' && currCh=='=') { - tk = LEX_OREQUAL; - getNextCh(); - } else if (tk=='|' && currCh=='|') { - tk = LEX_OROR; - getNextCh(); - } else if (tk=='^' && currCh=='=') { - tk = LEX_XOREQUAL; - getNextCh(); - } - } - /* This isn't quite right yet */ - tokenLastEnd = tokenEnd; - tokenEnd = dataPos-3; + tk = LEX_EOF; + tkStr.clear(); + while (currCh && isWhitespace(currCh)) getNextCh(); + // newline comments + if (currCh=='/' && nextCh=='/') { + while (currCh && currCh!='\n') getNextCh(); + getNextCh(); + getNextToken(); + return; + } + // block comments + if (currCh=='/' && nextCh=='*') { + while (currCh && (currCh!='*' || nextCh!='/')) getNextCh(); + getNextCh(); + getNextCh(); + getNextToken(); + return; + } + // record beginning of this token + tokenStart = dataPos-2; + // tokens + if (isAlpha(currCh)) { // IDs + while (isAlpha(currCh) || isNumeric(currCh)) { + tkStr += currCh; + getNextCh(); + } + tk = LEX_ID; + if (tkStr=="if") tk = LEX_R_IF; + else if (tkStr=="else") tk = LEX_R_ELSE; + else if (tkStr=="do") tk = LEX_R_DO; + else if (tkStr=="while") tk = LEX_R_WHILE; + else if (tkStr=="for") tk = LEX_R_FOR; + else if (tkStr=="break") tk = LEX_R_BREAK; + else if (tkStr=="continue") tk = LEX_R_CONTINUE; + else if (tkStr=="function") tk = LEX_R_FUNCTION; + else if (tkStr=="return") tk = LEX_R_RETURN; + else if (tkStr=="var") tk = LEX_R_VAR; + else if (tkStr=="true") tk = LEX_R_TRUE; + else if (tkStr=="false") tk = LEX_R_FALSE; + else if (tkStr=="null") tk = LEX_R_NULL; + else if (tkStr=="undefined") tk = LEX_R_UNDEFINED; + else if (tkStr=="new") tk = LEX_R_NEW; + } else if (isNumeric(currCh)) { // Numbers + bool isHex = false; + if (currCh=='0') { tkStr += currCh; getNextCh(); } + if (currCh=='x') { + isHex = true; + tkStr += currCh; getNextCh(); + } + tk = LEX_INT; + while (isNumeric(currCh) || (isHex && isHexadecimal(currCh))) { + tkStr += currCh; + getNextCh(); + } + if (!isHex && currCh=='.') { + tk = LEX_FLOAT; + tkStr += '.'; + getNextCh(); + while (isNumeric(currCh)) { + tkStr += currCh; + getNextCh(); + } + } + // do fancy e-style floating point + if (!isHex && currCh=='e') { + tk = LEX_FLOAT; + tkStr += currCh; getNextCh(); + if (currCh=='-') { tkStr += currCh; getNextCh(); } + while (isNumeric(currCh)) { + tkStr += currCh; getNextCh(); + } + } + } else if (currCh=='"') { + // strings... + getNextCh(); + while (currCh && currCh!='"') { + if (currCh == '\\') { + getNextCh(); + switch (currCh) { + case 'n' : tkStr += '\n'; break; + case '"' : tkStr += '"'; break; + case '\\' : tkStr += '\\'; break; + default: tkStr += currCh; + } + } else { + tkStr += currCh; + } + getNextCh(); + } + getNextCh(); + tk = LEX_STR; + } else if (currCh=='\'') { + // strings again... + getNextCh(); + while (currCh && currCh!='\'') { + if (currCh == '\\') { + getNextCh(); + switch (currCh) { + case 'n' : tkStr += '\n'; break; + case '\'' : tkStr += '\''; break; + case '\\' : tkStr += '\\'; break; + default: tkStr += currCh; + } + } else { + tkStr += currCh; + } + getNextCh(); + } + getNextCh(); + tk = LEX_STR; + } else { + // single chars + tk = currCh; + if (currCh) getNextCh(); + if (tk=='=' && currCh=='=') { // == + tk = LEX_EQUAL; + getNextCh(); + if (currCh=='=') { // === + tk = LEX_TYPEEQUAL; + getNextCh(); + } + } else if (tk=='!' && currCh=='=') { // != + tk = LEX_NEQUAL; + getNextCh(); + if (currCh=='=') { // !== + tk = LEX_NTYPEEQUAL; + getNextCh(); + } + } else if (tk=='<' && currCh=='=') { + tk = LEX_LEQUAL; + getNextCh(); + } else if (tk=='<' && currCh=='<') { + tk = LEX_LSHIFT; + getNextCh(); + if (currCh=='=') { // <<= + tk = LEX_LSHIFTEQUAL; + getNextCh(); + } + } else if (tk=='>' && currCh=='=') { + tk = LEX_GEQUAL; + getNextCh(); + } else if (tk=='>' && currCh=='>') { + tk = LEX_RSHIFT; + getNextCh(); + if (currCh=='=') { // <<= + tk = LEX_RSHIFTEQUAL; + getNextCh(); + } + } else if (tk=='+' && currCh=='=') { + tk = LEX_PLUSEQUAL; + getNextCh(); + } else if (tk=='-' && currCh=='=') { + tk = LEX_MINUSEQUAL; + getNextCh(); + } else if (tk=='+' && currCh=='+') { + tk = LEX_PLUSPLUS; + getNextCh(); + } else if (tk=='-' && currCh=='-') { + tk = LEX_MINUSMINUS; + getNextCh(); + } else if (tk=='&' && currCh=='=') { + tk = LEX_ANDEQUAL; + getNextCh(); + } else if (tk=='&' && currCh=='&') { + tk = LEX_ANDAND; + getNextCh(); + } else if (tk=='|' && currCh=='=') { + tk = LEX_OREQUAL; + getNextCh(); + } else if (tk=='|' && currCh=='|') { + tk = LEX_OROR; + getNextCh(); + } else if (tk=='^' && currCh=='=') { + tk = LEX_XOREQUAL; + getNextCh(); + } + } + /* This isn't quite right yet */ + tokenLastEnd = tokenEnd; + tokenEnd = dataPos-3; } string CScriptLex::getSubString(int lastPosition) { - int lastCharIdx = tokenLastEnd+1; - if (lastCharIdx < dataEnd) { - /* save a memory alloc by using our data array to create the - substring */ - char old = data[lastCharIdx]; - data[lastCharIdx] = 0; - std::string value = &data[lastPosition]; - data[lastCharIdx] = old; - return value; - } else { - return std::string(&data[lastPosition]); - } + int lastCharIdx = tokenLastEnd+1; + if (lastCharIdx < dataEnd) { + /* save a memory alloc by using our data array to create the substring */ + char old = data[lastCharIdx]; + data[lastCharIdx] = 0; + std::string value = &data[lastPosition]; + data[lastCharIdx] = old; + return value; + } else { + return std::string(&data[lastPosition]); + } } CScriptLex *CScriptLex::getSubLex(int lastPosition) { - int lastCharIdx = tokenLastEnd+1; - if (lastCharIdx < dataEnd) - return new CScriptLex( this, lastPosition, lastCharIdx); - else - return new CScriptLex( this, lastPosition, dataEnd ); + int lastCharIdx = tokenLastEnd+1; + if (lastCharIdx < dataEnd) + return new CScriptLex( this, lastPosition, lastCharIdx); + else + return new CScriptLex( this, lastPosition, dataEnd ); } string CScriptLex::getPosition(int pos) { - if (pos<0) pos=tokenLastEnd; - int line = 1,col = 1; - for (int i=0;iname = name; - this->nextSibling = 0; - this->prevSibling = 0; - this->var = var->ref(); - this->owned = NULL; + this->name = name; + this->nextSibling = 0; + this->prevSibling = 0; + this->var = var->ref(); + this->owned = NULL; } CScriptVarLink::CScriptVarLink(const CScriptVarLink &link) { - // Copy constructor + // Copy constructor #if DEBUG_MEMORY - mark_allocated(this); + mark_allocated(this); #endif - this->name = link.name; - this->nextSibling = 0; - this->prevSibling = 0; - this->var = link.var->ref(); - this->owned = NULL; + this->name = link.name; + this->nextSibling = 0; + this->prevSibling = 0; + this->var = link.var->ref(); + this->owned = NULL; } CScriptVarLink::~CScriptVarLink() { #if DEBUG_MEMORY - mark_deallocated(this); + mark_deallocated(this); #endif - var->unref(); + var->unref(); } void CScriptVarLink::replaceWith(CScriptVar *newVar) { - CScriptVar *oldVar = var; - var = newVar->ref(); - oldVar->unref(); + CScriptVar *oldVar = var; + var = newVar->ref(); + oldVar->unref(); } void CScriptVarLink::replaceWith(CScriptVarLink *newVar) { - if (newVar) - replaceWith(newVar->var); - else - replaceWith(new CScriptVar()); + if (newVar) + replaceWith(newVar->var); + else + replaceWith(new CScriptVar()); } // ----------------------------------------------------------------------------------- CSCRIPTVAR CScriptVar::CScriptVar() { - refs = 0; + refs = 0; __proto__ = NULL; #if DEBUG_MEMORY - mark_allocated(this); + mark_allocated(this); #endif - init(); - flags = SCRIPTVAR_UNDEFINED; + init(); + flags = SCRIPTVAR_UNDEFINED; } CScriptVar::CScriptVar(const string &str) { - refs = 0; + refs = 0; __proto__ = NULL; #if DEBUG_MEMORY - mark_allocated(this); + mark_allocated(this); #endif - init(); - flags = SCRIPTVAR_STRING; - data = str; + init(); + flags = SCRIPTVAR_STRING; + data = str; } CScriptVar::CScriptVar(const string &varData, int varFlags) { - refs = 0; + refs = 0; __proto__ = NULL; #if DEBUG_MEMORY - mark_allocated(this); + mark_allocated(this); #endif - init(); - flags = varFlags; - if (varFlags & SCRIPTVAR_INTEGER) { - intData = strtol(varData.c_str(),0,0); - } else if (varFlags & SCRIPTVAR_DOUBLE) { - doubleData = strtod(varData.c_str(),0); - } else - data = varData; + init(); + flags = varFlags; + if (varFlags & SCRIPTVAR_INTEGER) { + intData = strtol(varData.c_str(),0,0); + } else if (varFlags & SCRIPTVAR_DOUBLE) { + doubleData = strtod(varData.c_str(),0); + } else + data = varData; } CScriptVar::CScriptVar(double val) { - refs = 0; + refs = 0; __proto__ = NULL; #if DEBUG_MEMORY - mark_allocated(this); + mark_allocated(this); #endif - init(); - setDouble(val); + init(); + setDouble(val); } CScriptVar::CScriptVar(int val) { - refs = 0; + refs = 0; __proto__ = NULL; #if DEBUG_MEMORY - mark_allocated(this); + mark_allocated(this); #endif - init(); - setInt(val); + init(); + setInt(val); } CScriptVar::CScriptVar(bool val) { - refs = 0; + refs = 0; __proto__ = NULL; #if DEBUG_MEMORY - mark_allocated(this); + mark_allocated(this); #endif - init(); - setBool(val); + init(); + setBool(val); } CScriptVar::~CScriptVar(void) { #if DEBUG_MEMORY - mark_deallocated(this); + mark_deallocated(this); #endif - removeAllChildren(); + removeAllChildren(); if(flags & SCRIPTVAR_NATIVE_MFNC) delete jsCallbackClass; } void CScriptVar::init() { - firstChild = 0; - lastChild = 0; - flags = 0; - jsCallback = 0; - jsCallbackUserData = 0; - data = TINYJS_BLANK_DATA; - intData = 0; - doubleData = 0; +//// firstChild = 0; +//// lastChild = 0; + flags = 0; + jsCallback = 0; + jsCallbackUserData = 0; + data = TINYJS_BLANK_DATA; + intData = 0; + doubleData = 0; } CScriptVar *CScriptVar::getReturnVar() { - return getParameter(TINYJS_RETURN_VAR); + return getParameter(TINYJS_RETURN_VAR); } void CScriptVar::setReturnVar(CScriptVar *var) { - findChildOrCreate(TINYJS_RETURN_VAR)->replaceWith(var); + findChildOrCreate(TINYJS_RETURN_VAR)->replaceWith(var); } CScriptVar *CScriptVar::getParameter(const std::string &name) { - return findChildOrCreate(name)->var; + return findChildOrCreate(name)->var; } + CScriptVarLink *CScriptVar::findChild(const string &childName) { - CScriptVarLink *v = firstChild; - while (v) { - if (v->name.compare(childName)==0) - return v; - v = v->nextSibling; - } - return 0; + SCRIPTVAR_CHILDS::iterator it = Childs.find(childName); + if(it != Childs.end()) + return it->second; +//// +/* + CScriptVarLink *v = firstChild; + while (v) { + if (v->name.compare(childName)==0) + return v; + v = v->nextSibling; + } +*/ +//// + return NULL; } CScriptVarLink *CScriptVar::findChildOrCreate(const string &childName, int varFlags) { - CScriptVarLink *l = findChild(childName); - if (l) return l; + CScriptVarLink *l = findChild(childName); + if (l) return l; + return addChild(childName, new CScriptVar(TINYJS_BLANK_DATA, varFlags)); +//// +/* - return addChild(childName, new CScriptVar(TINYJS_BLANK_DATA, varFlags)); + return addChild(childName, new CScriptVar(TINYJS_BLANK_DATA, varFlags)); +*/ +//// } CScriptVarLink *CScriptVar::findChildOrCreateByPath(const std::string &path) { size_t p = path.find('.'); if (p == string::npos) - return findChildOrCreate(path); + return findChildOrCreate(path); return findChildOrCreate(path.substr(0,p), SCRIPTVAR_OBJECT)->var-> - findChildOrCreateByPath(path.substr(p+1)); + findChildOrCreateByPath(path.substr(p+1)); } CScriptVarLink *CScriptVar::addChild(const std::string &childName, CScriptVar *child) { if (isUndefined()) { - flags = SCRIPTVAR_OBJECT; + flags = SCRIPTVAR_OBJECT; } - // if no child supplied, create one - if (!child) - child = new CScriptVar(); - - CScriptVarLink *link = new CScriptVarLink(child, childName); - link->owned = this; - if (lastChild) { - lastChild->nextSibling = link; - link->prevSibling = lastChild; - lastChild = link; - } else { - firstChild = link; - lastChild = link; - } - return link; + // if no child supplied, create one + if (!child) + child = new CScriptVar(); +#ifdef _DEBUG + if(findChild(childName)) + ASSERT(0); // addCild - the child exists +#endif + CScriptVarLink *link = new CScriptVarLink(child, childName); + link->owned = this; + return Childs[childName] = link; +//// +/* + if (lastChild) { + lastChild->nextSibling = link; + link->prevSibling = lastChild; + lastChild = link; + } else { + firstChild = link; + lastChild = link; + } + return link; +*/ +//// } - CScriptVarLink *CScriptVar::addChildNoDup(const std::string &childName, CScriptVar *child) { - // if no child supplied, create one - if (!child) - child = new CScriptVar(); - - CScriptVarLink *v = findChild(childName); - if (v) { - v->replaceWith(child); - } else { - v = addChild(childName, child); - } + // if no child supplied, create one + if (!child) + child = new CScriptVar(); + + CScriptVarLink *v = findChild(childName); + if (v) { + v->replaceWith(child); + } else { + CScriptVarLink *link = new CScriptVarLink(child, childName); + link->owned = this; + v = Childs[childName] = link; +//// v = addChild(childName, child); + } - return v; + return v; } void CScriptVar::removeChild(CScriptVar *child) { - CScriptVarLink *link = firstChild; - while (link) { - if (link->var == child) - break; - link = link->nextSibling; - } - ASSERT(link); - removeLink(link); + for(SCRIPTVAR_CHILDS::iterator it = Childs.begin(); it != Childs.end(); ++it) { + if(it->second->var == child) { + removeLink(it->second); + break; + } + } + ASSERT(0);// removeChild - the child is not atached to this var +//// +/* + CScriptVarLink *link = firstChild; + while (link) { + if (link->var == child) + break; + link = link->nextSibling; + } + ASSERT(link); + removeLink(link); +*/ +//// } void CScriptVar::removeLink(CScriptVarLink *link) { - if (!link) return; - if (link->nextSibling) - link->nextSibling->prevSibling = link->prevSibling; - if (link->prevSibling) - link->prevSibling->nextSibling = link->nextSibling; - if (lastChild == link) - lastChild = link->prevSibling; - if (firstChild == link) - firstChild = link->nextSibling; - delete link; + if (!link) return; +#ifdef _DEBUG + if(findChild(link->name) != link) + ASSERT(0); // removeLink - the link is not atached to this var +#endif + Childs.erase(link->name); +//// +/* + if (link->nextSibling) + link->nextSibling->prevSibling = link->prevSibling; + if (link->prevSibling) + link->prevSibling->nextSibling = link->nextSibling; + if (lastChild == link) + lastChild = link->prevSibling; + if (firstChild == link) + firstChild = link->nextSibling; +*/ +//// + delete link; } void CScriptVar::removeAllChildren() { - CScriptVarLink *c = firstChild; - while (c) { - CScriptVarLink *t = c->nextSibling; - delete c; - c = t; - } - firstChild = 0; - lastChild = 0; + SCRIPTVAR_CHILDS::iterator it; + while((it = Childs.begin()) != Childs.end()) { + delete it->second; + Childs.erase(it); + } +//// +/* + CScriptVarLink *c = firstChild; + while (c) { + CScriptVarLink *t = c->nextSibling; + delete c; + c = t; + } + firstChild = 0; + lastChild = 0; +*/ +//// } CScriptVar *CScriptVar::getArrayIndex(int idx) { - char sIdx[64]; - sprintf_s(sIdx, sizeof(sIdx), "%d", idx); - CScriptVarLink *link = findChild(sIdx); - if (link) return link->var; - else return new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_NULL); // undefined + char sIdx[64]; + sprintf_s(sIdx, sizeof(sIdx), "%d", idx); + CScriptVarLink *link = findChild(sIdx); + if (link) return link->var; + else return new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_NULL); // undefined } void CScriptVar::setArrayIndex(int idx, CScriptVar *value) { - char sIdx[64]; - sprintf_s(sIdx, sizeof(sIdx), "%d", idx); - CScriptVarLink *link = findChild(sIdx); + char sIdx[64]; + sprintf_s(sIdx, sizeof(sIdx), "%d", idx); + CScriptVarLink *link = findChild(sIdx); - if (link) { - if (value->isUndefined()) - removeLink(link); - else - link->replaceWith(value); - } else { - if (!value->isUndefined()) - addChild(sIdx, value); - } + if (link) { + if (value->isUndefined()) + removeLink(link); + else + link->replaceWith(value); + } else { + if (!value->isUndefined()) + addChild(sIdx, value); + } } int CScriptVar::getArrayLength() { - int highest = -1; - if (!isArray()) return 0; + int highest = -1; + if (!isArray()) return 0; - CScriptVarLink *link = firstChild; - while (link) { - if (isNumber(link->name)) { - int val = atoi(link->name.c_str()); - if (val > highest) highest = val; - } - link = link->nextSibling; - } - return highest+1; + for(SCRIPTVAR_CHILDS::iterator it = Childs.begin(); it != Childs.end(); ++it) { + if (isNumber(it->first)) { + int val = atoi(it->first.c_str()); + if (val > highest) highest = val; + } + } +//// +/* + CScriptVarLink *link = firstChild; + while (link) { + if (isNumber(link->name)) { + int val = atoi(link->name.c_str()); + if (val > highest) highest = val; + } + link = link->nextSibling; + } +*/ +//// + return highest+1; } - +//// moved to TinyJS.h +/* int CScriptVar::getChildren() { - int n = 0; - CScriptVarLink *link = firstChild; - while (link) { - n++; - link = link->nextSibling; - } - return n; + int n = 0; + CScriptVarLink *link = firstChild; + while (link) { + n++; + link = link->nextSibling; + } + return n; } +*/ +//// int CScriptVar::getInt() { - /* strtol understands about hex and octal */ - if (isInt()) return intData; + /* strtol understands about hex and octal */ + if (isInt()) return intData; // if (isNull()) return 0; // if (isUndefined()) return 0; - if (isDouble()) return (int)doubleData; - return 0; + if (isDouble()) return (int)doubleData; + return 0; } bool CScriptVar::getBool() { - if (isInt()) return intData!=0; - if (isNull()) return 0; - if (isUndefined()) return 0; - if (isDouble()) return doubleData!=0; + if (isInt()) return intData!=0; + if (isNull()) return 0; + if (isUndefined()) return 0; + if (isDouble()) return doubleData!=0; if (isString()) return data.length()!=0; - return 0; + return 0; } double CScriptVar::getDouble() { - if (isDouble()) return doubleData; - if (isInt() || isBool()) intData; - if (isNull()) return 0; - if (isUndefined()) return 0; - return 0; /* or NaN? */ + if (isDouble()) return doubleData; + if (isInt() || isBool()) intData; + if (isNull()) return 0; + if (isUndefined()) return 0; + return 0; /* or NaN? */ } const string &CScriptVar::getString() { - /* Because we can't return a string that is generated on demand. - * I should really just use char* :) */ - static string s_null = "null"; - static string s_undefined = "undefined"; - static string s_true = "true"; - static string s_false = "false"; + /* Because we can't return a string that is generated on demand. + * I should really just use char* :) */ + static string s_null = "null"; + static string s_undefined = "undefined"; + static string s_true = "true"; + static string s_false = "false"; if (isBool()) return getInt() ? s_true : s_false; - if (isInt()) { - char buffer[32]; - sprintf_s(buffer, sizeof(buffer), "%ld", intData); - data = buffer; - return data; - } - if (isDouble()) { - char buffer[32]; - sprintf_s(buffer, sizeof(buffer), "%lf", doubleData); - data = buffer; - return data; - } - if (isNull()) return s_null; - if (isUndefined()) return s_undefined; - // are we just a string here? - return data; + if (isInt()) { + char buffer[32]; + sprintf_s(buffer, sizeof(buffer), "%ld", intData); + data = buffer; + return data; + } + if (isDouble()) { + char buffer[32]; + sprintf_s(buffer, sizeof(buffer), "%lf", doubleData); + data = buffer; + return data; + } + if (isNull()) return s_null; + if (isUndefined()) return s_undefined; + // are we just a string here? + return data; } void CScriptVar::setInt(int val) { - flags = (flags&~SCRIPTVAR_VARTYPEMASK) | SCRIPTVAR_INTEGER; - intData = val; - doubleData = 0; - data = TINYJS_BLANK_DATA; + flags = (flags&~SCRIPTVAR_VARTYPEMASK) | SCRIPTVAR_INTEGER; + intData = val; + doubleData = 0; + data = TINYJS_BLANK_DATA; } void CScriptVar::setBool(bool val) { - flags = (flags&~SCRIPTVAR_VARTYPEMASK) | SCRIPTVAR_BOOLEAN; - intData = val?1:0; - doubleData = 0; - data = TINYJS_BLANK_DATA; + flags = (flags&~SCRIPTVAR_VARTYPEMASK) | SCRIPTVAR_BOOLEAN; + intData = val?1:0; + doubleData = 0; + data = TINYJS_BLANK_DATA; } void CScriptVar::setDouble(double val) { - flags = (flags&~SCRIPTVAR_VARTYPEMASK) | SCRIPTVAR_DOUBLE; - doubleData = val; - intData = 0; - data = TINYJS_BLANK_DATA; + flags = (flags&~SCRIPTVAR_VARTYPEMASK) | SCRIPTVAR_DOUBLE; + doubleData = val; + intData = 0; + data = TINYJS_BLANK_DATA; } void CScriptVar::setString(const string &str) { - // name sure it's not still a number or integer - flags = (flags&~SCRIPTVAR_VARTYPEMASK) | SCRIPTVAR_STRING; - data = str; - intData = 0; - doubleData = 0; + // name sure it's not still a number or integer + flags = (flags&~SCRIPTVAR_VARTYPEMASK) | SCRIPTVAR_STRING; + data = str; + intData = 0; + doubleData = 0; } void CScriptVar::setUndefined() { - // name sure it's not still a number or integer - flags = (flags&~SCRIPTVAR_VARTYPEMASK) | SCRIPTVAR_UNDEFINED; - data = TINYJS_BLANK_DATA; - intData = 0; - doubleData = 0; - removeAllChildren(); + // name sure it's not still a number or integer + flags = (flags&~SCRIPTVAR_VARTYPEMASK) | SCRIPTVAR_UNDEFINED; + data = TINYJS_BLANK_DATA; + intData = 0; + doubleData = 0; + removeAllChildren(); } template @@ -1001,151 +1067,186 @@ CScriptVar *DoMaths(CScriptVar *a, CScriptVar *b, int op) T da = (T)(a->isDouble()?a->getDouble():dai); T db = (T)(b->isDouble()?b->getDouble():dbi); switch (op) { - case '+': return new CScriptVar(da+db); - case '-': return new CScriptVar(da-db); - case '*': return new CScriptVar(da*db); - case '/': return new CScriptVar(da/db); - case '&': return new CScriptVar(dai&dbi); - case '|': return new CScriptVar(dai|dbi); - case '^': return new CScriptVar(dai^dbi); - case '%': return new CScriptVar(dai%dbi); - case '~': return new CScriptVar(~dai); - case LEX_LSHIFT: return new CScriptVar(dai<>dbi); - case LEX_EQUAL: return new CScriptVar(da==db); - case LEX_NEQUAL: return new CScriptVar(da!=db); - case '<': return new CScriptVar(da': return new CScriptVar(da>db); - case LEX_GEQUAL: return new CScriptVar(da>=db); - default: throw new CScriptException("This operation not supported on the int datatype"); - } + case '+': return new CScriptVar(da+db); + case '-': return new CScriptVar(da-db); + case '*': return new CScriptVar(da*db); + case '/': return new CScriptVar(da/db); + case '&': return new CScriptVar(dai&dbi); + case '|': return new CScriptVar(dai|dbi); + case '^': return new CScriptVar(dai^dbi); + case '%': return new CScriptVar(dai%dbi); + case '~': return new CScriptVar(~dai); + case LEX_LSHIFT: return new CScriptVar(dai<>dbi); + case LEX_EQUAL: return new CScriptVar(da==db); + case LEX_NEQUAL: return new CScriptVar(da!=db); + case '<': return new CScriptVar(da': return new CScriptVar(da>db); + case LEX_GEQUAL: return new CScriptVar(da>=db); + default: throw new CScriptException("This operation not supported on the int datatype"); + } } CScriptVar *CScriptVar::mathsOp(CScriptVar *b, int op) { - CScriptVar *a = this; - // TODO Equality checks on classes/structures - // Type equality check - if (op == LEX_TYPEEQUAL || op == LEX_NTYPEEQUAL) { - // check type first, then call again to check data - bool eql = ((a->flags & SCRIPTVAR_VARTYPEMASK) == - (b->flags & SCRIPTVAR_VARTYPEMASK)) && - a->mathsOp(b, LEX_EQUAL); - if (op == LEX_TYPEEQUAL) - return new CScriptVar(eql); - else - return new CScriptVar(!eql); - } - // do maths... - if (a->isUndefined() && b->isUndefined()) { - if (op == LEX_EQUAL) return new CScriptVar(true); - else if (op == LEX_NEQUAL) return new CScriptVar(false); - else return new CScriptVar(); // undefined - } else if ((a->isNumeric() || a->isUndefined()) && - (b->isNumeric() || b->isUndefined())) { - if (!a->isDouble() && !b->isDouble()) { - // use ints + CScriptVar *a = this; + // TODO Equality checks on classes/structures + // Type equality check + if (op == LEX_TYPEEQUAL || op == LEX_NTYPEEQUAL) { + // check type first, then call again to check data + bool eql = ((a->flags & SCRIPTVAR_VARTYPEMASK) == + (b->flags & SCRIPTVAR_VARTYPEMASK)) && + a->mathsOp(b, LEX_EQUAL); + if (op == LEX_TYPEEQUAL) + return new CScriptVar(eql); + else + return new CScriptVar(!eql); + } + // do maths... + if (a->isUndefined() && b->isUndefined()) { + if (op == LEX_EQUAL) return new CScriptVar(true); + else if (op == LEX_NEQUAL) return new CScriptVar(false); + else return new CScriptVar(); // undefined + } else if ((a->isNumeric() || a->isUndefined()) && + (b->isNumeric() || b->isUndefined())) { + if (!a->isDouble() && !b->isDouble()) { + // use ints return DoMaths(a, b, op); - } else { - // use doubles + } else { + // use doubles return DoMaths(a, b, op); } - } else if (a->isArray()) { - /* Just check pointers */ - switch (op) { - case LEX_EQUAL: return new CScriptVar(a==b); - case LEX_NEQUAL: return new CScriptVar(a!=b); - default: throw new CScriptException("This operation not supported on the Array datatype"); - } - } else if (a->isObject()) { - /* Just check pointers */ - switch (op) { - case LEX_EQUAL: return new CScriptVar(a==b); - case LEX_NEQUAL: return new CScriptVar(a!=b); - default: throw new CScriptException("This operation not supported on the Object datatype"); - } - } else { - string da = a->getString(); - string db = b->getString(); - // use strings - switch (op) { - case '+': return new CScriptVar(da+db, SCRIPTVAR_STRING); - case LEX_EQUAL: return new CScriptVar(da==db); - case LEX_NEQUAL: return new CScriptVar(da!=db); - case '<': return new CScriptVar(da': return new CScriptVar(da>db); - case LEX_GEQUAL: return new CScriptVar(da>=db); - default: throw new CScriptException("This operation not supported on the string datatype"); - } - } - ASSERT(0); - return 0; + } else if (a->isArray()) { + /* Just check pointers */ + switch (op) { + case LEX_EQUAL: return new CScriptVar(a==b); + case LEX_NEQUAL: return new CScriptVar(a!=b); + default: throw new CScriptException("This operation not supported on the Array datatype"); + } + } else if (a->isObject()) { + /* Just check pointers */ + switch (op) { + case LEX_EQUAL: return new CScriptVar(a==b); + case LEX_NEQUAL: return new CScriptVar(a!=b); + default: throw new CScriptException("This operation not supported on the Object datatype"); + } + } else { + string da = a->getString(); + string db = b->getString(); + // use strings + switch (op) { + case '+': return new CScriptVar(da+db, SCRIPTVAR_STRING); + case LEX_EQUAL: return new CScriptVar(da==db); + case LEX_NEQUAL: return new CScriptVar(da!=db); + case '<': return new CScriptVar(da': return new CScriptVar(da>db); + case LEX_GEQUAL: return new CScriptVar(da>=db); + default: throw new CScriptException("This operation not supported on the string datatype"); + } + } + ASSERT(0); + return 0; } void CScriptVar::copySimpleData(CScriptVar *val) { - data = val->data; - intData = val->intData; - doubleData = val->doubleData; - flags = (flags & ~SCRIPTVAR_VARTYPEMASK) | (val->flags & SCRIPTVAR_VARTYPEMASK); + data = val->data; + intData = val->intData; + doubleData = val->doubleData; + flags = (flags & ~SCRIPTVAR_VARTYPEMASK) | (val->flags & SCRIPTVAR_VARTYPEMASK); } void CScriptVar::copyValue(CScriptVar *val) { - if (val) { - copySimpleData(val); - // remove all current children - removeAllChildren(); - // copy children of 'val' - CScriptVarLink *child = val->firstChild; - while (child) { - CScriptVar *copied; - // don't copy the 'parent' object... - if (child->name != TINYJS_PROTOTYPE_CLASS) - copied = child->var->deepCopy(); - else - copied = child->var; - - addChild(child->name, copied); - - child = child->nextSibling; - } - } else { - setUndefined(); - } + if (val) { + copySimpleData(val); + // remove all current children + removeAllChildren(); + // copy children of 'val' +//// +/* + CScriptVarLink *child = val->firstChild; + while (child) { + CScriptVar *copied; + // don't copy the 'parent' object... + if (child->name != TINYJS_PROTOTYPE_CLASS) + copied = child->var->deepCopy(); + else + copied = child->var; + + addChild(child->name, copied); + + child = child->nextSibling; + } +*/ +//// + for(SCRIPTVAR_CHILDS::iterator it = val->Childs.begin(); it != val->Childs.end(); ++it) { + CScriptVar *copied; + // don't copy the 'parent' object... + if (it->first != TINYJS_PROTOTYPE_CLASS) + copied = it->second->var->deepCopy(); + else + copied = it->second->var; + + addChild(it->first, copied); + } + } else { + setUndefined(); + } } CScriptVar *CScriptVar::deepCopy() { - CScriptVar *newVar = new CScriptVar(); - newVar->copySimpleData(this); - // copy children - CScriptVarLink *child = firstChild; - while (child) { - CScriptVar *copied; - // don't copy the 'parent' object... - if (child->name != TINYJS_PROTOTYPE_CLASS) - copied = child->var->deepCopy(); - else - copied = child->var; - - newVar->addChild(child->name, copied); - child = child->nextSibling; - } - return newVar; + CScriptVar *newVar = new CScriptVar(); + newVar->copySimpleData(this); + // copy children +//// +/* + CScriptVarLink *child = firstChild; + while (child) { + CScriptVar *copied; + // don't copy the 'parent' object... + if (child->name != TINYJS_PROTOTYPE_CLASS) + copied = child->var->deepCopy(); + else + copied = child->var; + + newVar->addChild(child->name, copied); + child = child->nextSibling; + } +*/ +//// + for(SCRIPTVAR_CHILDS::iterator it = Childs.begin(); it != Childs.end(); ++it) { + CScriptVar *copied; + // don't copy the 'parent' object... + if (it->first != TINYJS_PROTOTYPE_CLASS) + copied = it->second->var->deepCopy(); + else + copied = it->second->var; + + newVar->addChild(it->first, copied); + } + return newVar; } void CScriptVar::trace(string indentStr, const string &name) { - TRACE("%s'%s' = '%s' %s\n", - indentStr.c_str(), - name.c_str(), - getString().c_str(), - getFlagsAsString().c_str()); - string indent = indentStr+" "; - CScriptVarLink *link = firstChild; - while (link) { - link->var->trace(indent, link->name); - link = link->nextSibling; - } + TRACE("%s'%s' = '%s' %s\n", + indentStr.c_str(), + name.c_str(), + getString().c_str(), + getFlagsAsString().c_str()); + string indent = indentStr+" "; +//// +/* + CScriptVarLink *link = firstChild; + while (link) { + link->var->trace(indent, link->name); + link = link->nextSibling; + } +*/ +//// + for(SCRIPTVAR_CHILDS::iterator it = Childs.begin(); it != Childs.end(); ++it) { + it->second->var->trace(indent, it->first); + } } string CScriptVar::getFlagsAsString() { @@ -1164,231 +1265,258 @@ string CScriptVar::getFlagsAsString() { string CScriptVar::getParsableString() { // Numbers can just be put in directly if (isNumeric()) - return getString(); + return getString(); if (isFunction()) { - ostringstream funcStr; - funcStr << "function ("; - // get list of parameters - CScriptVarLink *link = firstChild; - while (link) { - funcStr << link->name; - if (link->nextSibling) funcStr << ","; - link = link->nextSibling; - } - // add function body - funcStr << ") " << getString(); - return funcStr.str(); + ostringstream funcStr; + funcStr << "function ("; + // get list of parameters + SCRIPTVAR_CHILDS::iterator last_it = --(Childs.end());//// --last_it; + for(SCRIPTVAR_CHILDS::iterator it = Childs.begin(); it != Childs.end(); ++it) { + funcStr << it->first; + if (it != last_it) funcStr << ", "; + } +//// +/* + CScriptVarLink *link = firstChild; + while (link) { + funcStr << link->name; + if (link->nextSibling) funcStr << ","; + link = link->nextSibling; + } +*/ +//// + // add function body + funcStr << ") " << getString(); + return funcStr.str(); } // if it is a string then we quote it if (isString()) - return getJSString(getString()); + return getJSString(getString()); if (isNull()) - return "null"; + return "null"; return "undefined"; } void CScriptVar::getJSON(ostringstream &destination, const string linePrefix) { - if (isObject()) { - string indentedLinePrefix = linePrefix+" "; - // children - handle with bracketed list - destination << "{ \n"; - CScriptVarLink *link = firstChild; - while (link) { - destination << indentedLinePrefix; - if (isAlphaNum(link->name)) - destination << link->name; - else - destination << getJSString(link->name); - destination << " : "; - link->var->getJSON(destination, indentedLinePrefix); - link = link->nextSibling; - if (link) { - destination << ",\n"; - } - } - destination << "\n" << linePrefix << "}"; - } else if (isArray()) { - string indentedLinePrefix = linePrefix+" "; - destination << "[\n"; - int len = getArrayLength(); - if (len>10000) len=10000; // we don't want to get stuck here! - - for (int i=0;igetJSON(destination, indentedLinePrefix); - if (ifirst)) + destination << it->first; + else + destination << getJSString(it->first); + destination << " : "; + it->second->var->getJSON(destination, indentedLinePrefix); + if (it != last_it) { + destination << ",\n"; + } + } + destination << "\n" << linePrefix << "}"; +//// +/* + CScriptVarLink *link = firstChild; + while (link) { + destination << indentedLinePrefix; + if (isAlphaNum(link->name)) + destination << link->name; + else + destination << getJSString(link->name); + destination << " : "; + link->var->getJSON(destination, indentedLinePrefix); + link = link->nextSibling; + if (link) { + destination << ",\n"; + } + } + destination << "\n" << linePrefix << "}"; +*/ +//// + } else if (isArray()) { + string indentedLinePrefix = linePrefix+" "; + destination << "[\n"; + int len = getArrayLength(); + if (len>10000) len=10000; // we don't want to get stuck here! + + for (int i=0;igetJSON(destination, indentedLinePrefix); + if (iref(); - // Add built-in classes - stringClass = (new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT))->ref(); - arrayClass = (new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT))->ref(); - objectClass = (new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT))->ref(); - root->addChild("String", stringClass); - root->addChild("Array", arrayClass); - root->addChild("Object", objectClass); + root = (new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT))->ref(); + // Add built-in classes + stringClass = (new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT))->ref(); + arrayClass = (new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT))->ref(); + objectClass = (new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT))->ref(); + root->addChild("String", stringClass); + root->addChild("Array", arrayClass); + root->addChild("Object", objectClass); addNative("function eval(jsCode)", this, &CTinyJS::scEval); // execute the given string and return the resul } CTinyJS::~CTinyJS() { - ASSERT(!l); - scopes.clear(); - stringClass->unref(); - arrayClass->unref(); - objectClass->unref(); - root->unref(); + ASSERT(!l); + scopes.clear(); + stringClass->unref(); + arrayClass->unref(); + objectClass->unref(); + root->unref(); #if DEBUG_MEMORY - show_allocated(); + show_allocated(); #endif } void CTinyJS::trace() { - root->trace(); + root->trace(); } void CTinyJS::execute(const string &code) { - CScriptLex *oldLex = l; - vector oldScopes = scopes; - l = new CScriptLex(code); - scopes.clear(); - scopes.push_back(root); - try { - bool execute = true; - while (l->tk) statement(execute); - } catch (CScriptException *e) { - ostringstream msg; - msg << "Error " << e->text << " at " << l->getPosition(); - delete l; - l = oldLex; - throw new CScriptException(msg.str()); - } - delete l; - l = oldLex; - scopes = oldScopes; + CScriptLex *oldLex = l; + vector oldScopes = scopes; + l = new CScriptLex(code); + scopes.clear(); + scopes.push_back(root); + try { + bool execute = true; + while (l->tk) statement(execute); + } catch (CScriptException *e) { + ostringstream msg; + msg << "Error " << e->text << " at " << l->getPosition(); + delete l; + l = oldLex; + throw new CScriptException(msg.str()); + } + delete l; + l = oldLex; + scopes = oldScopes; } CScriptVarLink CTinyJS::evaluateComplex(const string &code) { - CScriptLex *oldLex = l; - vector oldScopes = scopes; - - l = new CScriptLex(code); - scopes.clear(); - scopes.push_back(root); - CScriptVarLink *v = 0; - try { - bool execute = true; - do { - CLEAN(v); - v = base(execute); - if (l->tk!=LEX_EOF) l->match(';'); - } while (l->tk!=LEX_EOF); - } catch (CScriptException *e) { - ostringstream msg; - msg << "Error " << e->text << " at " << l->getPosition(); - delete l; - l = oldLex; - throw new CScriptException(msg.str()); - } - delete l; - l = oldLex; - scopes = oldScopes; - - if (v) { - CScriptVarLink r = *v; - CLEAN(v); - return r; - } - // return undefined... - return CScriptVarLink(new CScriptVar()); + CScriptLex *oldLex = l; + vector oldScopes = scopes; + + l = new CScriptLex(code); + scopes.clear(); + scopes.push_back(root); + CScriptVarLink *v = 0; + try { + bool execute = true; + do { + CLEAN(v); + v = base(execute); + if (l->tk!=LEX_EOF) l->match(';'); + } while (l->tk!=LEX_EOF); + } catch (CScriptException *e) { + ostringstream msg; + msg << "Error " << e->text << " at " << l->getPosition(); + delete l; + l = oldLex; + throw new CScriptException(msg.str()); + } + delete l; + l = oldLex; + scopes = oldScopes; + + if (v) { + CScriptVarLink r = *v; + CLEAN(v); + return r; + } + // return undefined... + return CScriptVarLink(new CScriptVar()); } string CTinyJS::evaluate(const string &code) { - return evaluateComplex(code).var->getString(); + return evaluateComplex(code).var->getString(); } void CTinyJS::parseFunctionArguments(CScriptVar *funcVar) { l->match('('); while (l->tk!=')') { - funcVar->addChildNoDup(l->tkStr); - l->match(LEX_ID); - if (l->tk!=')') l->match(','); + funcVar->addChildNoDup(l->tkStr); + l->match(LEX_ID); + if (l->tk!=')') l->match(','); } l->match(')'); } void CTinyJS::addNative(const string &funcDesc, JSCallback ptr, void *userdata) { CScriptVar *funcVar = addNative(funcDesc); - funcVar->setCallback(ptr, userdata); + funcVar->setCallback(ptr, userdata); } void CTinyJS::addNative(const string &funcDesc, NativeFncBase *ptr, void *userdata) { CScriptVar *funcVar = addNative(funcDesc); - funcVar->setCallback(ptr, userdata); + funcVar->setCallback(ptr, userdata); } CScriptVar *CTinyJS::addNative(const string &funcDesc) { - CScriptLex *oldLex = l; - l = new CScriptLex(funcDesc); - CScriptVar *base = root; - - l->match(LEX_R_FUNCTION); - string funcName = l->tkStr; - l->match(LEX_ID); - /* Check for dots, we might want to do something like function String.substring ... */ - while (l->tk == '.') { - l->match('.'); - CScriptVarLink *link = base->findChild(funcName); - // if it doesn't exist, make an object class - if (!link) link = base->addChild(funcName, new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT)); - base = link->var; - funcName = l->tkStr; - l->match(LEX_ID); - } - - CScriptVar *funcVar = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_FUNCTION); - parseFunctionArguments(funcVar); - delete l; - l = oldLex; - - base->addChild(funcName, funcVar); + CScriptLex *oldLex = l; + l = new CScriptLex(funcDesc); + CScriptVar *base = root; + + l->match(LEX_R_FUNCTION); + string funcName = l->tkStr; + l->match(LEX_ID); + /* Check for dots, we might want to do something like function String.substring ... */ + while (l->tk == '.') { + l->match('.'); + CScriptVarLink *link = base->findChild(funcName); + // if it doesn't exist, make an object class + if (!link) link = base->addChild(funcName, new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT)); + base = link->var; + funcName = l->tkStr; + l->match(LEX_ID); + } + + CScriptVar *funcVar = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_FUNCTION); + parseFunctionArguments(funcVar); + delete l; + l = oldLex; + + base->addChild(funcName, funcVar); return funcVar; } @@ -1398,8 +1526,8 @@ CScriptVarLink *CTinyJS::parseFunctionDefinition() { string funcName = TINYJS_TEMP_NAME; /* we can have functions without names */ if (l->tk==LEX_ID) { - funcName = l->tkStr; - l->match(LEX_ID); + funcName = l->tkStr; + l->match(LEX_ID); } CScriptVarLink *funcVar = new CScriptVarLink(new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_FUNCTION), funcName); parseFunctionArguments(funcVar->var); @@ -1412,23 +1540,23 @@ CScriptVarLink *CTinyJS::parseFunctionDefinition() { CScriptVarLink *CTinyJS::factor(bool &execute) { if (l->tk==LEX_R_TRUE) { - l->match(LEX_R_TRUE); - return new CScriptVarLink(new CScriptVar(true)); - } - if (l->tk==LEX_R_FALSE) { - l->match(LEX_R_FALSE); - return new CScriptVarLink(new CScriptVar(false)); - } - if (l->tk==LEX_R_NULL) { - l->match(LEX_R_NULL); - return new CScriptVarLink(new CScriptVar(TINYJS_BLANK_DATA,SCRIPTVAR_NULL)); - } - if (l->tk==LEX_R_UNDEFINED) { - l->match(LEX_R_UNDEFINED); - return new CScriptVarLink(new CScriptVar(TINYJS_BLANK_DATA,SCRIPTVAR_UNDEFINED)); - } - if (l->tk==LEX_ID || l->tk=='(') { - CScriptVarLink *a; + l->match(LEX_R_TRUE); + return new CScriptVarLink(new CScriptVar(true)); + } + if (l->tk==LEX_R_FALSE) { + l->match(LEX_R_FALSE); + return new CScriptVarLink(new CScriptVar(false)); + } + if (l->tk==LEX_R_NULL) { + l->match(LEX_R_NULL); + return new CScriptVarLink(new CScriptVar(TINYJS_BLANK_DATA,SCRIPTVAR_NULL)); + } + if (l->tk==LEX_R_UNDEFINED) { + l->match(LEX_R_UNDEFINED); + return new CScriptVarLink(new CScriptVar(TINYJS_BLANK_DATA,SCRIPTVAR_UNDEFINED)); + } + if (l->tk==LEX_ID || l->tk=='(') { + CScriptVarLink *a; int op = l->tk; if(l->tk=='(') { @@ -1438,272 +1566,290 @@ CScriptVarLink *CTinyJS::factor(bool &execute) { } else a = execute ? findInScopes(l->tkStr) : new CScriptVarLink(new CScriptVar()); - /* The parent if we're executing a method call */ - CScriptVar *parent = 0; - - if (execute && !a) { - /* Variable doesn't exist! JavaScript says we should create it - * (we won't add it here. This is done in the assignment operator)*/ - a = new CScriptVarLink(new CScriptVar(), l->tkStr); - } + /* The parent if we're executing a method call */ + CScriptVar *parent = 0; + + if (execute && !a) { + /* Variable doesn't exist! JavaScript says we should create it + * (we won't add it here. This is done in the assignment operator)*/ + a = new CScriptVarLink(new CScriptVar(), l->tkStr); + } if(op==LEX_ID) l->match(LEX_ID); - while (l->tk=='(' || l->tk=='.' || l->tk=='[') { - if (l->tk=='(') { // ------------------------------------- Function Call - if (execute) { - if (!a->var->isFunction()) { - string errorMsg = "Expecting '"; - errorMsg = errorMsg + a->name + "' to be a function"; - throw new CScriptException(errorMsg.c_str()); - } - l->match('('); - // create a new symbol table entry for execution of this function - CScriptVar *functionRoot = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_FUNCTION); - if (parent) - functionRoot->addChildNoDup("this", parent); + while (l->tk=='(' || l->tk=='.' || l->tk=='[') { + if (l->tk=='(') { // ------------------------------------- Function Call + if (execute) { + if (!a->var->isFunction()) { + string errorMsg = "Expecting '"; + errorMsg = errorMsg + a->name + "' to be a function"; + throw new CScriptException(errorMsg.c_str()); + } + l->match('('); + // create a new symbol table entry for execution of this function + CScriptVar *functionRoot = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_FUNCTION); + if (parent) + functionRoot->addChildNoDup("this", parent); else - functionRoot->addChildNoDup("this", new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT)); // always add a this-Object - // grab in all parameters - CScriptVarLink *v = a->var->firstChild; - while (v) { - CScriptVarLink *value = base(execute); - if (execute) { - if (value->var->isBasic()) { - // pass by value - functionRoot->addChild(v->name, value->var->deepCopy()); - } else { - // pass by reference - functionRoot->addChild(v->name, value->var); - } - } - CLEAN(value); - if (l->tk!=')') l->match(','); - v = v->nextSibling; - } - l->match(')'); - // setup a return variable - CScriptVarLink *returnVar = NULL; - // execute function! - // add the function's execute space to the symbol table so we can recurse - CScriptVarLink *returnVarLink = functionRoot->addChild(TINYJS_RETURN_VAR); - scopes.push_back(functionRoot); - - if (a->var->isNative()) { - ASSERT(a->var->jsCallback); + functionRoot->addChildNoDup("this", new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT)); // always add a this-Object + // grab in all parameters + for(SCRIPTVAR_CHILDS::iterator it = a->var->Childs.begin(); it != a->var->Childs.end(); ++it) { + CScriptVarLink *value = assignment(execute); + if (execute) { + if (value->var->isBasic()) { + // pass by value + functionRoot->addChild( it->first, value->var->deepCopy()); + } else { + // pass by reference + functionRoot->addChild(it->first, value->var); + } + } + CLEAN(value); + if (l->tk!=')') l->match(','); + } +//// +/* + CScriptVarLink *v = a->var->firstChild; + while (v) { + CScriptVarLink *value = base(execute); + if (execute) { + if (value->var->isBasic()) { + // pass by value + functionRoot->addChild(v->name, value->var->deepCopy()); + } else { + // pass by reference + functionRoot->addChild(v->name, value->var); + } + } + CLEAN(value); + if (l->tk!=')') l->match(','); + v = v->nextSibling; + } +*/ +//// + l->match(')'); + // setup a return variable + CScriptVarLink *returnVar = NULL; + // execute function! + // add the function's execute space to the symbol table so we can recurse + CScriptVarLink *returnVarLink = functionRoot->addChild(TINYJS_RETURN_VAR); + scopes.push_back(functionRoot); + + if (a->var->isNative()) { + ASSERT(a->var->jsCallback); if (a->var->isNative_ClassMemberFnc()) (*a->var->jsCallbackClass)(functionRoot, a->var->jsCallbackUserData); - else + else a->var->jsCallback(functionRoot, a->var->jsCallbackUserData); - } else { - /* we just want to execute the block, but something could - * have messed up and left us with the wrong ScriptLex, so - * we want to be careful here... */ - CScriptException *exception = 0; - CScriptLex *oldLex = l; - CScriptLex *newLex = new CScriptLex(a->var->getString()); - l = newLex; - try { - block(execute); - // because return will probably have called this, and set execute to false - execute = true; - } catch (CScriptException *e) { - exception = e; - } - delete newLex; - l = oldLex; - - if (exception) - throw exception; - } - - scopes.pop_back(); - /* get the real return var before we remove it from our function */ - returnVar = new CScriptVarLink(returnVarLink->var); - functionRoot->removeLink(returnVarLink); - delete functionRoot; - if (returnVar) - a = returnVar; - else - a = new CScriptVarLink(new CScriptVar()); - } else { - // function, but not executing - just parse args and be done - l->match('('); - while (l->tk != ')') { - CScriptVarLink *value = base(execute); - CLEAN(value); - if (l->tk!=')') l->match(','); - } - l->match(')'); - if (l->tk == '{') { - block(execute); - } - } - } else if (l->tk == '.') { // ------------------------------------- Record Access - l->match('.'); - if (execute) { - const string &name = l->tkStr; - CScriptVarLink *child = a->var->findChild(name); - if (!child) child = findInParentClasses(a->var, name); - if (!child) { - /* if we haven't found this defined yet, use the built-in - 'length' properly */ - if (a->var->isArray() && name == "length") { - int l = a->var->getArrayLength(); - child = new CScriptVarLink(new CScriptVar(l)); - } else if (a->var->isString() && name == "length") { - int l = a->var->getString().size(); - child = new CScriptVarLink(new CScriptVar(l)); - } else { - child = a->var->addChild(name); - } - } - parent = a->var; - a = child; - } - l->match(LEX_ID); - } else if (l->tk == '[') { // ------------------------------------- Array Access - l->match('['); - CScriptVarLink *index = expression(execute); - l->match(']'); - if (execute) { - CScriptVarLink *child = a->var->findChildOrCreate(index->var->getString()); - parent = a->var; - a = child; - } - CLEAN(index); - } else ASSERT(0); - } - return a; - } - if (l->tk==LEX_INT || l->tk==LEX_FLOAT) { - CScriptVar *a = new CScriptVar(l->tkStr, - ((l->tk==LEX_INT)?SCRIPTVAR_INTEGER:SCRIPTVAR_DOUBLE)); - l->match(l->tk); - return new CScriptVarLink(a); - } - if (l->tk==LEX_STR) { - CScriptVar *a = new CScriptVar(l->tkStr, SCRIPTVAR_STRING); - l->match(LEX_STR); - return new CScriptVarLink(a); - } - if (l->tk=='{') { - CScriptVar *contents = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT); - /* JSON-style object definition */ - l->match('{'); - while (l->tk != '}') { - string id = l->tkStr; - // we only allow strings or IDs on the left hand side of an initialisation - if (l->tk==LEX_STR) l->match(LEX_STR); - else l->match(LEX_ID); - l->match(':'); - if (execute) { - CScriptVarLink *a = base(execute); - contents->addChild(id, a->var); - CLEAN(a); - } - // no need to clean here, as it will definitely be used - if (l->tk != '}') l->match(','); - } - - l->match('}'); - return new CScriptVarLink(contents); - } - if (l->tk=='[') { - CScriptVar *contents = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_ARRAY); - /* JSON-style array */ - l->match('['); - int idx = 0; - while (l->tk != ']') { - if (execute) { - char idx_str[16]; // big enough for 2^32 - sprintf_s(idx_str, sizeof(idx_str), "%d",idx); - - CScriptVarLink *a = base(execute); - contents->addChild(idx_str, a->var); - CLEAN(a); - } - // no need to clean here, as it will definitely be used - if (l->tk != ']') l->match(','); - idx++; - } - l->match(']'); - return new CScriptVarLink(contents); - } - if (l->tk==LEX_R_FUNCTION) { - CScriptVarLink *funcVar = parseFunctionDefinition(); - if (funcVar->name != TINYJS_TEMP_NAME) - TRACE("Functions not defined at statement-level are not meant to have a name"); - return funcVar; - } - if (l->tk==LEX_R_NEW) { - // new -> create a new object - l->match(LEX_R_NEW); - const string &className = l->tkStr; - if (execute) { - CScriptVarLink *objClass = findInScopes(className); - if (!objClass) { - TRACE("%s is not a valid class name", className.c_str()); - return new CScriptVarLink(new CScriptVar()); - } - l->match(LEX_ID); - CScriptVar *obj = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT); - obj->addChild(TINYJS_PROTOTYPE_CLASS, objClass->var); - if (l->tk == '(') { - l->match('('); - l->match(')'); - } - // TODO: Object constructors - return new CScriptVarLink(obj); - } else { - l->match(LEX_ID); - if (l->tk == '(') { - l->match('('); - while(l->tk != ')') l->match(l->tk); - l->match(')'); - } - } - } - // Nothing we can do here... just hope it's the end... - l->match(LEX_EOF); - return 0; + } else { + /* we just want to execute the block, but something could + * have messed up and left us with the wrong ScriptLex, so + * we want to be careful here... */ + CScriptException *exception = 0; + CScriptLex *oldLex = l; + CScriptLex *newLex = new CScriptLex(a->var->getString()); + l = newLex; + try { + block(execute); + // because return will probably have called this, and set execute to false + execute = true; + } catch (CScriptException *e) { + exception = e; + } + delete newLex; + l = oldLex; + + if (exception) + throw exception; + } + + scopes.pop_back(); + /* get the real return var before we remove it from our function */ + returnVar = new CScriptVarLink(returnVarLink->var); + functionRoot->removeLink(returnVarLink); + delete functionRoot; + if (returnVar) + a = returnVar; + else + a = new CScriptVarLink(new CScriptVar()); + } else { + // function, but not executing - just parse args and be done + l->match('('); + while (l->tk != ')') { + CScriptVarLink *value = base(execute); + CLEAN(value); + if (l->tk!=')') l->match(','); + } + l->match(')'); + if (l->tk == '{') { + block(execute); + } + } + } else if (l->tk == '.') { // ------------------------------------- Record Access + l->match('.'); + if (execute) { + const string &name = l->tkStr; + CScriptVarLink *child = a->var->findChild(name); + if (!child) child = findInParentClasses(a->var, name); + if (!child) { + /* if we haven't found this defined yet, use the built-in + 'length' properly */ + if (a->var->isArray() && name == "length") { + int l = a->var->getArrayLength(); + child = new CScriptVarLink(new CScriptVar(l)); + } else if (a->var->isString() && name == "length") { + int l = a->var->getString().size(); + child = new CScriptVarLink(new CScriptVar(l)); + } else { + child = a->var->addChild(name); + } + } + parent = a->var; + a = child; + } + l->match(LEX_ID); + } else if (l->tk == '[') { // ------------------------------------- Array Access + l->match('['); + CScriptVarLink *index = expression(execute); + l->match(']'); + if (execute) { + CScriptVarLink *child = a->var->findChildOrCreate(index->var->getString()); + parent = a->var; + a = child; + } + CLEAN(index); + } else ASSERT(0); + } + return a; + } + if (l->tk==LEX_INT || l->tk==LEX_FLOAT) { + CScriptVar *a = new CScriptVar(l->tkStr, + ((l->tk==LEX_INT)?SCRIPTVAR_INTEGER:SCRIPTVAR_DOUBLE)); + l->match(l->tk); + return new CScriptVarLink(a); + } + if (l->tk==LEX_STR) { + CScriptVar *a = new CScriptVar(l->tkStr, SCRIPTVAR_STRING); + l->match(LEX_STR); + return new CScriptVarLink(a); + } + if (l->tk=='{') { + CScriptVar *contents = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT); + /* JSON-style object definition */ + l->match('{'); + while (l->tk != '}') { + string id = l->tkStr; + // we only allow strings or IDs on the left hand side of an initialisation + if (l->tk==LEX_STR) l->match(LEX_STR); + else l->match(LEX_ID); + l->match(':'); + if (execute) { + CScriptVarLink *a = base(execute); + contents->addChild(id, a->var); + CLEAN(a); + } + // no need to clean here, as it will definitely be used + if (l->tk != '}') l->match(','); + } + + l->match('}'); + return new CScriptVarLink(contents); + } + if (l->tk=='[') { + CScriptVar *contents = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_ARRAY); + /* JSON-style array */ + l->match('['); + int idx = 0; + while (l->tk != ']') { + if (execute) { + char idx_str[16]; // big enough for 2^32 + sprintf_s(idx_str, sizeof(idx_str), "%d",idx); + + CScriptVarLink *a = base(execute); + contents->addChild(idx_str, a->var); + CLEAN(a); + } + // no need to clean here, as it will definitely be used + if (l->tk != ']') l->match(','); + idx++; + } + l->match(']'); + return new CScriptVarLink(contents); + } + if (l->tk==LEX_R_FUNCTION) { + CScriptVarLink *funcVar = parseFunctionDefinition(); + if (funcVar->name != TINYJS_TEMP_NAME) + TRACE("Functions not defined at statement-level are not meant to have a name"); + return funcVar; + } + if (l->tk==LEX_R_NEW) { + // new -> create a new object + l->match(LEX_R_NEW); + const string &className = l->tkStr; + if (execute) { + CScriptVarLink *objClass = findInScopes(className); + if (!objClass) { + TRACE("%s is not a valid class name", className.c_str()); + return new CScriptVarLink(new CScriptVar()); + } + l->match(LEX_ID); + CScriptVar *obj = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT); + obj->addChild(TINYJS_PROTOTYPE_CLASS, objClass->var); + if (l->tk == '(') { + l->match('('); + l->match(')'); + } + // TODO: Object constructors + return new CScriptVarLink(obj); + } else { + l->match(LEX_ID); + if (l->tk == '(') { + l->match('('); + while(l->tk != ')') l->match(l->tk); + l->match(')'); + } + } + } + // Nothing we can do here... just hope it's the end... + l->match(LEX_EOF); + return 0; } CScriptVarLink *CTinyJS::unary(bool &execute) { - CScriptVarLink *a; - if (l->tk=='-') { - l->match('-'); - a = unary(execute); - if (execute) { - CScriptVar zero(0); - CScriptVar *res = zero.mathsOp(a->var, '-'); - CREATE_LINK(a, res); - } - } else - if (l->tk=='+') { - l->match('+'); - a = unary(execute); - } else - if (l->tk=='!') { - l->match('!'); // binary not - a = unary(execute); - if (execute) { - CScriptVar zero(0); - CScriptVar *res = a->var->mathsOp(&zero, LEX_EQUAL); - CREATE_LINK(a, res); - } - } else - if (l->tk=='~') { - l->match('~'); // binary neg - a = unary(execute); - if (execute) { - CScriptVar *res = a->var->mathsOp(NULL, '~'); - CREATE_LINK(a, res); - } - } else + CScriptVarLink *a; + if (l->tk=='-') { + l->match('-'); + a = unary(execute); + if (execute) { + CScriptVar zero(0); + CScriptVar *res = zero.mathsOp(a->var, '-'); + CREATE_LINK(a, res); + } + } else + if (l->tk=='+') { + l->match('+'); + a = unary(execute); + } else + if (l->tk=='!') { + l->match('!'); // binary not + a = unary(execute); + if (execute) { + CScriptVar zero(0); + CScriptVar *res = a->var->mathsOp(&zero, LEX_EQUAL); + CREATE_LINK(a, res); + } + } else + if (l->tk=='~') { + l->match('~'); // binary neg + a = unary(execute); + if (execute) { + CScriptVar *res = a->var->mathsOp(NULL, '~'); + CREATE_LINK(a, res); + } + } else // pre increment/decrement if (l->tk==LEX_PLUSPLUS || l->tk==LEX_MINUSMINUS) { int op = l->tk; l->match(l->tk); - a = factor(execute); + a = factor(execute); if (execute) { if(a->owned) { @@ -1716,13 +1862,13 @@ CScriptVarLink *CTinyJS::unary(bool &execute) { } } else - a = factor(execute); + a = factor(execute); // post increment/decrement if (l->tk==LEX_PLUSPLUS || l->tk==LEX_MINUSMINUS) { int op = l->tk; - l->match(l->tk); - if (execute) { + l->match(l->tk); + if (execute) { if(a->owned) { // TRACE("post-increment of %s and a is %sthe owner\n", a->name.c_str(), a->owned?"":"not "); @@ -1744,132 +1890,129 @@ CScriptVarLink *CTinyJS::unary(bool &execute) { // errorString << " at " << (l->getPosition(l->tokenStart-2)); throw new CScriptException(errorString.str()); } - } - } + } + } return a; } CScriptVarLink *CTinyJS::term(bool &execute) { - CScriptVarLink *a = unary(execute); - while (l->tk=='*' || l->tk=='/' || l->tk=='%') { - int op = l->tk; - l->match(l->tk); - CScriptVarLink *b = unary(execute); - if (execute) { - CScriptVar *res = a->var->mathsOp(b->var, op); - CREATE_LINK(a, res); - } - CLEAN(b); - } - return a; + CScriptVarLink *a = unary(execute); + while (l->tk=='*' || l->tk=='/' || l->tk=='%') { + int op = l->tk; + l->match(l->tk); + CScriptVarLink *b = unary(execute); + if (execute) { + CScriptVar *res = a->var->mathsOp(b->var, op); + CREATE_LINK(a, res); + } + CLEAN(b); + } + return a; } CScriptVarLink *CTinyJS::expression(bool &execute) { CScriptVarLink *a = term(execute); - while (l->tk=='+' || l->tk=='-') { - int op = l->tk; - l->match(l->tk); - CScriptVarLink *b = term(execute); - if (execute) { - // not in-place, so just replace - CScriptVar *res = a->var->mathsOp(b->var, op); - CREATE_LINK(a, res); - } - CLEAN(b); - } - return a; + while (l->tk=='+' || l->tk=='-') { + int op = l->tk; + l->match(l->tk); + CScriptVarLink *b = term(execute); + if (execute) { + // not in-place, so just replace + CScriptVar *res = a->var->mathsOp(b->var, op); + CREATE_LINK(a, res); + } + CLEAN(b); + } + return a; } CScriptVarLink *CTinyJS::binary_shift(bool &execute) { CScriptVarLink *a = expression(execute); - while (l->tk==LEX_LSHIFT || l->tk==LEX_RSHIFT) { - int op = l->tk; - l->match(l->tk); + while (l->tk==LEX_LSHIFT || l->tk==LEX_RSHIFT) { + int op = l->tk; + l->match(l->tk); CScriptVarLink *b = expression(execute); - if (execute) { - // not in-place, so just replace - CScriptVar *res = a->var->mathsOp(b->var, op); - CREATE_LINK(a, res); - } - CLEAN(b); - } - return a; + if (execute) { + // not in-place, so just replace + CScriptVar *res = a->var->mathsOp(b->var, op); + CREATE_LINK(a, res); + } + CLEAN(b); + } + return a; } CScriptVarLink *CTinyJS::relation(bool &execute, int set, int set_n) { CScriptVarLink *a = set_n ? relation(execute, set_n, 0) : binary_shift(execute); // CScriptVarLink *a = expression(execute); - CScriptVarLink *b; - while ((set==LEX_EQUAL && (l->tk==LEX_EQUAL || l->tk==LEX_NEQUAL || - l->tk==LEX_TYPEEQUAL || l->tk==LEX_NTYPEEQUAL)) - || - (set=='<' && (l->tk==LEX_LEQUAL || l->tk==LEX_GEQUAL || - l->tk=='<' || l->tk=='>'))) { - int op = l->tk; - l->match(l->tk); - b = set_n ? relation(execute, set_n, 0) : binary_shift(execute); - if (execute) { - CScriptVar *res = a->var->mathsOp(b->var, op); - CREATE_LINK(a,res); - } - CLEAN(b); - } - return a; + CScriptVarLink *b; + while ((set==LEX_EQUAL && (l->tk==LEX_EQUAL || l->tk==LEX_NEQUAL || l->tk==LEX_TYPEEQUAL || l->tk==LEX_NTYPEEQUAL)) + || (set=='<' && (l->tk==LEX_LEQUAL || l->tk==LEX_GEQUAL || l->tk=='<' || l->tk=='>'))) { + int op = l->tk; + l->match(l->tk); + b = set_n ? relation(execute, set_n, 0) : binary_shift(execute); + if (execute) { + CScriptVar *res = a->var->mathsOp(b->var, op); + CREATE_LINK(a,res); + } + CLEAN(b); + } + return a; } CScriptVarLink *CTinyJS::logic_binary(bool &execute, int op, int op_n1, int op_n2) { CScriptVarLink *a = op_n1 ? logic_binary(execute, op_n1, op_n2, 0) : relation(execute); - CScriptVarLink *b; - while (l->tk==op) { - l->match(l->tk); - b = op_n1 ? logic_binary(execute, op_n1, op_n2, 0) : relation(execute); - if (execute) { - CScriptVar *res = a->var->mathsOp(b->var, op); - CREATE_LINK(a, res); - } - CLEAN(b); - } - return a; + CScriptVarLink *b; + while (l->tk==op) { + l->match(l->tk); + b = op_n1 ? logic_binary(execute, op_n1, op_n2, 0) : relation(execute); + if (execute) { + CScriptVar *res = a->var->mathsOp(b->var, op); + CREATE_LINK(a, res); + } + CLEAN(b); + } + return a; } CScriptVarLink *CTinyJS::logic(bool &execute, int op, int op_n) { CScriptVarLink *a = op_n ? logic(execute, op_n, 0) : logic_binary(execute); - CScriptVarLink *b; - while (l->tk==op) { - bool noexecute = false; - int bop = l->tk; - l->match(l->tk); - bool shortCircuit = false; - bool boolean = false; - // if we have short-circuit ops, then if we know the outcome - // we don't bother to execute the other op. Even if not - // we need to tell mathsOp it's an & or | - if (op==LEX_ANDAND) { - bop = '&'; - shortCircuit = !a->var->getBool(); - boolean = true; - } else if (op==LEX_OROR) { - bop = '|'; - shortCircuit = a->var->getBool(); - boolean = true; - } + CScriptVarLink *b; + while (l->tk==op) { + bool noexecute = false; + int bop = l->tk; + l->match(l->tk); + bool shortCircuit = false; + bool boolean = false; + // if we have short-circuit ops, then if we know the outcome + // we don't bother to execute the other op. Even if not + // we need to tell mathsOp it's an & or | + if (op==LEX_ANDAND) { + bop = '&'; + shortCircuit = !a->var->getBool(); + boolean = true; + } else if (op==LEX_OROR) { + bop = '|'; + shortCircuit = a->var->getBool(); + boolean = true; + } b = op_n ? logic(shortCircuit ? noexecute : execute, op_n, 0) : logic_binary(shortCircuit ? noexecute : execute); - if (execute && !shortCircuit) { - if (boolean) { - CScriptVar *newa = new CScriptVar(a->var->getBool()); - CScriptVar *newb = new CScriptVar(b->var->getBool()); - CREATE_LINK(a, newa); - CREATE_LINK(b, newb); - } - CScriptVar *res = a->var->mathsOp(b->var, bop); - CREATE_LINK(a, res); - } - CLEAN(b); - } - return a; + if (execute && !shortCircuit) { + if (boolean) { + CScriptVar *newa = new CScriptVar(a->var->getBool()); + CScriptVar *newb = new CScriptVar(b->var->getBool()); + CREATE_LINK(a, newa); + CREATE_LINK(b, newb); + } + CScriptVar *res = a->var->mathsOp(b->var, bop); + CREATE_LINK(a, res); + } + CLEAN(b); + } + return a; } CScriptVarLink *CTinyJS::condition(bool &execute) { @@ -1896,56 +2039,56 @@ CScriptVarLink *CTinyJS::condition(bool &execute) } CScriptVarLink *CTinyJS::assignment(bool &execute) { - CScriptVarLink *lhs = condition(execute); - if (l->tk=='=' || l->tk==LEX_PLUSEQUAL || l->tk==LEX_MINUSEQUAL || + CScriptVarLink *lhs = condition(execute); + if (l->tk=='=' || l->tk==LEX_PLUSEQUAL || l->tk==LEX_MINUSEQUAL || l->tk==LEX_LSHIFTEQUAL || l->tk==LEX_RSHIFTEQUAL || l->tk==LEX_ANDEQUAL || l->tk==LEX_OREQUAL || l->tk==LEX_XOREQUAL) { - /* If we're assigning to this and we don't have a parent, - * add it to the symbol table root as per JavaScript. */ - if (execute && !lhs->owned) { - if (lhs->name.length()>0) { - CScriptVarLink *realLhs = root->addChildNoDup(lhs->name, lhs->var); - CLEAN(lhs); - lhs = realLhs; - } else - TRACE("Trying to assign to an un-named type\n"); - } - - int op = l->tk; - l->match(l->tk); - CScriptVarLink *rhs = assignment(execute); - if (execute) { - if (op=='=') { - lhs->replaceWith(rhs); - } else if (op==LEX_PLUSEQUAL) { - CScriptVar *res = lhs->var->mathsOp(rhs->var, '+'); - lhs->replaceWith(res); - } else if (op==LEX_MINUSEQUAL) { - CScriptVar *res = lhs->var->mathsOp(rhs->var, '-'); - lhs->replaceWith(res); - } else if (op==LEX_LSHIFTEQUAL) { - CScriptVar *res = lhs->var->mathsOp(rhs->var, LEX_LSHIFT); - lhs->replaceWith(res); - } else if (op==LEX_RSHIFTEQUAL) { - CScriptVar *res = lhs->var->mathsOp(rhs->var, LEX_RSHIFT); - lhs->replaceWith(res); - } else if (op==LEX_ANDEQUAL) { - CScriptVar *res = lhs->var->mathsOp(rhs->var, '&'); - lhs->replaceWith(res); - } else if (op==LEX_OREQUAL) { - CScriptVar *res = lhs->var->mathsOp(rhs->var, '|'); - lhs->replaceWith(res); - } else if (op==LEX_XOREQUAL) { - CScriptVar *res = lhs->var->mathsOp(rhs->var, '^'); - lhs->replaceWith(res); - } - } - CLEAN(rhs); - } - return lhs; + /* If we're assigning to this and we don't have a parent, + * add it to the symbol table root as per JavaScript. */ + if (execute && !lhs->owned) { + if (lhs->name.length()>0) { + CScriptVarLink *realLhs = root->addChildNoDup(lhs->name, lhs->var); + CLEAN(lhs); + lhs = realLhs; + } else + TRACE("Trying to assign to an un-named type\n"); + } + + int op = l->tk; + l->match(l->tk); + CScriptVarLink *rhs = assignment(execute); + if (execute) { + if (op=='=') { + lhs->replaceWith(rhs); + } else if (op==LEX_PLUSEQUAL) { + CScriptVar *res = lhs->var->mathsOp(rhs->var, '+'); + lhs->replaceWith(res); + } else if (op==LEX_MINUSEQUAL) { + CScriptVar *res = lhs->var->mathsOp(rhs->var, '-'); + lhs->replaceWith(res); + } else if (op==LEX_LSHIFTEQUAL) { + CScriptVar *res = lhs->var->mathsOp(rhs->var, LEX_LSHIFT); + lhs->replaceWith(res); + } else if (op==LEX_RSHIFTEQUAL) { + CScriptVar *res = lhs->var->mathsOp(rhs->var, LEX_RSHIFT); + lhs->replaceWith(res); + } else if (op==LEX_ANDEQUAL) { + CScriptVar *res = lhs->var->mathsOp(rhs->var, '&'); + lhs->replaceWith(res); + } else if (op==LEX_OREQUAL) { + CScriptVar *res = lhs->var->mathsOp(rhs->var, '|'); + lhs->replaceWith(res); + } else if (op==LEX_XOREQUAL) { + CScriptVar *res = lhs->var->mathsOp(rhs->var, '^'); + lhs->replaceWith(res); + } + } + CLEAN(rhs); + } + return lhs; } CScriptVarLink *CTinyJS::base(bool &execute) { - CScriptVarLink *a; + CScriptVarLink *a; for(;;) { a = assignment(execute); @@ -1958,52 +2101,52 @@ CScriptVarLink *CTinyJS::base(bool &execute) { return a; } void CTinyJS::block(bool &execute) { - // TODO: fast skip of blocks - l->match('{'); - if (execute) { - while (l->tk && l->tk!='}') - statement(execute); - l->match('}'); - } else { - int brackets = 1; - while (l->tk && brackets) { - if (l->tk == '{') brackets++; - if (l->tk == '}') brackets--; - l->match(l->tk); - } - } + // TODO: fast skip of blocks + l->match('{'); + if (execute) { + while (l->tk && l->tk!='}') + statement(execute); + l->match('}'); + } else { + int brackets = 1; + while (l->tk && brackets) { + if (l->tk == '{') brackets++; + if (l->tk == '}') brackets--; + l->match(l->tk); + } + } } void CTinyJS::statement(bool &execute) { - if (l->tk==LEX_ID || - l->tk==LEX_INT || - l->tk==LEX_FLOAT || - l->tk==LEX_STR || - l->tk==LEX_PLUSPLUS || - l->tk==LEX_MINUSMINUS || - l->tk=='+' || - l->tk=='-' || - l->tk=='(') { - /* Execute a simple statement that only contains basic arithmetic... */ - CLEAN(base(execute)); - l->match(';'); + if (l->tk==LEX_ID || + l->tk==LEX_INT || + l->tk==LEX_FLOAT || + l->tk==LEX_STR || + l->tk==LEX_PLUSPLUS || + l->tk==LEX_MINUSMINUS || + l->tk=='+' || + l->tk=='-' || + l->tk=='(') { + /* Execute a simple statement that only contains basic arithmetic... */ + CLEAN(base(execute)); + l->match(';'); } else if (l->tk=='{') { - /* A block of code */ - block(execute); - } else if (l->tk==';') { - /* Empty statement - to allow things like ;;; */ - l->match(';'); - } else if (l->tk==LEX_R_VAR) { - /* variable creation. TODO - we need a better way of parsing the left - * hand side. Maybe just have a flag called can_create_var that we - * set and then we parse as if we're doing a normal equals.*/ - l->match(LEX_R_VAR); - for(;;) + /* A block of code */ + block(execute); + } else if (l->tk==';') { + /* Empty statement - to allow things like ;;; */ + l->match(';'); + } else if (l->tk==LEX_R_VAR) { + /* variable creation. TODO - we need a better way of parsing the left + * hand side. Maybe just have a flag called can_create_var that we + * set and then we parse as if we're doing a normal equals.*/ + l->match(LEX_R_VAR); + for(;;) { CScriptVarLink *a = 0; if (execute) - a = scopes.back()->findChildOrCreate(l->tkStr); + a = scopes.back()->findChildOrCreate(l->tkStr); l->match(LEX_ID); // now do stuff defined with dots while (l->tk == '.') { @@ -2022,29 +2165,29 @@ void CTinyJS::statement(bool &execute) { a->replaceWith(var); CLEAN(var); } - if (l->tk == ',') { + if (l->tk == ',') l->match(','); - } else + else break; } - l->match(';'); - } else if (l->tk==LEX_R_IF) { - l->match(LEX_R_IF); - l->match('('); - CScriptVarLink *var = base(execute); - l->match(')'); - bool cond = execute && var->var->getBool(); - CLEAN(var); - bool noexecute = false; // because we need to be abl;e to write to it - statement(cond ? execute : noexecute); - if (l->tk==LEX_R_ELSE) { - l->match(LEX_R_ELSE); - statement(cond ? noexecute : execute); - } - } else if (l->tk==LEX_R_DO) { - // We do repetition by pulling out the string representing our statement - // there's definitely some opportunity for optimisation here - l->match(LEX_R_DO); + l->match(';'); + } else if (l->tk==LEX_R_IF) { + l->match(LEX_R_IF); + l->match('('); + CScriptVarLink *var = base(execute); + l->match(')'); + bool cond = execute && var->var->getBool(); + CLEAN(var); + bool noexecute = false; // because we need to be abl;e to write to it + statement(cond ? execute : noexecute); + if (l->tk==LEX_R_ELSE) { + l->match(LEX_R_ELSE); + statement(cond ? noexecute : execute); + } + } else if (l->tk==LEX_R_DO) { + // We do repetition by pulling out the string representing our statement + // there's definitely some opportunity for optimisation here + l->match(LEX_R_DO); bool loopCond = true; bool old_execute = execute; int old_runtimeFlags = runtimeFlags; @@ -2055,7 +2198,7 @@ void CTinyJS::statement(bool &execute) { CScriptLex *whileBody = NULL; CScriptLex *whileCond = NULL; int loopCount = TINYJS_LOOP_MAX_ITERATIONS; - while (loopCond && loopCount-->0) + while (loopCond && loopCount-->0) { if(whileBody) { @@ -2094,41 +2237,41 @@ void CTinyJS::statement(bool &execute) { if(!whileCond) { whileCond = l->getSubLex(whileCondStart); - l->match(')'); - l->match(';'); + l->match(')'); + l->match(';'); oldLex = l; } } runtimeFlags = old_runtimeFlags; - l = oldLex; - delete whileCond; - delete whileBody; - - if (loopCount<=0) { - root->trace(); - TRACE("WHILE Loop exceeded %d iterations at %s\n", TINYJS_LOOP_MAX_ITERATIONS, l->getPosition(l->tokenLastEnd).c_str()); - throw new CScriptException("LOOP_ERROR"); - } - } else if (l->tk==LEX_R_WHILE) { - // We do repetition by pulling out the string representing our statement - // there's definitely some opportunity for optimisation here - l->match(LEX_R_WHILE); - l->match('('); - int whileCondStart = l->tokenStart; - bool noexecute = false; - CScriptVarLink *cond = base(execute); - bool loopCond = execute && cond->var->getBool(); - CLEAN(cond); - CScriptLex *whileCond = l->getSubLex(whileCondStart); - l->match(')'); - int whileBodyStart = l->tokenStart; + l = oldLex; + delete whileCond; + delete whileBody; + + if (loopCount<=0) { + root->trace(); + TRACE("WHILE Loop exceeded %d iterations at %s\n", TINYJS_LOOP_MAX_ITERATIONS, l->getPosition(l->tokenLastEnd).c_str()); + throw new CScriptException("LOOP_ERROR"); + } + } else if (l->tk==LEX_R_WHILE) { + // We do repetition by pulling out the string representing our statement + // there's definitely some opportunity for optimisation here + l->match(LEX_R_WHILE); + l->match('('); + int whileCondStart = l->tokenStart; + bool noexecute = false; + CScriptVarLink *cond = base(execute); + bool loopCond = execute && cond->var->getBool(); + CLEAN(cond); + CScriptLex *whileCond = l->getSubLex(whileCondStart); + l->match(')'); + int whileBodyStart = l->tokenStart; bool old_execute = execute; int old_runtimeFlags = runtimeFlags; runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | RUNTIME_CANBREAK | RUNTIME_CANCONTINUE; - statement(loopCond ? execute : noexecute); - CScriptLex *whileBody = l->getSubLex(whileBodyStart); - CScriptLex *oldLex = l; + statement(loopCond ? execute : noexecute); + CScriptLex *whileBody = l->getSubLex(whileBodyStart); + CScriptLex *oldLex = l; if(loopCond && old_execute != execute) { // break; @@ -2143,16 +2286,16 @@ void CTinyJS::statement(bool &execute) { } int loopCount = TINYJS_LOOP_MAX_ITERATIONS; - while (loopCond && loopCount-->0) { - whileCond->reset(); - l = whileCond; - cond = base(execute); - loopCond = execute && cond->var->getBool(); - CLEAN(cond); - if (loopCond) { - whileBody->reset(); - l = whileBody; - statement(execute); + while (loopCond && loopCount-->0) { + whileCond->reset(); + l = whileCond; + cond = base(execute); + loopCond = execute && cond->var->getBool(); + CLEAN(cond); + if (loopCond) { + whileBody->reset(); + l = whileBody; + statement(execute); if(!execute) { // break; @@ -2165,22 +2308,22 @@ void CTinyJS::statement(bool &execute) { } // return, break or continue; } - } - } + } + } runtimeFlags = old_runtimeFlags; - l = oldLex; - delete whileCond; - delete whileBody; - - if (loopCount<=0) { - root->trace(); - TRACE("WHILE Loop exceeded %d iterations at %s\n", TINYJS_LOOP_MAX_ITERATIONS, l->getPosition(l->tokenLastEnd).c_str()); - throw new CScriptException("LOOP_ERROR"); - } + l = oldLex; + delete whileCond; + delete whileBody; + + if (loopCount<=0) { + root->trace(); + TRACE("WHILE Loop exceeded %d iterations at %s\n", TINYJS_LOOP_MAX_ITERATIONS, l->getPosition(l->tokenLastEnd).c_str()); + throw new CScriptException("LOOP_ERROR"); + } } else if (l->tk==LEX_R_FOR) { l->match(LEX_R_FOR); - l->match('('); - statement(execute); // initialisation + l->match('('); + statement(execute); // initialisation //l->match(';'); bool noexecute = false; int forCondStart = l->tokenStart; @@ -2263,117 +2406,117 @@ void CTinyJS::statement(bool &execute) { l = forIter; base(execute); } - } + } runtimeFlags = old_runtimeFlags; - l = oldLex; - delete forCond; - delete forIter; - delete forBody; - if (loopCount<=0) { - root->trace(); - TRACE("FOR Loop exceeded %d iterations at %s\n", TINYJS_LOOP_MAX_ITERATIONS, l->getPosition(l->tokenLastEnd).c_str()); - throw new CScriptException("LOOP_ERROR"); - } + l = oldLex; + delete forCond; + delete forIter; + delete forBody; + if (loopCount<=0) { + root->trace(); + TRACE("FOR Loop exceeded %d iterations at %s\n", TINYJS_LOOP_MAX_ITERATIONS, l->getPosition(l->tokenLastEnd).c_str()); + throw new CScriptException("LOOP_ERROR"); + } } else if (l->tk==LEX_R_BREAK) { - l->match(LEX_R_BREAK); + l->match(LEX_R_BREAK); if(runtimeFlags & RUNTIME_CANBREAK) { runtimeFlags |= RUNTIME_BREAK; execute = false; } - l->match(';'); + l->match(';'); } else if (l->tk==LEX_R_CONTINUE) { - l->match(LEX_R_CONTINUE); + l->match(LEX_R_CONTINUE); if(runtimeFlags & RUNTIME_CANCONTINUE) { runtimeFlags |= RUNTIME_CONTINUE; execute = false; } - l->match(';'); + l->match(';'); } else if (l->tk==LEX_R_RETURN) { - l->match(LEX_R_RETURN); - CScriptVarLink *result = 0; - if (l->tk != ';') - result = base(execute); - if (execute) { - CScriptVarLink *resultVar = scopes.back()->findChild(TINYJS_RETURN_VAR); - if (resultVar) - resultVar->replaceWith(result); - else - TRACE("RETURN statement, but not in a function.\n"); - execute = false; - } - CLEAN(result); - l->match(';'); - } else if (l->tk==LEX_R_FUNCTION) { - CScriptVarLink *funcVar = parseFunctionDefinition(); - if (execute) { - if (funcVar->name == TINYJS_TEMP_NAME) - TRACE("Functions defined at statement-level are meant to have a name\n"); - else - scopes.back()->addChildNoDup(funcVar->name, funcVar->var); - } - CLEAN(funcVar); - } else l->match(LEX_EOF); + l->match(LEX_R_RETURN); + CScriptVarLink *result = 0; + if (l->tk != ';') + result = base(execute); + if (execute) { + CScriptVarLink *resultVar = scopes.back()->findChild(TINYJS_RETURN_VAR); + if (resultVar) + resultVar->replaceWith(result); + else + TRACE("RETURN statement, but not in a function.\n"); + execute = false; + } + CLEAN(result); + l->match(';'); + } else if (l->tk==LEX_R_FUNCTION) { + CScriptVarLink *funcVar = parseFunctionDefinition(); + if (execute) { + if (funcVar->name == TINYJS_TEMP_NAME) + TRACE("Functions defined at statement-level are meant to have a name\n"); + else + scopes.back()->addChildNoDup(funcVar->name, funcVar->var); + } + CLEAN(funcVar); + } else l->match(LEX_EOF); } /// Get the value of the given variable, or return 0 const string *CTinyJS::getVariable(const string &path) { - // traverse path - size_t prevIdx = 0; - size_t thisIdx = path.find('.'); - if (thisIdx == string::npos) thisIdx = path.length(); - CScriptVar *var = root; - while (var && prevIdxfindChild(el); - var = varl?varl->var:0; - prevIdx = thisIdx+1; - thisIdx = path.find('.', prevIdx); - if (thisIdx == string::npos) thisIdx = path.length(); - } - // return result - if (var) - return &var->getString(); - else - return 0; + // traverse path + size_t prevIdx = 0; + size_t thisIdx = path.find('.'); + if (thisIdx == string::npos) thisIdx = path.length(); + CScriptVar *var = root; + while (var && prevIdxfindChild(el); + var = varl?varl->var:0; + prevIdx = thisIdx+1; + thisIdx = path.find('.', prevIdx); + if (thisIdx == string::npos) thisIdx = path.length(); + } + // return result + if (var) + return &var->getString(); + else + return 0; } /// Finds a child, looking recursively up the scopes CScriptVarLink *CTinyJS::findInScopes(const std::string &childName) { - for (int s=scopes.size()-1;s>=0;s--) { - CScriptVarLink *v = scopes[s]->findChild(childName); - if (v) return v; - } - return NULL; + for (int s=scopes.size()-1;s>=0;s--) { + CScriptVarLink *v = scopes[s]->findChild(childName); + if (v) return v; + } + return NULL; } /// Look up in any parent classes of the given object CScriptVarLink *CTinyJS::findInParentClasses(CScriptVar *object, const std::string &name) { - // Look for links to actual parent classes - CScriptVarLink *parentClass = object->findChild(TINYJS_PROTOTYPE_CLASS); - while (parentClass) { - CScriptVarLink *implementation = parentClass->var->findChild(name); - if (implementation) return implementation; - parentClass = parentClass->var->findChild(TINYJS_PROTOTYPE_CLASS); - } - // else fake it for strings and finally objects - if (object->isString()) { - CScriptVarLink *implementation = stringClass->findChild(name); - if (implementation) return implementation; - } - if (object->isArray()) { - CScriptVarLink *implementation = arrayClass->findChild(name); - if (implementation) return implementation; - } - CScriptVarLink *implementation = objectClass->findChild(name); - if (implementation) return implementation; - - return 0; + // Look for links to actual parent classes + CScriptVarLink *parentClass = object->findChild(TINYJS_PROTOTYPE_CLASS); + while (parentClass) { + CScriptVarLink *implementation = parentClass->var->findChild(name); + if (implementation) return implementation; + parentClass = parentClass->var->findChild(TINYJS_PROTOTYPE_CLASS); + } + // else fake it for strings and finally objects + if (object->isString()) { + CScriptVarLink *implementation = stringClass->findChild(name); + if (implementation) return implementation; + } + if (object->isArray()) { + CScriptVarLink *implementation = arrayClass->findChild(name); + if (implementation) return implementation; + } + CScriptVarLink *implementation = objectClass->findChild(name); + if (implementation) return implementation; + + return 0; } void CTinyJS::scEval(CScriptVar *c, void *data) { - std::string str = c->getParameter("jsCode")->getString(); - c->setReturnVar(evaluateComplex(str).var); + std::string str = c->getParameter("jsCode")->getString(); + c->setReturnVar(evaluateComplex(str).var); } diff --git a/TinyJS.h b/TinyJS.h index be69d0b..d5098d0 100755 --- a/TinyJS.h +++ b/TinyJS.h @@ -28,6 +28,7 @@ #include #include +#include #undef TRACE #ifndef TRACE @@ -37,77 +38,77 @@ const int TINYJS_LOOP_MAX_ITERATIONS = 8192; enum LEX_TYPES { - LEX_EOF = 0, - LEX_ID = 256, - LEX_INT, - LEX_FLOAT, - LEX_STR, - - LEX_EQUAL, - LEX_TYPEEQUAL, - LEX_NEQUAL, - LEX_NTYPEEQUAL, - LEX_LEQUAL, - LEX_LSHIFT, - LEX_LSHIFTEQUAL, - LEX_GEQUAL, - LEX_RSHIFT, - LEX_RSHIFTEQUAL, - LEX_PLUSEQUAL, - LEX_MINUSEQUAL, - LEX_PLUSPLUS, - LEX_MINUSMINUS, - LEX_ANDEQUAL, - LEX_ANDAND, - LEX_OREQUAL, - LEX_OROR, - LEX_XOREQUAL, - // reserved words + LEX_EOF = 0, + LEX_ID = 256, + LEX_INT, + LEX_FLOAT, + LEX_STR, + + LEX_EQUAL, + LEX_TYPEEQUAL, + LEX_NEQUAL, + LEX_NTYPEEQUAL, + LEX_LEQUAL, + LEX_LSHIFT, + LEX_LSHIFTEQUAL, + LEX_GEQUAL, + LEX_RSHIFT, + LEX_RSHIFTEQUAL, + LEX_PLUSEQUAL, + LEX_MINUSEQUAL, + LEX_PLUSPLUS, + LEX_MINUSMINUS, + LEX_ANDEQUAL, + LEX_ANDAND, + LEX_OREQUAL, + LEX_OROR, + LEX_XOREQUAL, + // reserved words #define LEX_R_LIST_START LEX_R_IF - LEX_R_IF, - LEX_R_ELSE, - LEX_R_DO, - LEX_R_WHILE, - LEX_R_FOR, - LEX_R_BREAK, - LEX_R_CONTINUE, - LEX_R_FUNCTION, - LEX_R_RETURN, - LEX_R_VAR, - LEX_R_TRUE, - LEX_R_FALSE, - LEX_R_NULL, - LEX_R_UNDEFINED, - LEX_R_NEW, + LEX_R_IF, + LEX_R_ELSE, + LEX_R_DO, + LEX_R_WHILE, + LEX_R_FOR, + LEX_R_BREAK, + LEX_R_CONTINUE, + LEX_R_FUNCTION, + LEX_R_RETURN, + LEX_R_VAR, + LEX_R_TRUE, + LEX_R_FALSE, + LEX_R_NULL, + LEX_R_UNDEFINED, + LEX_R_NEW, LEX_R_LIST_END /* always the last entry */ }; enum SCRIPTVAR_FLAGS { - SCRIPTVAR_UNDEFINED = 0, - SCRIPTVAR_FUNCTION = 1, - SCRIPTVAR_OBJECT = 2, - SCRIPTVAR_ARRAY = 4, - SCRIPTVAR_DOUBLE = 8, // floating point double - SCRIPTVAR_INTEGER = 16, // integer number - SCRIPTVAR_BOOLEAN = 32, // boolean - SCRIPTVAR_STRING = 64, // string - SCRIPTVAR_NULL = 128, // it seems null is its own data type - - SCRIPTVAR_NATIVE_FNC = 256, // to specify this is a native function - SCRIPTVAR_NATIVE_MFNC = 512, // to specify this is a native function from class->memberFunc - SCRIPTVAR_NUMERICMASK = SCRIPTVAR_NULL | - SCRIPTVAR_DOUBLE | - SCRIPTVAR_INTEGER | - SCRIPTVAR_BOOLEAN, - SCRIPTVAR_VARTYPEMASK = SCRIPTVAR_DOUBLE | - SCRIPTVAR_INTEGER | - SCRIPTVAR_BOOLEAN | - SCRIPTVAR_STRING | - SCRIPTVAR_FUNCTION | - SCRIPTVAR_OBJECT | - SCRIPTVAR_ARRAY | - SCRIPTVAR_NULL, + SCRIPTVAR_UNDEFINED = 0, + SCRIPTVAR_FUNCTION = 1, + SCRIPTVAR_OBJECT = 2, + SCRIPTVAR_ARRAY = 4, + SCRIPTVAR_DOUBLE = 8, // floating point double + SCRIPTVAR_INTEGER = 16, // integer number + SCRIPTVAR_BOOLEAN = 32, // boolean + SCRIPTVAR_STRING = 64, // string + SCRIPTVAR_NULL = 128, // it seems null is its own data type + + SCRIPTVAR_NATIVE_FNC = 256, // to specify this is a native function + SCRIPTVAR_NATIVE_MFNC = 512, // to specify this is a native function from class->memberFunc + SCRIPTVAR_NUMERICMASK = SCRIPTVAR_NULL | + SCRIPTVAR_DOUBLE | + SCRIPTVAR_INTEGER | + SCRIPTVAR_BOOLEAN, + SCRIPTVAR_VARTYPEMASK = SCRIPTVAR_DOUBLE | + SCRIPTVAR_INTEGER | + SCRIPTVAR_BOOLEAN | + SCRIPTVAR_STRING | + SCRIPTVAR_FUNCTION | + SCRIPTVAR_OBJECT | + SCRIPTVAR_ARRAY | + SCRIPTVAR_NULL, SCRIPTVAR_NATIVE = SCRIPTVAR_NATIVE_FNC | SCRIPTVAR_NATIVE_MFNC, }; @@ -130,45 +131,45 @@ std::string getJSString(const std::string &str); class CScriptException { public: - std::string text; - CScriptException(const std::string &exceptionText); + std::string text; + CScriptException(const std::string &exceptionText); }; class CScriptLex { public: - CScriptLex(const std::string &input); - CScriptLex(CScriptLex *owner, int startChar, int endChar); - ~CScriptLex(void); + CScriptLex(const std::string &input); + CScriptLex(CScriptLex *owner, int startChar, int endChar); + ~CScriptLex(void); - char currCh, nextCh; - int tk; ///< The type of the token that we have - int tokenStart; ///< Position in the data at the beginning of the token we have here - int tokenEnd; ///< Position in the data at the last character of the token we have here - int tokenLastEnd; ///< Position in the data at the last character of the last token - std::string tkStr; ///< Data contained in the token we have here + char currCh, nextCh; + int tk; ///< The type of the token that we have + int tokenStart; ///< Position in the data at the beginning of the token we have here + int tokenEnd; ///< Position in the data at the last character of the token we have here + int tokenLastEnd; ///< Position in the data at the last character of the last token + std::string tkStr; ///< Data contained in the token we have here - void match(int expected_tk); ///< Lexical match wotsit - static std::string getTokenStr(int token); ///< Get the string representation of the given token - void reset(); ///< Reset this lex so we can start again + void match(int expected_tk); ///< Lexical match wotsit + static std::string getTokenStr(int token); ///< Get the string representation of the given token + void reset(); ///< Reset this lex so we can start again - std::string getSubString(int pos); ///< Return a sub-string from the given position up until right now - CScriptLex *getSubLex(int lastPosition); ///< Return a sub-lexer from the given position up until right now + std::string getSubString(int pos); ///< Return a sub-string from the given position up until right now + CScriptLex *getSubLex(int lastPosition); ///< Return a sub-lexer from the given position up until right now - std::string getPosition(int pos=-1); ///< Return a string representing the position in lines and columns of the character pos given + std::string getPosition(int pos=-1); ///< Return a string representing the position in lines and columns of the character pos given protected: - /* When we go into a loop, we use getSubLex to get a lexer for just the sub-part of the - relevant string. This doesn't re-allocate and copy the string, but instead copies - the data pointer and sets dataOwned to false, and dataStart/dataEnd to the relevant things. */ - char *data; ///< Data string to get tokens from - int dataStart, dataEnd; ///< Start and end position in data string - bool dataOwned; ///< Do we own this data string? + /* When we go into a loop, we use getSubLex to get a lexer for just the sub-part of the + relevant string. This doesn't re-allocate and copy the string, but instead copies + the data pointer and sets dataOwned to false, and dataStart/dataEnd to the relevant things. */ + char *data; ///< Data string to get tokens from + int dataStart, dataEnd; ///< Start and end position in data string + bool dataOwned; ///< Do we own this data string? - int dataPos; ///< Position in data (we CAN go past the end of the string here) + int dataPos; ///< Position in data (we CAN go past the end of the string here) - void getNextCh(); - void getNextToken(); ///< Get the text token from our text string + void getNextCh(); + void getNextToken(); ///< Get the text token from our text string }; class CScriptVar; @@ -178,112 +179,115 @@ typedef void (*JSCallback)(CScriptVar *var, void *userdata); class CScriptVarLink { public: - std::string name; - CScriptVarLink *nextSibling; - CScriptVarLink *prevSibling; - CScriptVar *var; - CScriptVar *owned; // pointer to the owner CScriptVar - - CScriptVarLink(CScriptVar *var, const std::string &name = TINYJS_TEMP_NAME); - CScriptVarLink(const CScriptVarLink &link); ///< Copy constructor - ~CScriptVarLink(); - void replaceWith(CScriptVar *newVar); ///< Replace the Variable pointed to - void replaceWith(CScriptVarLink *newVar); ///< Replace the Variable pointed to (just dereferences) + std::string name; + CScriptVarLink *nextSibling; + CScriptVarLink *prevSibling; + CScriptVar *var; + CScriptVar *owned; // pointer to the owner CScriptVar + + CScriptVarLink(CScriptVar *var, const std::string &name = TINYJS_TEMP_NAME); + CScriptVarLink(const CScriptVarLink &link); ///< Copy constructor + ~CScriptVarLink(); + void replaceWith(CScriptVar *newVar); ///< Replace the Variable pointed to + void replaceWith(CScriptVarLink *newVar); ///< Replace the Variable pointed to (just dereferences) }; class NativeFncBase; +typedef std::map SCRIPTVAR_CHILDS; /// Variable class (containing a doubly-linked list of children) class CScriptVar { public: - CScriptVar(); ///< Create undefined - CScriptVar(const std::string &varData, int varFlags); ///< User defined - CScriptVar(const std::string &str); ///< Create a string - CScriptVar(double varData); - CScriptVar(int val); - CScriptVar(bool val); - ~CScriptVar(void); - - CScriptVar *getReturnVar(); ///< If this is a function, get the result value (for use by native functions) - void setReturnVar(CScriptVar *var); ///< Set the result value. Use this when setting complex return data as it avoids a deepCopy() - CScriptVar *getParameter(const std::string &name); ///< If this is a function, get the parameter with the given name (for use by native functions) - - CScriptVarLink *findChild(const std::string &childName); ///< Tries to find a child with the given name, may return 0 - CScriptVarLink *findChildOrCreate(const std::string &childName, int varFlags=SCRIPTVAR_UNDEFINED); ///< Tries to find a child with the given name, or will create it with the given flags - CScriptVarLink *findChildOrCreateByPath(const std::string &path); ///< Tries to find a child with the given path (separated by dots) - CScriptVarLink *addChild(const std::string &childName, CScriptVar *child=NULL); - CScriptVarLink *addChildNoDup(const std::string &childName, CScriptVar *child=NULL); ///< add a child overwriting any with the same name - void removeChild(CScriptVar *child); - void removeLink(CScriptVarLink *link); ///< Remove a specific link (this is faster than finding via a child) - void removeAllChildren(); - CScriptVar *getArrayIndex(int idx); ///< The the value at an array index - void setArrayIndex(int idx, CScriptVar *value); ///< Set the value at an array index - int getArrayLength(); ///< If this is an array, return the number of items in it (else 0) - int getChildren(); ///< Get the number of children - - int getInt(); - bool getBool(); - double getDouble(); - const std::string &getString(); - std::string getParsableString(); ///< get Data as a parsable javascript string - void setInt(int num); - void setBool(bool val); - void setDouble(double val); - void setString(const std::string &str); - void setUndefined(); - - bool isInt() { return (flags&SCRIPTVAR_INTEGER)!=0; } - bool isBool() { return (flags&SCRIPTVAR_BOOLEAN)!=0; } - bool isDouble() { return (flags&SCRIPTVAR_DOUBLE)!=0; } - bool isString() { return (flags&SCRIPTVAR_STRING)!=0; } - bool isNumeric() { return (flags&SCRIPTVAR_NUMERICMASK)!=0; } - bool isFunction() { return (flags&SCRIPTVAR_FUNCTION)!=0; } - bool isObject() { return (flags&SCRIPTVAR_OBJECT)!=0; } - bool isArray() { return (flags&SCRIPTVAR_ARRAY)!=0; } - bool isNative() { return (flags&SCRIPTVAR_NATIVE)!=0; } - bool isNative_ClassMemberFnc() { return (flags&SCRIPTVAR_NATIVE_MFNC)!=0; } - bool isUndefined() { return (flags & SCRIPTVAR_VARTYPEMASK) == SCRIPTVAR_UNDEFINED; } - bool isNull() { return (flags & SCRIPTVAR_NULL)!=0; } - bool isBasic() { return firstChild==0; } ///< Is this *not* an array/object/etc - - CScriptVar *mathsOp(CScriptVar *b, int op); ///< do a maths op with another script variable - void copyValue(CScriptVar *val); ///< copy the value from the value given - CScriptVar *deepCopy(); ///< deep copy this node and return the result - - void trace(std::string indentStr = "", const std::string &name = ""); ///< Dump out the contents of this using trace - std::string getFlagsAsString(); ///< For debugging - just dump a string version of the flags - void getJSON(std::ostringstream &destination, const std::string linePrefix=""); ///< Write out all the JS code needed to recreate this script variable to the stream (as JSON) - void setCallback(JSCallback callback, void *userdata); + CScriptVar(); ///< Create undefined + CScriptVar(const std::string &varData, int varFlags); ///< User defined + CScriptVar(const std::string &str); ///< Create a string + CScriptVar(double varData); + CScriptVar(int val); + CScriptVar(bool val); + ~CScriptVar(void); + + CScriptVar *getReturnVar(); ///< If this is a function, get the result value (for use by native functions) + void setReturnVar(CScriptVar *var); ///< Set the result value. Use this when setting complex return data as it avoids a deepCopy() + CScriptVar *getParameter(const std::string &name); ///< If this is a function, get the parameter with the given name (for use by native functions) + + CScriptVarLink *findChild(const std::string &childName); ///< Tries to find a child with the given name, may return 0 + CScriptVarLink *findChildOrCreate(const std::string &childName, int varFlags=SCRIPTVAR_UNDEFINED); ///< Tries to find a child with the given name, or will create it with the given flags + CScriptVarLink *findChildOrCreateByPath(const std::string &path); ///< Tries to find a child with the given path (separated by dots) + CScriptVarLink *addChild(const std::string &childName, CScriptVar *child=NULL); + CScriptVarLink *addChildNoDup(const std::string &childName, CScriptVar *child=NULL); ///< add a child overwriting any with the same name + void removeChild(CScriptVar *child); + void removeLink(CScriptVarLink *link); ///< Remove a specific link (this is faster than finding via a child) + void removeAllChildren(); + CScriptVar *getArrayIndex(int idx); ///< The the value at an array index + void setArrayIndex(int idx, CScriptVar *value); ///< Set the value at an array index + int getArrayLength(); ///< If this is an array, return the number of items in it (else 0) + int getChildren() { return Childs.size(); } ///< Get the number of children + + int getInt(); + bool getBool(); + double getDouble(); + const std::string &getString(); + std::string getParsableString(); ///< get Data as a parsable javascript string + void setInt(int num); + void setBool(bool val); + void setDouble(double val); + void setString(const std::string &str); + void setUndefined(); + + bool isInt() { return (flags&SCRIPTVAR_INTEGER)!=0; } + bool isBool() { return (flags&SCRIPTVAR_BOOLEAN)!=0; } + bool isDouble() { return (flags&SCRIPTVAR_DOUBLE)!=0; } + bool isString() { return (flags&SCRIPTVAR_STRING)!=0; } + bool isNumeric() { return (flags&SCRIPTVAR_NUMERICMASK)!=0; } + bool isFunction() { return (flags&SCRIPTVAR_FUNCTION)!=0; } + bool isObject() { return (flags&SCRIPTVAR_OBJECT)!=0; } + bool isArray() { return (flags&SCRIPTVAR_ARRAY)!=0; } + bool isNative() { return (flags&SCRIPTVAR_NATIVE)!=0; } + bool isNative_ClassMemberFnc() { return (flags&SCRIPTVAR_NATIVE_MFNC)!=0; } + bool isUndefined() { return (flags & SCRIPTVAR_VARTYPEMASK) == SCRIPTVAR_UNDEFINED; } + bool isNull() { return (flags & SCRIPTVAR_NULL)!=0; } +//// bool isBasic() { return firstChild==0; } ///< Is this *not* an array/object/etc + bool isBasic() { return Childs.empty(); } ///< Is this *not* an array/object/etc + + CScriptVar *mathsOp(CScriptVar *b, int op); ///< do a maths op with another script variable + void copyValue(CScriptVar *val); ///< copy the value from the value given + CScriptVar *deepCopy(); ///< deep copy this node and return the result + + void trace(std::string indentStr = "", const std::string &name = ""); ///< Dump out the contents of this using trace + std::string getFlagsAsString(); ///< For debugging - just dump a string version of the flags + void getJSON(std::ostringstream &destination, const std::string linePrefix=""); ///< Write out all the JS code needed to recreate this script variable to the stream (as JSON) + void setCallback(JSCallback callback, void *userdata); void setCallback(NativeFncBase *callback, void *userdata); - CScriptVarLink *firstChild; - CScriptVarLink *lastChild; +// CScriptVarLink *firstChild; +// CScriptVarLink *lastChild; + SCRIPTVAR_CHILDS Childs; - /// For memory management/garbage collection - CScriptVar *ref(); ///< Add reference to this variable - void unref(); ///< Remove a reference, and delete this variable if required - int getRefs(); ///< Get the number of references to this script variable + /// For memory management/garbage collection + CScriptVar *ref(); ///< Add reference to this variable + void unref(); ///< Remove a reference, and delete this variable if required + int getRefs(); ///< Get the number of references to this script variable protected: - int refs; ///< The number of references held to this - used for garbage collection + int refs; ///< The number of references held to this - used for garbage collection CScriptVarLink *__proto__; - std::string data; ///< The contents of this variable if it is a string - long intData; ///< The contents of this variable if it is an int - double doubleData; ///< The contents of this variable if it is a double - int flags; ///< the flags determine the type of the variable - int/double/string/etc + std::string data; ///< The contents of this variable if it is a string + long intData; ///< The contents of this variable if it is an int + double doubleData; ///< The contents of this variable if it is a double + int flags; ///< the flags determine the type of the variable - int/double/string/etc union { - JSCallback jsCallback; ///< Callback for native functions + JSCallback jsCallback; ///< Callback for native functions NativeFncBase *jsCallbackClass; ///< Wrapper for Class-Member-Functions as Callback for native functions }; - void *jsCallbackUserData; ///< user data passed as second argument to native functions + void *jsCallbackUserData; ///< user data passed as second argument to native functions - void init(); ///< initialisation of data members + void init(); ///< initialisation of data members - /** Copy the basic data and flags from the variable given, with no - * children. Should be used internally only - by copyValue and deepCopy */ - void copySimpleData(CScriptVar *val); + /** Copy the basic data and flags from the variable given, with no + * children. Should be used internally only - by copyValue and deepCopy */ + void copySimpleData(CScriptVar *val); - friend class CTinyJS; + friend class CTinyJS; }; class NativeFncBase @@ -307,78 +311,89 @@ class NativeFnc : public NativeFncBase class CTinyJS { public: - CTinyJS(); - ~CTinyJS(); - - void execute(const std::string &code); - /** Evaluate the given code and return a link to a javascript object, - * useful for (dangerous) JSON parsing. If nothing to return, will return - * 'undefined' variable type. CScriptVarLink is returned as this will - * automatically unref the result as it goes out of scope. If you want to - * keep it, you must use ref() and unref() */ - CScriptVarLink evaluateComplex(const std::string &code); - /** Evaluate the given code and return a string. If nothing to return, will return - * 'undefined' */ - std::string evaluate(const std::string &code); - - /// add a native function to be called from TinyJS - /** example: - \code - void scRandInt(CScriptVar *c, void *userdata) { ... } - tinyJS->addNative("function randInt(min, max)", scRandInt, 0); - \endcode - - or - - \code - void scSubstring(CScriptVar *c, void *userdata) { ... } - tinyJS->addNative("function String.substring(lo, hi)", scSubstring, 0); - \endcode - */ - void addNative(const std::string &funcDesc, JSCallback ptr, void *userdata=0); - void addNative(const std::string &funcDesc, NativeFncBase *ptr, void *userdata=0); + CTinyJS(); + ~CTinyJS(); + + void execute(const std::string &code); + /** Evaluate the given code and return a link to a javascript object, + * useful for (dangerous) JSON parsing. If nothing to return, will return + * 'undefined' variable type. CScriptVarLink is returned as this will + * automatically unref the result as it goes out of scope. If you want to + * keep it, you must use ref() and unref() */ + CScriptVarLink evaluateComplex(const std::string &code); + /** Evaluate the given code and return a string. If nothing to return, will return + * 'undefined' */ + std::string evaluate(const std::string &code); + + /// add a native function to be called from TinyJS + /** example: + \code + void scRandInt(CScriptVar *c, void *userdata) { ... } + tinyJS->addNative("function randInt(min, max)", scRandInt, 0); + \endcode + + or + + \code + void scSubstring(CScriptVar *c, void *userdata) { ... } + tinyJS->addNative("function String.substring(lo, hi)", scSubstring, 0); + \endcode + or + + \code + class Class + { + public: + void scSubstring(CScriptVar *c, void *userdata) { ... } + }; + Class Instanz; + tinyJS->addNative("function String.substring(lo, hi)", &Instanz, &Class::*scSubstring, 0); + \endcode + */ + void addNative(const std::string &funcDesc, JSCallback ptr, void *userdata=0); + void addNative(const std::string &funcDesc, NativeFncBase *ptr, void *userdata=0); template void addNative(const std::string &funcDesc, C *class_ptr, void(C::*class_fnc)(CScriptVar *, void *), void *userdata=0) { addNative(funcDesc, new NativeFnc(class_ptr, class_fnc), userdata); } - /// Get the value of the given variable, or return 0 - const std::string *getVariable(const std::string &path); + /// Get the value of the given variable, or return 0 + const std::string *getVariable(const std::string &path); - /// Send all variables to stdout - void trace(); + /// Send all variables to stdout + void trace(); - CScriptVar *root; /// root of symbol table + CScriptVar *root; /// root of symbol table private: - CScriptLex *l; /// current lexer + CScriptLex *l; /// current lexer int runtimeFlags; - std::vector scopes; /// stack of scopes when parsing - CScriptVar *stringClass; /// Built in string class - CScriptVar *objectClass; /// Built in object class - CScriptVar *arrayClass; /// Built in array class - - // parsing - in order of precedence - CScriptVarLink *factor(bool &execute); - CScriptVarLink *unary(bool &execute); - CScriptVarLink *term(bool &execute); - CScriptVarLink *expression(bool &execute); + std::vector scopes; /// stack of scopes when parsing + CScriptVar *stringClass; /// Built in string class + CScriptVar *objectClass; /// Built in object class + CScriptVar *arrayClass; /// Built in array class + + // parsing - in order of precedence + CScriptVarLink *factor(bool &execute); + CScriptVarLink *unary(bool &execute); + CScriptVarLink *term(bool &execute); + CScriptVarLink *expression(bool &execute); CScriptVarLink *binary_shift(bool &execute); - CScriptVarLink *relation(bool &execute, int set=LEX_EQUAL, int set_n='<'); - CScriptVarLink *logic_binary(bool &execute, int op='|', int op_n1='^', int op_n2='&'); - CScriptVarLink *logic(bool &execute, int op=LEX_OROR, int op_n=LEX_ANDAND); + CScriptVarLink *relation(bool &execute, int set=LEX_EQUAL, int set_n='<'); + CScriptVarLink *logic_binary(bool &execute, int op='|', int op_n1='^', int op_n2='&'); + CScriptVarLink *logic(bool &execute, int op=LEX_OROR, int op_n=LEX_ANDAND); CScriptVarLink *condition(bool &execute); CScriptVarLink *assignment(bool &execute); - CScriptVarLink *base(bool &execute); - void block(bool &execute); - void statement(bool &execute); - // parsing utility functions - CScriptVarLink *parseFunctionDefinition(); - void parseFunctionArguments(CScriptVar *funcVar); - - CScriptVarLink *findInScopes(const std::string &childName); ///< Finds a child, looking recursively up the scopes - /// Look up in any parent classes of the given object - CScriptVarLink *findInParentClasses(CScriptVar *object, const std::string &name); + CScriptVarLink *base(bool &execute); + void block(bool &execute); + void statement(bool &execute); + // parsing utility functions + CScriptVarLink *parseFunctionDefinition(); + void parseFunctionArguments(CScriptVar *funcVar); + + CScriptVarLink *findInScopes(const std::string &childName); ///< Finds a child, looking recursively up the scopes + /// Look up in any parent classes of the given object + CScriptVarLink *findInParentClasses(CScriptVar *object, const std::string &name); CScriptVar *addNative(const std::string &funcDesc); public: // native Functions void scEval(CScriptVar *c, void *data); diff --git a/TinyJS_Functions.cpp b/TinyJS_Functions.cpp index 8eb7961..4585268 100755 --- a/TinyJS_Functions.cpp +++ b/TinyJS_Functions.cpp @@ -42,110 +42,110 @@ using namespace std; // ----------------------------------------------- Actual Functions void scTrace(CScriptVar *UNUSED(c), void * userdata) { - CTinyJS *js = (CTinyJS*)userdata; - js->root->trace(); + CTinyJS *js = (CTinyJS*)userdata; + js->root->trace(); } void scObjectDump(CScriptVar *c, void *) { - c->getParameter("this")->trace("> "); + c->getParameter("this")->trace("> "); } void scObjectClone(CScriptVar *c, void *) { - CScriptVar *obj = c->getParameter("this"); - c->getReturnVar()->copyValue(obj); + CScriptVar *obj = c->getParameter("this"); + c->getReturnVar()->copyValue(obj); } void scMathRand(CScriptVar *c, void *) { - c->getReturnVar()->setDouble((double)rand()/RAND_MAX); + c->getReturnVar()->setDouble((double)rand()/RAND_MAX); } void scMathRandInt(CScriptVar *c, void *) { - int min = c->getParameter("min")->getInt(); - int max = c->getParameter("max")->getInt(); - int val = min + (int)((long)rand()*(1+max-min)/RAND_MAX); - if (val>max) val=max; - c->getReturnVar()->setInt(val); + int min = c->getParameter("min")->getInt(); + int max = c->getParameter("max")->getInt(); + int val = min + (int)((long)rand()*(1+max-min)/RAND_MAX); + if (val>max) val=max; + c->getReturnVar()->setInt(val); } void scCharToInt(CScriptVar *c, void *) { - string str = c->getParameter("ch")->getString();; - int val = 0; - if (str.length()>0) - val = (int)str.c_str()[0]; - c->getReturnVar()->setInt(val); + string str = c->getParameter("ch")->getString();; + int val = 0; + if (str.length()>0) + val = (int)str.c_str()[0]; + c->getReturnVar()->setInt(val); } void scStringIndexOf(CScriptVar *c, void *) { - string str = c->getParameter("this")->getString(); - string search = c->getParameter("search")->getString(); - size_t p = str.find(search); - int val = (p==string::npos) ? -1 : p; - c->getReturnVar()->setInt(val); + string str = c->getParameter("this")->getString(); + string search = c->getParameter("search")->getString(); + size_t p = str.find(search); + int val = (p==string::npos) ? -1 : p; + c->getReturnVar()->setInt(val); } void scStringSubstring(CScriptVar *c, void *) { - string str = c->getParameter("this")->getString(); - int lo = c->getParameter("lo")->getInt(); - int hi = c->getParameter("hi")->getInt(); - - int l = hi-lo; - if (l>0 && lo>=0 && lo+l<=(int)str.length()) - c->getReturnVar()->setString(str.substr(lo, l)); - else - c->getReturnVar()->setString(""); + string str = c->getParameter("this")->getString(); + int lo = c->getParameter("lo")->getInt(); + int hi = c->getParameter("hi")->getInt(); + + int l = hi-lo; + if (l>0 && lo>=0 && lo+l<=(int)str.length()) + c->getReturnVar()->setString(str.substr(lo, l)); + else + c->getReturnVar()->setString(""); } void scStringCharAt(CScriptVar *c, void *) { - string str = c->getParameter("this")->getString(); - int p = c->getParameter("pos")->getInt(); - if (p>=0 && p<(int)str.length()) - c->getReturnVar()->setString(str.substr(p, 1)); - else - c->getReturnVar()->setString(""); + string str = c->getParameter("this")->getString(); + int p = c->getParameter("pos")->getInt(); + if (p>=0 && p<(int)str.length()) + c->getReturnVar()->setString(str.substr(p, 1)); + else + c->getReturnVar()->setString(""); } void scIntegerParseInt(CScriptVar *c, void *) { - string str = c->getParameter("str")->getString(); - int val = strtol(str.c_str(),0,0); - c->getReturnVar()->setInt(val); + string str = c->getParameter("str")->getString(); + int val = strtol(str.c_str(),0,0); + c->getReturnVar()->setInt(val); } void scIntegerValueOf(CScriptVar *c, void *) { - string str = c->getParameter("str")->getString(); + string str = c->getParameter("str")->getString(); - int val = 0; - if (str.length()==1) - val = str[0]; - c->getReturnVar()->setInt(val); + int val = 0; + if (str.length()==1) + val = str[0]; + c->getReturnVar()->setInt(val); } void scJSONStringify(CScriptVar *c, void *) { - std::ostringstream result; - c->getParameter("obj")->getJSON(result); - c->getReturnVar()->setString(result.str()); + std::ostringstream result; + c->getParameter("obj")->getJSON(result); + c->getReturnVar()->setString(result.str()); } void scEval(CScriptVar *c, void *data) { - CTinyJS *tinyJS = (CTinyJS *)data; - std::string str = c->getParameter("jsCode")->getString(); - c->setReturnVar(tinyJS->evaluateComplex(str).var); + CTinyJS *tinyJS = (CTinyJS *)data; + std::string str = c->getParameter("jsCode")->getString(); + c->setReturnVar(tinyJS->evaluateComplex(str).var); } // ----------------------------------------------- Register Functions void registerFunctions(CTinyJS *tinyJS) { // tinyJS->addNative("function eval(jsCode)", scEval, tinyJS); // execute the given string and return the result - tinyJS->addNative("function trace()", scTrace, tinyJS); - tinyJS->addNative("function Object.dump()", scObjectDump, 0); - tinyJS->addNative("function Object.clone()", scObjectClone, 0); - tinyJS->addNative("function Math.rand()", scMathRand, 0); - tinyJS->addNative("function Math.randInt(min, max)", scMathRandInt, 0); - tinyJS->addNative("function charToInt(ch)", scCharToInt, 0); // convert a character to an int - get its value - tinyJS->addNative("function String.indexOf(search)", scStringIndexOf, 0); // find the position of a string in a string, -1 if not - tinyJS->addNative("function String.substring(lo,hi)", scStringSubstring, 0); - tinyJS->addNative("function String.charAt(pos)", scStringCharAt, 0); - tinyJS->addNative("function Integer.parseInt(str)", scIntegerParseInt, 0); // string to int - tinyJS->addNative("function Integer.valueOf(str)", scIntegerValueOf, 0); // value of a single character - tinyJS->addNative("function JSON.stringify(obj, replacer)", scJSONStringify, 0); // convert to JSON. replacer is ignored at the moment - // JSON.parse is left out as you can (unsafely!) use eval instead + tinyJS->addNative("function trace()", scTrace, tinyJS); + tinyJS->addNative("function Object.dump()", scObjectDump, 0); + tinyJS->addNative("function Object.clone()", scObjectClone, 0); + tinyJS->addNative("function Math.rand()", scMathRand, 0); + tinyJS->addNative("function Math.randInt(min, max)", scMathRandInt, 0); + tinyJS->addNative("function charToInt(ch)", scCharToInt, 0); // convert a character to an int - get its value + tinyJS->addNative("function String.indexOf(search)", scStringIndexOf, 0); // find the position of a string in a string, -1 if not + tinyJS->addNative("function String.substring(lo,hi)", scStringSubstring, 0); + tinyJS->addNative("function String.charAt(pos)", scStringCharAt, 0); + tinyJS->addNative("function Integer.parseInt(str)", scIntegerParseInt, 0); // string to int + tinyJS->addNative("function Integer.valueOf(str)", scIntegerValueOf, 0); // value of a single character + tinyJS->addNative("function JSON.stringify(obj, replacer)", scJSONStringify, 0); // convert to JSON. replacer is ignored at the moment + // JSON.parse is left out as you can (unsafely!) use eval instead } From d1aac300d72587484150f404a2e042c2f7b76180 Mon Sep 17 00:00:00 2001 From: armin Date: Sun, 5 Sep 2010 12:32:00 +0000 Subject: [PATCH 04/43] added variadic args by injecting a "parameters" object in the functions-scope for exemple: function test(a, b) { ... } test(25); // --> a=25, b=undefined, parameters.length=1, parameters[0]=25 test(25,12,19) // --> a=25, b=12, parameters.length=3, parameters[0]=25, parameters[1]=12, parameters[2]=19 --- TinyJS.cpp | 229 +++++++++-------------------------------------------- TinyJS.h | 31 ++++---- 2 files changed, 50 insertions(+), 210 deletions(-) diff --git a/TinyJS.cpp b/TinyJS.cpp index 23f5589..172a116 100755 --- a/TinyJS.cpp +++ b/TinyJS.cpp @@ -735,8 +735,6 @@ CScriptVar::~CScriptVar(void) { } void CScriptVar::init() { -//// firstChild = 0; -//// lastChild = 0; flags = 0; jsCallback = 0; jsCallbackUserData = 0; @@ -763,16 +761,6 @@ CScriptVarLink *CScriptVar::findChild(const string &childName) { SCRIPTVAR_CHILDS::iterator it = Childs.find(childName); if(it != Childs.end()) return it->second; -//// -/* - CScriptVarLink *v = firstChild; - while (v) { - if (v->name.compare(childName)==0) - return v; - v = v->nextSibling; - } -*/ -//// return NULL; } @@ -780,12 +768,6 @@ CScriptVarLink *CScriptVar::findChildOrCreate(const string &childName, int varFl CScriptVarLink *l = findChild(childName); if (l) return l; return addChild(childName, new CScriptVar(TINYJS_BLANK_DATA, varFlags)); -//// -/* - - return addChild(childName, new CScriptVar(TINYJS_BLANK_DATA, varFlags)); -*/ -//// } CScriptVarLink *CScriptVar::findChildOrCreateByPath(const std::string &path) { @@ -798,9 +780,9 @@ CScriptVarLink *CScriptVar::findChildOrCreateByPath(const std::string &path) { } CScriptVarLink *CScriptVar::addChild(const std::string &childName, CScriptVar *child) { - if (isUndefined()) { - flags = SCRIPTVAR_OBJECT; - } + if (isUndefined()) { + flags = SCRIPTVAR_OBJECT; + } // if no child supplied, create one if (!child) child = new CScriptVar(); @@ -811,19 +793,6 @@ CScriptVarLink *CScriptVar::addChild(const std::string &childName, CScriptVar *c CScriptVarLink *link = new CScriptVarLink(child, childName); link->owned = this; return Childs[childName] = link; -//// -/* - if (lastChild) { - lastChild->nextSibling = link; - link->prevSibling = lastChild; - lastChild = link; - } else { - firstChild = link; - lastChild = link; - } - return link; -*/ -//// } CScriptVarLink *CScriptVar::addChildNoDup(const std::string &childName, CScriptVar *child) { // if no child supplied, create one @@ -837,7 +806,6 @@ CScriptVarLink *CScriptVar::addChildNoDup(const std::string &childName, CScriptV CScriptVarLink *link = new CScriptVarLink(child, childName); link->owned = this; v = Childs[childName] = link; -//// v = addChild(childName, child); } return v; @@ -851,18 +819,6 @@ void CScriptVar::removeChild(CScriptVar *child) { } } ASSERT(0);// removeChild - the child is not atached to this var -//// -/* - CScriptVarLink *link = firstChild; - while (link) { - if (link->var == child) - break; - link = link->nextSibling; - } - ASSERT(link); - removeLink(link); -*/ -//// } void CScriptVar::removeLink(CScriptVarLink *link) { @@ -872,18 +828,6 @@ void CScriptVar::removeLink(CScriptVarLink *link) { ASSERT(0); // removeLink - the link is not atached to this var #endif Childs.erase(link->name); -//// -/* - if (link->nextSibling) - link->nextSibling->prevSibling = link->prevSibling; - if (link->prevSibling) - link->prevSibling->nextSibling = link->nextSibling; - if (lastChild == link) - lastChild = link->prevSibling; - if (firstChild == link) - firstChild = link->nextSibling; -*/ -//// delete link; } @@ -893,18 +837,6 @@ void CScriptVar::removeAllChildren() { delete it->second; Childs.erase(it); } -//// -/* - CScriptVarLink *c = firstChild; - while (c) { - CScriptVarLink *t = c->nextSibling; - delete c; - c = t; - } - firstChild = 0; - lastChild = 0; -*/ -//// } CScriptVar *CScriptVar::getArrayIndex(int idx) { @@ -941,33 +873,8 @@ int CScriptVar::getArrayLength() { if (val > highest) highest = val; } } -//// -/* - CScriptVarLink *link = firstChild; - while (link) { - if (isNumber(link->name)) { - int val = atoi(link->name.c_str()); - if (val > highest) highest = val; - } - link = link->nextSibling; - } -*/ -//// return highest+1; } -//// moved to TinyJS.h -/* -int CScriptVar::getChildren() { - int n = 0; - CScriptVarLink *link = firstChild; - while (link) { - n++; - link = link->nextSibling; - } - return n; -} -*/ -//// int CScriptVar::getInt() { /* strtol understands about hex and octal */ @@ -979,7 +886,7 @@ int CScriptVar::getInt() { } bool CScriptVar::getBool() { - if (isInt()) return intData!=0; + if (isInt() || isBool()) return intData!=0; if (isNull()) return 0; if (isUndefined()) return 0; if (isDouble()) return doubleData!=0; @@ -1163,23 +1070,6 @@ void CScriptVar::copyValue(CScriptVar *val) { // remove all current children removeAllChildren(); // copy children of 'val' -//// -/* - CScriptVarLink *child = val->firstChild; - while (child) { - CScriptVar *copied; - // don't copy the 'parent' object... - if (child->name != TINYJS_PROTOTYPE_CLASS) - copied = child->var->deepCopy(); - else - copied = child->var; - - addChild(child->name, copied); - - child = child->nextSibling; - } -*/ -//// for(SCRIPTVAR_CHILDS::iterator it = val->Childs.begin(); it != val->Childs.end(); ++it) { CScriptVar *copied; // don't copy the 'parent' object... @@ -1199,22 +1089,6 @@ CScriptVar *CScriptVar::deepCopy() { CScriptVar *newVar = new CScriptVar(); newVar->copySimpleData(this); // copy children -//// -/* - CScriptVarLink *child = firstChild; - while (child) { - CScriptVar *copied; - // don't copy the 'parent' object... - if (child->name != TINYJS_PROTOTYPE_CLASS) - copied = child->var->deepCopy(); - else - copied = child->var; - - newVar->addChild(child->name, copied); - child = child->nextSibling; - } -*/ -//// for(SCRIPTVAR_CHILDS::iterator it = Childs.begin(); it != Childs.end(); ++it) { CScriptVar *copied; // don't copy the 'parent' object... @@ -1235,15 +1109,6 @@ void CScriptVar::trace(string indentStr, const string &name) { getString().c_str(), getFlagsAsString().c_str()); string indent = indentStr+" "; -//// -/* - CScriptVarLink *link = firstChild; - while (link) { - link->var->trace(indent, link->name); - link = link->nextSibling; - } -*/ -//// for(SCRIPTVAR_CHILDS::iterator it = Childs.begin(); it != Childs.end(); ++it) { it->second->var->trace(indent, it->first); } @@ -1270,21 +1135,11 @@ string CScriptVar::getParsableString() { ostringstream funcStr; funcStr << "function ("; // get list of parameters - SCRIPTVAR_CHILDS::iterator last_it = --(Childs.end());//// --last_it; + SCRIPTVAR_CHILDS::iterator last_it = --(Childs.end()); for(SCRIPTVAR_CHILDS::iterator it = Childs.begin(); it != Childs.end(); ++it) { funcStr << it->first; if (it != last_it) funcStr << ", "; } -//// -/* - CScriptVarLink *link = firstChild; - while (link) { - funcStr << link->name; - if (link->nextSibling) funcStr << ","; - link = link->nextSibling; - } -*/ -//// // add function body funcStr << ") " << getString(); return funcStr.str(); @@ -1302,7 +1157,7 @@ void CScriptVar::getJSON(ostringstream &destination, const string linePrefix) { string indentedLinePrefix = linePrefix+" "; // children - handle with bracketed list destination << "{ \n"; - SCRIPTVAR_CHILDS::iterator last_it = --(Childs.end());//// --last_it; + SCRIPTVAR_CHILDS::iterator last_it = --(Childs.end()); for(SCRIPTVAR_CHILDS::iterator it = Childs.begin(); it != Childs.end(); ++it) { destination << indentedLinePrefix; if (isAlphaNum(it->first)) @@ -1316,25 +1171,6 @@ void CScriptVar::getJSON(ostringstream &destination, const string linePrefix) { } } destination << "\n" << linePrefix << "}"; -//// -/* - CScriptVarLink *link = firstChild; - while (link) { - destination << indentedLinePrefix; - if (isAlphaNum(link->name)) - destination << link->name; - else - destination << getJSString(link->name); - destination << " : "; - link->var->getJSON(destination, indentedLinePrefix); - link = link->nextSibling; - if (link) { - destination << ",\n"; - } - } - destination << "\n" << linePrefix << "}"; -*/ -//// } else if (isArray()) { string indentedLinePrefix = linePrefix+" "; destination << "[\n"; @@ -1537,7 +1373,11 @@ CScriptVarLink *CTinyJS::parseFunctionDefinition() { funcVar->var->data = l->getSubString(funcBegin); return funcVar; } - +string int2string(int intData) { + char buffer[32]; + sprintf_s(buffer, sizeof(buffer), "%ld", intData); + return buffer; +} CScriptVarLink *CTinyJS::factor(bool &execute) { if (l->tk==LEX_R_TRUE) { l->match(LEX_R_TRUE); @@ -1592,41 +1432,44 @@ CScriptVarLink *CTinyJS::factor(bool &execute) { else functionRoot->addChildNoDup("this", new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT)); // always add a this-Object // grab in all parameters + CScriptVar *parameters = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT); + int parameters_idx = 0; for(SCRIPTVAR_CHILDS::iterator it = a->var->Childs.begin(); it != a->var->Childs.end(); ++it) { + if (l->tk!=')') { + CScriptVarLink *value = assignment(execute); + if (execute) { + if (value->var->isBasic()) { + // pass by value + functionRoot->addChild( it->first, parameters->addChild(int2string(parameters_idx++) , value->var->deepCopy())->var); + } else { + // pass by reference + functionRoot->addChild(it->first, parameters->addChild(int2string(parameters_idx++), value->var->deepCopy())->var); + } + } + CLEAN(value); + } else { + functionRoot->addChild(it->first, new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_UNDEFINED)); + } + if (l->tk!=')') l->match(','); + } + while(l->tk!=')') { CScriptVarLink *value = assignment(execute); if (execute) { if (value->var->isBasic()) { // pass by value - functionRoot->addChild( it->first, value->var->deepCopy()); + parameters->addChild(int2string(parameters_idx++) , value->var->deepCopy()); } else { // pass by reference - functionRoot->addChild(it->first, value->var); + parameters->addChild(int2string(parameters_idx++), value->var->deepCopy()); } } CLEAN(value); if (l->tk!=')') l->match(','); } -//// -/* - CScriptVarLink *v = a->var->firstChild; - while (v) { - CScriptVarLink *value = base(execute); - if (execute) { - if (value->var->isBasic()) { - // pass by value - functionRoot->addChild(v->name, value->var->deepCopy()); - } else { - // pass by reference - functionRoot->addChild(v->name, value->var); - } - } - CLEAN(value); - if (l->tk!=')') l->match(','); - v = v->nextSibling; - } -*/ -//// + l->match(')'); + parameters->addChild("length", new CScriptVar(parameters_idx)); + functionRoot->addChild("parameters", parameters); // setup a return variable CScriptVarLink *returnVar = NULL; // execute function! diff --git a/TinyJS.h b/TinyJS.h index d5098d0..c35f424 100755 --- a/TinyJS.h +++ b/TinyJS.h @@ -98,25 +98,25 @@ enum SCRIPTVAR_FLAGS { SCRIPTVAR_NATIVE_FNC = 256, // to specify this is a native function SCRIPTVAR_NATIVE_MFNC = 512, // to specify this is a native function from class->memberFunc SCRIPTVAR_NUMERICMASK = SCRIPTVAR_NULL | - SCRIPTVAR_DOUBLE | - SCRIPTVAR_INTEGER | - SCRIPTVAR_BOOLEAN, + SCRIPTVAR_DOUBLE | + SCRIPTVAR_INTEGER | + SCRIPTVAR_BOOLEAN, SCRIPTVAR_VARTYPEMASK = SCRIPTVAR_DOUBLE | - SCRIPTVAR_INTEGER | - SCRIPTVAR_BOOLEAN | - SCRIPTVAR_STRING | - SCRIPTVAR_FUNCTION | - SCRIPTVAR_OBJECT | - SCRIPTVAR_ARRAY | - SCRIPTVAR_NULL, + SCRIPTVAR_INTEGER | + SCRIPTVAR_BOOLEAN | + SCRIPTVAR_STRING | + SCRIPTVAR_FUNCTION | + SCRIPTVAR_OBJECT | + SCRIPTVAR_ARRAY | + SCRIPTVAR_NULL, SCRIPTVAR_NATIVE = SCRIPTVAR_NATIVE_FNC | - SCRIPTVAR_NATIVE_MFNC, + SCRIPTVAR_NATIVE_MFNC, }; enum RUNTIME_FLAGS { - RUNTIME_CANBREAK = 1, - RUNTIME_BREAK = 2, + RUNTIME_CANBREAK = 1, + RUNTIME_BREAK = 2, RUNTIME_CANCONTINUE = 4, - RUNTIME_CONTINUE = 8, + RUNTIME_CONTINUE = 8, }; #define RUNTIME_LOOP_MASK (RUNTIME_CANBREAK | RUNTIME_BREAK | RUNTIME_CANCONTINUE | RUNTIME_CONTINUE) @@ -246,7 +246,6 @@ class CScriptVar bool isNative_ClassMemberFnc() { return (flags&SCRIPTVAR_NATIVE_MFNC)!=0; } bool isUndefined() { return (flags & SCRIPTVAR_VARTYPEMASK) == SCRIPTVAR_UNDEFINED; } bool isNull() { return (flags & SCRIPTVAR_NULL)!=0; } -//// bool isBasic() { return firstChild==0; } ///< Is this *not* an array/object/etc bool isBasic() { return Childs.empty(); } ///< Is this *not* an array/object/etc CScriptVar *mathsOp(CScriptVar *b, int op); ///< do a maths op with another script variable @@ -259,8 +258,6 @@ class CScriptVar void setCallback(JSCallback callback, void *userdata); void setCallback(NativeFncBase *callback, void *userdata); -// CScriptVarLink *firstChild; -// CScriptVarLink *lastChild; SCRIPTVAR_CHILDS Childs; /// For memory management/garbage collection From 6df8aa99ef34fb7a0cde172a24ae80b59983cc59 Mon Sep 17 00:00:00 2001 From: armin Date: Mon, 6 Sep 2010 19:21:13 +0000 Subject: [PATCH 05/43] fixed a segfaults --- TinyJS.cpp | 307 ++++++++++++++++++++++++++--------------------------- TinyJS.h | 61 +++++------ 2 files changed, 180 insertions(+), 188 deletions(-) diff --git a/TinyJS.cpp b/TinyJS.cpp index 172a116..178f89e 100755 --- a/TinyJS.cpp +++ b/TinyJS.cpp @@ -236,6 +236,12 @@ void replace(string &str, char textFrom, const char *textTo) { p = str.find(textFrom, p+sLen); } } +string &int2string(int intData, string &inString=string()); +string &int2string(int intData, string &inString) { + char buffer[32]; + sprintf_s(buffer, sizeof(buffer), "%d", intData); + return inString=buffer; +} /// convert the given string into a quoted string suitable for javascript std::string getJSString(const std::string &str) { @@ -840,17 +846,14 @@ void CScriptVar::removeAllChildren() { } CScriptVar *CScriptVar::getArrayIndex(int idx) { - char sIdx[64]; - sprintf_s(sIdx, sizeof(sIdx), "%d", idx); - CScriptVarLink *link = findChild(sIdx); + CScriptVarLink *link = findChild(int2string(idx)); if (link) return link->var; else return new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_NULL); // undefined } void CScriptVar::setArrayIndex(int idx, CScriptVar *value) { - char sIdx[64]; - sprintf_s(sIdx, sizeof(sIdx), "%d", idx); - CScriptVarLink *link = findChild(sIdx); + string sIdx; + CScriptVarLink *link = findChild(int2string(idx, sIdx)); if (link) { if (value->isUndefined()) @@ -911,10 +914,7 @@ const string &CScriptVar::getString() { static string s_false = "false"; if (isBool()) return getInt() ? s_true : s_false; if (isInt()) { - char buffer[32]; - sprintf_s(buffer, sizeof(buffer), "%ld", intData); - data = buffer; - return data; + return int2string(intData, data); } if (isDouble()) { char buffer[32]; @@ -1135,10 +1135,10 @@ string CScriptVar::getParsableString() { ostringstream funcStr; funcStr << "function ("; // get list of parameters - SCRIPTVAR_CHILDS::iterator last_it = --(Childs.end()); + int count = Childs.size(); for(SCRIPTVAR_CHILDS::iterator it = Childs.begin(); it != Childs.end(); ++it) { funcStr << it->first; - if (it != last_it) funcStr << ", "; + if(--count) funcStr << ", "; } // add function body funcStr << ") " << getString(); @@ -1157,7 +1157,7 @@ void CScriptVar::getJSON(ostringstream &destination, const string linePrefix) { string indentedLinePrefix = linePrefix+" "; // children - handle with bracketed list destination << "{ \n"; - SCRIPTVAR_CHILDS::iterator last_it = --(Childs.end()); + int count = Childs.size(); for(SCRIPTVAR_CHILDS::iterator it = Childs.begin(); it != Childs.end(); ++it) { destination << indentedLinePrefix; if (isAlphaNum(it->first)) @@ -1166,9 +1166,7 @@ void CScriptVar::getJSON(ostringstream &destination, const string linePrefix) { destination << getJSString(it->first); destination << " : "; it->second->var->getJSON(destination, indentedLinePrefix); - if (it != last_it) { - destination << ",\n"; - } + if (--count) destination << ",\n"; } destination << "\n" << linePrefix << "}"; } else if (isArray()) { @@ -1373,11 +1371,7 @@ CScriptVarLink *CTinyJS::parseFunctionDefinition() { funcVar->var->data = l->getSubString(funcBegin); return funcVar; } -string int2string(int intData) { - char buffer[32]; - sprintf_s(buffer, sizeof(buffer), "%ld", intData); - return buffer; -} + CScriptVarLink *CTinyJS::factor(bool &execute) { if (l->tk==LEX_R_TRUE) { l->match(LEX_R_TRUE); @@ -1418,148 +1412,148 @@ CScriptVarLink *CTinyJS::factor(bool &execute) { l->match(LEX_ID); while (l->tk=='(' || l->tk=='.' || l->tk=='[') { if (l->tk=='(') { // ------------------------------------- Function Call - if (execute) { - if (!a->var->isFunction()) { - string errorMsg = "Expecting '"; - errorMsg = errorMsg + a->name + "' to be a function"; - throw new CScriptException(errorMsg.c_str()); - } - l->match('('); - // create a new symbol table entry for execution of this function - CScriptVar *functionRoot = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_FUNCTION); - if (parent) - functionRoot->addChildNoDup("this", parent); - else - functionRoot->addChildNoDup("this", new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT)); // always add a this-Object - // grab in all parameters - CScriptVar *parameters = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT); - int parameters_idx = 0; - for(SCRIPTVAR_CHILDS::iterator it = a->var->Childs.begin(); it != a->var->Childs.end(); ++it) { - if (l->tk!=')') { - CScriptVarLink *value = assignment(execute); - if (execute) { - if (value->var->isBasic()) { - // pass by value - functionRoot->addChild( it->first, parameters->addChild(int2string(parameters_idx++) , value->var->deepCopy())->var); + if (execute) { + if (!a->var->isFunction()) { + string errorMsg = "Expecting '"; + errorMsg = errorMsg + a->name + "' to be a function"; + throw new CScriptException(errorMsg.c_str()); + } + l->match('('); + // create a new symbol table entry for execution of this function + CScriptVar *functionRoot = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_FUNCTION); + if (parent) + functionRoot->addChildNoDup("this", parent); + else + functionRoot->addChildNoDup("this", new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT)); // always add a this-Object + // grab in all parameters + CScriptVar *parameters = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT); + int parameters_idx = 0; + for(SCRIPTVAR_CHILDS::iterator it = a->var->Childs.begin(); it != a->var->Childs.end(); ++it) { + if (l->tk!=')') { + CScriptVarLink *value = assignment(execute); + if (execute) { + if (value->var->isBasic()) { + // pass by value + functionRoot->addChild( it->first, parameters->addChild(int2string(parameters_idx++) , value->var->deepCopy())->var); + } else { + // pass by reference + functionRoot->addChild(it->first, parameters->addChild(int2string(parameters_idx++), value->var)->var); + } + } + CLEAN(value); } else { - // pass by reference - functionRoot->addChild(it->first, parameters->addChild(int2string(parameters_idx++), value->var->deepCopy())->var); + functionRoot->addChild(it->first, new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_UNDEFINED)); } + if (l->tk!=')') l->match(','); } - CLEAN(value); - } else { - functionRoot->addChild(it->first, new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_UNDEFINED)); - } - if (l->tk!=')') l->match(','); - } - while(l->tk!=')') { - CScriptVarLink *value = assignment(execute); - if (execute) { - if (value->var->isBasic()) { - // pass by value - parameters->addChild(int2string(parameters_idx++) , value->var->deepCopy()); - } else { - // pass by reference - parameters->addChild(int2string(parameters_idx++), value->var->deepCopy()); + while(l->tk!=')') { + CScriptVarLink *value = assignment(execute); + if (execute) { + if (value->var->isBasic()) { + // pass by value + parameters->addChild(int2string(parameters_idx++) , value->var->deepCopy()); + } else { + // pass by reference + parameters->addChild(int2string(parameters_idx++), value->var); + } + } + CLEAN(value); + if (l->tk!=')') l->match(','); } - } - CLEAN(value); - if (l->tk!=')') l->match(','); - } - l->match(')'); - parameters->addChild("length", new CScriptVar(parameters_idx)); - functionRoot->addChild("parameters", parameters); - // setup a return variable - CScriptVarLink *returnVar = NULL; - // execute function! - // add the function's execute space to the symbol table so we can recurse - CScriptVarLink *returnVarLink = functionRoot->addChild(TINYJS_RETURN_VAR); - scopes.push_back(functionRoot); - - if (a->var->isNative()) { - ASSERT(a->var->jsCallback); - if (a->var->isNative_ClassMemberFnc()) - (*a->var->jsCallbackClass)(functionRoot, a->var->jsCallbackUserData); - else - a->var->jsCallback(functionRoot, a->var->jsCallbackUserData); - } else { - /* we just want to execute the block, but something could - * have messed up and left us with the wrong ScriptLex, so - * we want to be careful here... */ - CScriptException *exception = 0; - CScriptLex *oldLex = l; - CScriptLex *newLex = new CScriptLex(a->var->getString()); - l = newLex; - try { - block(execute); - // because return will probably have called this, and set execute to false - execute = true; - } catch (CScriptException *e) { - exception = e; - } - delete newLex; - l = oldLex; + l->match(')'); + parameters->addChild("length", new CScriptVar(parameters_idx)); + functionRoot->addChild("parameters", parameters); + // setup a return variable + CScriptVarLink *returnVar = NULL; + // execute function! + // add the function's execute space to the symbol table so we can recurse + CScriptVarLink *returnVarLink = functionRoot->addChild(TINYJS_RETURN_VAR); + scopes.push_back(functionRoot); + + if (a->var->isNative()) { + ASSERT(a->var->jsCallback); + if (a->var->isNative_ClassMemberFnc()) + (*a->var->jsCallbackClass)(functionRoot, a->var->jsCallbackUserData); + else + a->var->jsCallback(functionRoot, a->var->jsCallbackUserData); + } else { + /* we just want to execute the block, but something could + * have messed up and left us with the wrong ScriptLex, so + * we want to be careful here... */ + CScriptException *exception = 0; + CScriptLex *oldLex = l; + CScriptLex *newLex = new CScriptLex(a->var->getString()); + l = newLex; + try { + block(execute); + // because return will probably have called this, and set execute to false + execute = true; + } catch (CScriptException *e) { + exception = e; + } + delete newLex; + l = oldLex; - if (exception) - throw exception; - } + if (exception) + throw exception; + } - scopes.pop_back(); - /* get the real return var before we remove it from our function */ - returnVar = new CScriptVarLink(returnVarLink->var); - functionRoot->removeLink(returnVarLink); - delete functionRoot; - if (returnVar) - a = returnVar; - else - a = new CScriptVarLink(new CScriptVar()); - } else { - // function, but not executing - just parse args and be done - l->match('('); - while (l->tk != ')') { - CScriptVarLink *value = base(execute); - CLEAN(value); - if (l->tk!=')') l->match(','); - } - l->match(')'); - if (l->tk == '{') { - block(execute); - } - } + scopes.pop_back(); + /* get the real return var before we remove it from our function */ + returnVar = new CScriptVarLink(returnVarLink->var); + functionRoot->removeLink(returnVarLink); + delete functionRoot; + if (returnVar) + a = returnVar; + else + a = new CScriptVarLink(new CScriptVar()); + } else { + // function, but not executing - just parse args and be done + l->match('('); + while (l->tk != ')') { + CScriptVarLink *value = base(execute); + CLEAN(value); + if (l->tk!=')') l->match(','); + } + l->match(')'); + if (l->tk == '{') { + block(execute); + } + } } else if (l->tk == '.') { // ------------------------------------- Record Access - l->match('.'); - if (execute) { - const string &name = l->tkStr; - CScriptVarLink *child = a->var->findChild(name); - if (!child) child = findInParentClasses(a->var, name); - if (!child) { - /* if we haven't found this defined yet, use the built-in - 'length' properly */ - if (a->var->isArray() && name == "length") { - int l = a->var->getArrayLength(); - child = new CScriptVarLink(new CScriptVar(l)); - } else if (a->var->isString() && name == "length") { - int l = a->var->getString().size(); - child = new CScriptVarLink(new CScriptVar(l)); - } else { - child = a->var->addChild(name); - } - } - parent = a->var; - a = child; - } - l->match(LEX_ID); + l->match('.'); + if (execute) { + const string &name = l->tkStr; + CScriptVarLink *child = a->var->findChild(name); + if (!child) child = findInParentClasses(a->var, name); + if (!child) { + /* if we haven't found this defined yet, use the built-in + 'length' properly */ + if (a->var->isArray() && name == "length") { + int l = a->var->getArrayLength(); + child = new CScriptVarLink(new CScriptVar(l)); + } else if (a->var->isString() && name == "length") { + int l = a->var->getString().size(); + child = new CScriptVarLink(new CScriptVar(l)); + } else { + child = a->var->addChild(name); + } + } + parent = a->var; + a = child; + } + l->match(LEX_ID); } else if (l->tk == '[') { // ------------------------------------- Array Access - l->match('['); - CScriptVarLink *index = expression(execute); - l->match(']'); - if (execute) { - CScriptVarLink *child = a->var->findChildOrCreate(index->var->getString()); - parent = a->var; - a = child; - } - CLEAN(index); + l->match('['); + CScriptVarLink *index = expression(execute); + l->match(']'); + if (execute) { + CScriptVarLink *child = a->var->findChildOrCreate(index->var->getString()); + parent = a->var; + a = child; + } + CLEAN(index); } else ASSERT(0); } return a; @@ -1604,11 +1598,8 @@ CScriptVarLink *CTinyJS::factor(bool &execute) { int idx = 0; while (l->tk != ']') { if (execute) { - char idx_str[16]; // big enough for 2^32 - sprintf_s(idx_str, sizeof(idx_str), "%d",idx); - CScriptVarLink *a = base(execute); - contents->addChild(idx_str, a->var); + contents->addChild(int2string(idx), a->var); CLEAN(a); } // no need to clean here, as it will definitely be used diff --git a/TinyJS.h b/TinyJS.h index c35f424..1757cc3 100755 --- a/TinyJS.h +++ b/TinyJS.h @@ -85,38 +85,39 @@ enum LEX_TYPES { }; enum SCRIPTVAR_FLAGS { - SCRIPTVAR_UNDEFINED = 0, - SCRIPTVAR_FUNCTION = 1, - SCRIPTVAR_OBJECT = 2, - SCRIPTVAR_ARRAY = 4, - SCRIPTVAR_DOUBLE = 8, // floating point double - SCRIPTVAR_INTEGER = 16, // integer number - SCRIPTVAR_BOOLEAN = 32, // boolean - SCRIPTVAR_STRING = 64, // string - SCRIPTVAR_NULL = 128, // it seems null is its own data type - - SCRIPTVAR_NATIVE_FNC = 256, // to specify this is a native function - SCRIPTVAR_NATIVE_MFNC = 512, // to specify this is a native function from class->memberFunc - SCRIPTVAR_NUMERICMASK = SCRIPTVAR_NULL | - SCRIPTVAR_DOUBLE | - SCRIPTVAR_INTEGER | - SCRIPTVAR_BOOLEAN, - SCRIPTVAR_VARTYPEMASK = SCRIPTVAR_DOUBLE | - SCRIPTVAR_INTEGER | - SCRIPTVAR_BOOLEAN | - SCRIPTVAR_STRING | - SCRIPTVAR_FUNCTION | - SCRIPTVAR_OBJECT | - SCRIPTVAR_ARRAY | - SCRIPTVAR_NULL, - SCRIPTVAR_NATIVE = SCRIPTVAR_NATIVE_FNC | - SCRIPTVAR_NATIVE_MFNC, + SCRIPTVAR_UNDEFINED = 0, + SCRIPTVAR_FUNCTION = 1, + SCRIPTVAR_OBJECT = 2, + SCRIPTVAR_ARRAY = 4, + SCRIPTVAR_DOUBLE = 8, // floating point double + SCRIPTVAR_INTEGER = 16, // integer number + SCRIPTVAR_BOOLEAN = 32, // boolean + SCRIPTVAR_STRING = 64, // string + SCRIPTVAR_NULL = 128, // it seems null is its own data type + + SCRIPTVAR_NATIVE_FNC = 256, // to specify this is a native function + SCRIPTVAR_NATIVE_MFNC = 512, // to specify this is a native function from class->memberFunc + SCRIPTVAR_NUMERICMASK = SCRIPTVAR_NULL | + SCRIPTVAR_DOUBLE | + SCRIPTVAR_INTEGER | + SCRIPTVAR_BOOLEAN, + SCRIPTVAR_VARTYPEMASK = SCRIPTVAR_DOUBLE | + SCRIPTVAR_INTEGER | + SCRIPTVAR_BOOLEAN | + SCRIPTVAR_STRING | + SCRIPTVAR_FUNCTION | + SCRIPTVAR_OBJECT | + SCRIPTVAR_ARRAY | + SCRIPTVAR_NULL, + SCRIPTVAR_NATIVE = SCRIPTVAR_NATIVE_FNC | + SCRIPTVAR_NATIVE_MFNC, }; enum RUNTIME_FLAGS { - RUNTIME_CANBREAK = 1, - RUNTIME_BREAK = 2, - RUNTIME_CANCONTINUE = 4, - RUNTIME_CONTINUE = 8, + RUNTIME_CANBREAK = 1, + RUNTIME_BREAK = 2, + RUNTIME_CANCONTINUE = 4, + RUNTIME_CONTINUE = 8, + RUNTIME_NEW = 16, }; #define RUNTIME_LOOP_MASK (RUNTIME_CANBREAK | RUNTIME_BREAK | RUNTIME_CANCONTINUE | RUNTIME_CONTINUE) From 63f4985bf3ac07aac1930016f937a3ea864983a8 Mon Sep 17 00:00:00 2001 From: armin Date: Sun, 12 Sep 2010 16:11:14 +0000 Subject: [PATCH 06/43] fixed: memoryleaks after throwing an error added: VarType 'NaN' added: VarType 'Infinity' added: math-ops with strings e.g.: "1.5e1"==15 --> true "12"/1 --> 12 "xyz"-12 --> NaN fexed: check right hand side print(a); --> throws an error a=x+9; --> throws an error but: a={}; print(a.x) --> prints 'undefined' (no error) Note: a.x is not created but: a={}; print(a.x.z) --> throws an error 'a.x is not defined' fixed: vars can only be created with the assignment-operator '=' or with the 'var'-statement a+=12; --> throws an error added: try{}catch(){}finally{} and throw fixed: a few other things --- Script.cpp | 2 + TinyJS.cpp | 772 +++++++++++++++++++++++++++++++++-------------------- TinyJS.h | 34 ++- 3 files changed, 510 insertions(+), 298 deletions(-) diff --git a/Script.cpp b/Script.cpp index ff43d00..44c99b6 100755 --- a/Script.cpp +++ b/Script.cpp @@ -81,6 +81,7 @@ int main(int UNUSED(argc), char **UNUSED(argv)) js->execute("print(\"Interactive mode... Type quit(); to exit, or print(...); to print something, or dump() to dump the symbol table!\");"); } catch (CScriptException *e) { printf("ERROR: %s\n", e->text.c_str()); + delete e; } while (js->evaluate("lets_quit") == "0") { @@ -90,6 +91,7 @@ int main(int UNUSED(argc), char **UNUSED(argv)) js->execute(buffer); } catch (CScriptException *e) { printf("ERROR: %s\n", e->text.c_str()); + delete e; } } delete js; diff --git a/TinyJS.cpp b/TinyJS.cpp index 178f89e..936b18f 100755 --- a/TinyJS.cpp +++ b/TinyJS.cpp @@ -132,18 +132,21 @@ # define ASSERT(X) assert(X) #endif /* Frees the given link IF it isn't owned by anything else */ -#define CLEAN(x) do{ CScriptVarLink *__v = x; if (__v && !__v->owned) { delete __v; } }while(0) +#define CLEAN(x) do{ CScriptVarLink *__v = x; if (__v && !__v->owned) { delete __v;} }while(0) /* Create a LINK to point to VAR and free the old link. * BUT this is more clever - it tries to keep the old link if it's not owned to save allocations */ -#define CREATE_LINK(LINK, VAR) { if (!LINK || LINK->owned) LINK = new CScriptVarLink(VAR); else LINK->replaceWith(VAR); } +#define CREATE_LINK(LINK, VAR) do{ if (!LINK || LINK->owned) LINK = new CScriptVarLink(VAR); else {LINK->replaceWith(VAR);LINK->name=TINYJS_TEMP_NAME;} }while(0) using namespace std; #ifdef __GNUC__ -#define vsprintf_s vsnprintf -#define sprintf_s snprintf -#define _strdup strdup +# define vsprintf_s vsnprintf +# define vsnprintf_s(buffer, size, len, format, args) vsnprintf(buffer, size, format, args) +# define sprintf_s snprintf +# define _strdup strdup +#else +# define __attribute__(x) #endif // ----------------------------------------------------------------------------------- Memory Debug @@ -277,8 +280,9 @@ bool isAlphaNum(const std::string &str) { // ----------------------------------------------------------------------------------- CSCRIPTEXCEPTION -CScriptException::CScriptException(const string &exceptionText) { +CScriptException::CScriptException(const string &exceptionText, int Pos) { text = exceptionText; + pos = Pos; } // ----------------------------------------------------------------------------------- CSCRIPTLEX @@ -374,6 +378,10 @@ string CScriptLex::getTokenStr(int token) { case LEX_R_NULL : return "null"; case LEX_R_UNDEFINED : return "undefined"; case LEX_R_NEW : return "new"; + case LEX_R_TRY : return "try"; + case LEX_R_CATCH : return "catch"; + case LEX_R_FINALLY : return "finally"; + case LEX_R_THROW : return "throw"; } ostringstream msg; @@ -433,7 +441,12 @@ void CScriptLex::getNextToken() { else if (tkStr=="null") tk = LEX_R_NULL; else if (tkStr=="undefined") tk = LEX_R_UNDEFINED; else if (tkStr=="new") tk = LEX_R_NEW; - } else if (isNumeric(currCh)) { // Numbers + else if (tkStr=="try") tk = LEX_R_TRY; + else if (tkStr=="catch") tk = LEX_R_CATCH; + else if (tkStr=="finally") tk = LEX_R_FINALLY; + else if (tkStr=="throw") tk = LEX_R_THROW; + } else if (isNumeric(currCh) || (currCh=='.' && isNumeric(nextCh))) { // Numbers + if(currCh=='.') tkStr+='0'; bool isHex = false; if (currCh=='0') { tkStr += currCh; getNextCh(); } if (currCh=='x') { @@ -885,24 +898,27 @@ int CScriptVar::getInt() { // if (isNull()) return 0; // if (isUndefined()) return 0; if (isDouble()) return (int)doubleData; + if (isString()) return strtol(data.c_str(),0,0); return 0; } bool CScriptVar::getBool() { if (isInt() || isBool()) return intData!=0; - if (isNull()) return 0; - if (isUndefined()) return 0; +// if (isNull()) return 0; +// if (isUndefined()) return 0; if (isDouble()) return doubleData!=0; if (isString()) return data.length()!=0; - return 0; + if (isInfinity()) return true; + return false; } double CScriptVar::getDouble() { if (isDouble()) return doubleData; if (isInt() || isBool()) intData; - if (isNull()) return 0; - if (isUndefined()) return 0; - return 0; /* or NaN? */ +// if (isNull()) return 0.0; +// if (isUndefined()) return 0.0; + if (isString()) return strtod(data.c_str(),0); + return 0.0; /* or NaN? */ } const string &CScriptVar::getString() { @@ -910,9 +926,11 @@ const string &CScriptVar::getString() { * I should really just use char* :) */ static string s_null = "null"; static string s_undefined = "undefined"; + static string s_NaN = "NaN"; + static string s_Infinity = "Infinity"; static string s_true = "true"; static string s_false = "false"; - if (isBool()) return getInt() ? s_true : s_false; + if (isBool()) return getBool() ? s_true : s_false; if (isInt()) { return int2string(intData, data); } @@ -923,46 +941,42 @@ const string &CScriptVar::getString() { return data; } if (isNull()) return s_null; + if (isNaN()) return s_NaN; + if (isInfinity()) return s_Infinity; if (isUndefined()) return s_undefined; // are we just a string here? return data; } void CScriptVar::setInt(int val) { + init(); flags = (flags&~SCRIPTVAR_VARTYPEMASK) | SCRIPTVAR_INTEGER; intData = val; - doubleData = 0; - data = TINYJS_BLANK_DATA; } void CScriptVar::setBool(bool val) { + init(); flags = (flags&~SCRIPTVAR_VARTYPEMASK) | SCRIPTVAR_BOOLEAN; intData = val?1:0; - doubleData = 0; - data = TINYJS_BLANK_DATA; } void CScriptVar::setDouble(double val) { + init(); flags = (flags&~SCRIPTVAR_VARTYPEMASK) | SCRIPTVAR_DOUBLE; doubleData = val; - intData = 0; - data = TINYJS_BLANK_DATA; } void CScriptVar::setString(const string &str) { // name sure it's not still a number or integer + init(); flags = (flags&~SCRIPTVAR_VARTYPEMASK) | SCRIPTVAR_STRING; data = str; - intData = 0; - doubleData = 0; } void CScriptVar::setUndefined() { // name sure it's not still a number or integer + init(); flags = (flags&~SCRIPTVAR_VARTYPEMASK) | SCRIPTVAR_UNDEFINED; - data = TINYJS_BLANK_DATA; - intData = 0; - doubleData = 0; removeAllChildren(); } @@ -974,27 +988,45 @@ CScriptVar *DoMaths(CScriptVar *a, CScriptVar *b, int op) T da = (T)(a->isDouble()?a->getDouble():dai); T db = (T)(b->isDouble()?b->getDouble():dbi); switch (op) { - case '+': return new CScriptVar(da+db); - case '-': return new CScriptVar(da-db); - case '*': return new CScriptVar(da*db); - case '/': return new CScriptVar(da/db); - case '&': return new CScriptVar(dai&dbi); - case '|': return new CScriptVar(dai|dbi); - case '^': return new CScriptVar(dai^dbi); - case '%': return new CScriptVar(dai%dbi); - case '~': return new CScriptVar(~dai); - case LEX_LSHIFT: return new CScriptVar(dai<>dbi); - case LEX_EQUAL: return new CScriptVar(da==db); - case LEX_NEQUAL: return new CScriptVar(da!=db); - case '<': return new CScriptVar(da': return new CScriptVar(da>db); - case LEX_GEQUAL: return new CScriptVar(da>=db); + case '+': return new CScriptVar(da+db); + case '-': return new CScriptVar(da-db); + case '*': return new CScriptVar(da*db); + case '/': + if(db==0) + if(da==0) return new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_NAN); // 0/0 = NaN + else return new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_INFINITY); // /0 = Infinity + else return new CScriptVar(da/db); + case '&': return new CScriptVar(dai&dbi); + case '|': return new CScriptVar(dai|dbi); + case '^': return new CScriptVar(dai^dbi); + case '%': return new CScriptVar(dai%dbi); + case '~': return new CScriptVar(~dai); + case LEX_LSHIFT: return new CScriptVar(dai<>dbi); + case LEX_EQUAL: return new CScriptVar(da==db); + case LEX_NEQUAL: return new CScriptVar(da!=db); + case '<': return new CScriptVar(da': return new CScriptVar(da>db); + case LEX_GEQUAL: return new CScriptVar(da>=db); default: throw new CScriptException("This operation not supported on the int datatype"); } } - +static CScriptVar *String2NumericVar(string &numeric) { + double d; + char *endptr;//=NULL; + int i = strtol(numeric.c_str(),&endptr,10); + if(*endptr == 'x' || *endptr == 'X') + i = strtol(numeric.c_str(),&endptr,16); + if(*endptr == '\0') + return new CScriptVar(i); + if(*endptr=='.' || *endptr=='e' || *endptr=='E') { + d = strtod(numeric.c_str(),&endptr); + if(*endptr == '\0') + return new CScriptVar(d); + } + return NULL; +} CScriptVar *CScriptVar::mathsOp(CScriptVar *b, int op) { CScriptVar *a = this; // TODO Equality checks on classes/structures @@ -1022,35 +1054,66 @@ CScriptVar *CScriptVar::mathsOp(CScriptVar *b, int op) { } else { // use doubles return DoMaths(a, b, op); - } - + } + } else if((a->isInfinity() || b->isInfinity()) && !a->isString() && !b->isString()) { + switch (op) { + case LEX_EQUAL: + case LEX_GEQUAL: + case LEX_LEQUAL: return new CScriptVar(a->isInfinity() == b->isInfinity()); + case LEX_NEQUAL: return new CScriptVar(a->isInfinity() != b->isInfinity()); + case '<': + case '>': return new CScriptVar(false); + default: return new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_INFINITY); + } + } else if((a->isNaN() || b->isNaN()) && !a->isString() && !b->isString()) { + switch (op) { + case LEX_NEQUAL: return new CScriptVar(true); + case LEX_EQUAL: + case LEX_GEQUAL: + case LEX_LEQUAL: + case '<': + case '>': return new CScriptVar(false); + default: return new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_NAN); + } } else if (a->isArray()) { /* Just check pointers */ switch (op) { - case LEX_EQUAL: return new CScriptVar(a==b); - case LEX_NEQUAL: return new CScriptVar(a!=b); - default: throw new CScriptException("This operation not supported on the Array datatype"); + case LEX_EQUAL: return new CScriptVar(a==b); + case LEX_NEQUAL: return new CScriptVar(a!=b); + default: return new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_NAN); } } else if (a->isObject()) { /* Just check pointers */ switch (op) { - case LEX_EQUAL: return new CScriptVar(a==b); - case LEX_NEQUAL: return new CScriptVar(a!=b); - default: throw new CScriptException("This operation not supported on the Object datatype"); + case LEX_EQUAL: return new CScriptVar(a==b); + case LEX_NEQUAL: return new CScriptVar(a!=b); + default: return new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_NAN); } } else { string da = a->getString(); string db = b->getString(); + if((a->isNumeric() || b->isNumeric()) && op != '+') + { + CScriptVar *ret = NULL; + CScriptVar *a_num = String2NumericVar(da); + CScriptVar *b_num = String2NumericVar(db); + if(a_num && b_num) + ret = a_num->mathsOp(b_num, op); + if(a_num) delete a_num; + if(b_num) delete b_num; + if(ret) + return ret; + } // use strings switch (op) { - case '+': return new CScriptVar(da+db, SCRIPTVAR_STRING); - case LEX_EQUAL: return new CScriptVar(da==db); - case LEX_NEQUAL: return new CScriptVar(da!=db); - case '<': return new CScriptVar(da': return new CScriptVar(da>db); - case LEX_GEQUAL: return new CScriptVar(da>=db); - default: throw new CScriptException("This operation not supported on the string datatype"); + case '+': return new CScriptVar(da+db, SCRIPTVAR_STRING); + case LEX_EQUAL: return new CScriptVar(da==db); + case LEX_NEQUAL: return new CScriptVar(da!=db); + case '<': return new CScriptVar(da': return new CScriptVar(da>db); + case LEX_GEQUAL: return new CScriptVar(da>=db); + default: return new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_NAN); } } ASSERT(0); @@ -1229,6 +1292,8 @@ CTinyJS::CTinyJS() { root->addChild("String", stringClass); root->addChild("Array", arrayClass); root->addChild("Object", objectClass); + tempScope = NULL; + exeption = NULL; addNative("function eval(jsCode)", this, &CTinyJS::scEval); // execute the given string and return the resul } @@ -1245,6 +1310,16 @@ CTinyJS::~CTinyJS() { #endif } +bool CTinyJS::throwError(bool &execute, const string &message) { + if(execute && (runtimeFlags & RUNTIME_CANTHROW)) { + this->exeption = new CScriptVarLink(new CScriptVar(message)); + runtimeFlags |= RUNTIME_THROW; + execute = false; + return true; + } + return false; +} + void CTinyJS::trace() { root->trace(); } @@ -1260,7 +1335,8 @@ void CTinyJS::execute(const string &code) { while (l->tk) statement(execute); } catch (CScriptException *e) { ostringstream msg; - msg << "Error " << e->text << " at " << l->getPosition(); + msg << "Error " << e->text << " at " << l->getPosition(e->pos); + delete e; delete l; l = oldLex; throw new CScriptException(msg.str()); @@ -1287,7 +1363,8 @@ CScriptVarLink CTinyJS::evaluateComplex(const string &code) { } while (l->tk!=LEX_EOF); } catch (CScriptException *e) { ostringstream msg; - msg << "Error " << e->text << " at " << l->getPosition(); + msg << "Error " << e->text << " at " << l->getPosition(e->pos); + delete e; delete l; l = oldLex; throw new CScriptException(msg.str()); @@ -1374,23 +1451,23 @@ CScriptVarLink *CTinyJS::parseFunctionDefinition() { CScriptVarLink *CTinyJS::factor(bool &execute) { if (l->tk==LEX_R_TRUE) { - l->match(LEX_R_TRUE); - return new CScriptVarLink(new CScriptVar(true)); + l->match(LEX_R_TRUE); + return new CScriptVarLink(new CScriptVar(true)); } if (l->tk==LEX_R_FALSE) { - l->match(LEX_R_FALSE); - return new CScriptVarLink(new CScriptVar(false)); + l->match(LEX_R_FALSE); + return new CScriptVarLink(new CScriptVar(false)); } if (l->tk==LEX_R_NULL) { - l->match(LEX_R_NULL); - return new CScriptVarLink(new CScriptVar(TINYJS_BLANK_DATA,SCRIPTVAR_NULL)); + l->match(LEX_R_NULL); + return new CScriptVarLink(new CScriptVar(TINYJS_BLANK_DATA,SCRIPTVAR_NULL)); } if (l->tk==LEX_R_UNDEFINED) { - l->match(LEX_R_UNDEFINED); - return new CScriptVarLink(new CScriptVar(TINYJS_BLANK_DATA,SCRIPTVAR_UNDEFINED)); + l->match(LEX_R_UNDEFINED); + return new CScriptVarLink(new CScriptVar(TINYJS_BLANK_DATA,SCRIPTVAR_UNDEFINED)); } if (l->tk==LEX_ID || l->tk=='(') { - CScriptVarLink *a; + CScriptVarLink *a; int op = l->tk; if(l->tk=='(') { @@ -1400,72 +1477,102 @@ CScriptVarLink *CTinyJS::factor(bool &execute) { } else a = execute ? findInScopes(l->tkStr) : new CScriptVarLink(new CScriptVar()); - /* The parent if we're executing a method call */ - CScriptVar *parent = 0; + /* The parent if we're executing a method call */ + CScriptVar *parent = 0; - if (execute && !a) { - /* Variable doesn't exist! JavaScript says we should create it - * (we won't add it here. This is done in the assignment operator)*/ - a = new CScriptVarLink(new CScriptVar(), l->tkStr); - } + if (execute && !a) { + /* Variable doesn't exist! JavaScript says we should create it + * (we won't add it here. This is done in the assignment operator)*/ + a = new CScriptVarLink(new CScriptVar(), l->tkStr); + } + string path = a->name; if(op==LEX_ID) l->match(LEX_ID); - while (l->tk=='(' || l->tk=='.' || l->tk=='[') { - if (l->tk=='(') { // ------------------------------------- Function Call - if (execute) { - if (!a->var->isFunction()) { - string errorMsg = "Expecting '"; - errorMsg = errorMsg + a->name + "' to be a function"; - throw new CScriptException(errorMsg.c_str()); - } - l->match('('); - // create a new symbol table entry for execution of this function - CScriptVar *functionRoot = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_FUNCTION); - if (parent) - functionRoot->addChildNoDup("this", parent); - else - functionRoot->addChildNoDup("this", new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT)); // always add a this-Object - // grab in all parameters - CScriptVar *parameters = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT); - int parameters_idx = 0; - for(SCRIPTVAR_CHILDS::iterator it = a->var->Childs.begin(); it != a->var->Childs.end(); ++it) { - if (l->tk!=')') { - CScriptVarLink *value = assignment(execute); - if (execute) { - if (value->var->isBasic()) { - // pass by value - functionRoot->addChild( it->first, parameters->addChild(int2string(parameters_idx++) , value->var->deepCopy())->var); - } else { - // pass by reference - functionRoot->addChild(it->first, parameters->addChild(int2string(parameters_idx++), value->var)->var); - } - } - CLEAN(value); - } else { - functionRoot->addChild(it->first, new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_UNDEFINED)); + while (l->tk=='(' || l->tk=='.' || l->tk=='[') { + if(execute && a->var->isUndefined()) { + CLEAN(tempScope); tempScope=NULL; + string message = path + " is undefined"; + if(!throwError(execute, message)) { + CLEAN(a); + throw new CScriptException(message, l->tokenEnd); + } + } + if (l->tk=='(') { // ------------------------------------- Function Call + if (execute) { + if (!a->var->isFunction()) { + CLEAN(tempScope); tempScope=NULL; + string errorMsg = "Expecting '"; + errorMsg = errorMsg + a->name + "' to be a function"; + throw new CScriptException(errorMsg.c_str()); + } + l->match('('); path += '('; + // create a new symbol table entry for execution of this function + CScriptVar *functionRoot = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_FUNCTION); + if (parent) + functionRoot->addChildNoDup("this", parent); + else + functionRoot->addChildNoDup("this", new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT)); // always add a this-Object + // grab in all parameters + CScriptVar *parameters = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT); + int parameters_idx = 0; + for(SCRIPTVAR_CHILDS::iterator it = a->var->Childs.begin(); it != a->var->Childs.end(); ++it) { + if (l->tk!=')') { + CScriptVarLink *value; + try { + value = assignment(execute); + path += value->var->getString(); + } catch (CScriptException *e) { + CLEAN(tempScope); tempScope=NULL; + delete parameters; + delete functionRoot; + throw e; } - if (l->tk!=')') l->match(','); - } - while(l->tk!=')') { - CScriptVarLink *value = assignment(execute); if (execute) { if (value->var->isBasic()) { // pass by value - parameters->addChild(int2string(parameters_idx++) , value->var->deepCopy()); + functionRoot->addChild( it->first, parameters->addChild(int2string(parameters_idx++) , value->var->deepCopy())->var); } else { // pass by reference - parameters->addChild(int2string(parameters_idx++), value->var); + functionRoot->addChild(it->first, parameters->addChild(int2string(parameters_idx++), value->var)->var); } } CLEAN(value); - if (l->tk!=')') l->match(','); + } else { + functionRoot->addChild(it->first, new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_UNDEFINED)); + } + if (l->tk!=')') { l->match(','); path+=','; } + } + while(l->tk!=')') { + CScriptVarLink *value; + try { + value = assignment(execute); + path += value->var->getString(); + } catch (CScriptException *e) { + CLEAN(tempScope); tempScope=NULL; + delete parameters; + delete functionRoot; + throw e; } + if (execute) { + if (value->var->isBasic()) { + // pass by value + parameters->addChild(int2string(parameters_idx++) , value->var->deepCopy()); + } else { + // pass by reference + parameters->addChild(int2string(parameters_idx++), value->var); + } + } + CLEAN(value); + if (l->tk!=')') { l->match(','); path+=','; } + } + + l->match(')'); path+=')'; - l->match(')'); - parameters->addChild("length", new CScriptVar(parameters_idx)); - functionRoot->addChild("parameters", parameters); - // setup a return variable - CScriptVarLink *returnVar = NULL; + parameters->addChild("length", new CScriptVar(parameters_idx)); + functionRoot->addChild("parameters", parameters); + // setup a return variable + CScriptVarLink *returnVar = NULL; + if(execute) { // execute function! // add the function's execute space to the symbol table so we can recurse CScriptVarLink *returnVarLink = functionRoot->addChild(TINYJS_RETURN_VAR); @@ -1503,111 +1610,119 @@ CScriptVarLink *CTinyJS::factor(bool &execute) { /* get the real return var before we remove it from our function */ returnVar = new CScriptVarLink(returnVarLink->var); functionRoot->removeLink(returnVarLink); - delete functionRoot; - if (returnVar) - a = returnVar; - else - a = new CScriptVarLink(new CScriptVar()); - } else { - // function, but not executing - just parse args and be done - l->match('('); - while (l->tk != ')') { - CScriptVarLink *value = base(execute); - CLEAN(value); - if (l->tk!=')') l->match(','); - } - l->match(')'); - if (l->tk == '{') { - block(execute); - } } - } else if (l->tk == '.') { // ------------------------------------- Record Access - l->match('.'); - if (execute) { - const string &name = l->tkStr; - CScriptVarLink *child = a->var->findChild(name); - if (!child) child = findInParentClasses(a->var, name); - if (!child) { - /* if we haven't found this defined yet, use the built-in - 'length' properly */ - if (a->var->isArray() && name == "length") { - int l = a->var->getArrayLength(); - child = new CScriptVarLink(new CScriptVar(l)); - } else if (a->var->isString() && name == "length") { - int l = a->var->getString().size(); - child = new CScriptVarLink(new CScriptVar(l)); - } else { - child = a->var->addChild(name); - } - } - parent = a->var; - a = child; + delete functionRoot; + if (returnVar) + a = returnVar; + else + a = new CScriptVarLink(new CScriptVar()); + } else { + // function, but not executing - just parse args and be done + l->match('('); + while (l->tk != ')') { + CScriptVarLink *value = base(execute); + CLEAN(value); + if (l->tk!=')') l->match(','); } + l->match(')'); + if (l->tk == '{') { + block(execute); + } + } + } else if (l->tk == '.' || l->tk == '[') { // ------------------------------------- Record Access + string name; + if(l->tk == '.') { + l->match('.'); + name = l->tkStr; + path += "."+name; l->match(LEX_ID); - } else if (l->tk == '[') { // ------------------------------------- Array Access + } else { l->match('['); CScriptVarLink *index = expression(execute); + name = index->var->getString(); + path += "["+name+"]"; + CLEAN(index); l->match(']'); - if (execute) { - CScriptVarLink *child = a->var->findChildOrCreate(index->var->getString()); + } + + if (execute) { + CScriptVarLink *child = a->var->findChild(name); + if (!child) child = findInParentClasses(a->var, name); + if (!child) { + /* if we haven't found this defined yet, use the built-in + 'length' properly */ + if (a->var->isArray() && name == "length") { + int l = a->var->getArrayLength(); + child = new CScriptVarLink(new CScriptVar(l)); + } else if (a->var->isString() && name == "length") { + int l = a->var->getString().size(); + child = new CScriptVarLink(new CScriptVar(l)); + } else { + //child = a->var->addChild(name); + } + } + if(child) { parent = a->var; a = child; + } else { + tempScope = a; + a = new CScriptVarLink(new CScriptVar(), name); } - CLEAN(index); - } else ASSERT(0); - } - return a; + } + } else ASSERT(0); + } + return a; } if (l->tk==LEX_INT || l->tk==LEX_FLOAT) { - CScriptVar *a = new CScriptVar(l->tkStr, - ((l->tk==LEX_INT)?SCRIPTVAR_INTEGER:SCRIPTVAR_DOUBLE)); - l->match(l->tk); - return new CScriptVarLink(a); + CScriptVar *a = new CScriptVar(l->tkStr, + ((l->tk==LEX_INT)?SCRIPTVAR_INTEGER:SCRIPTVAR_DOUBLE)); + l->match(l->tk); + return new CScriptVarLink(a); } if (l->tk==LEX_STR) { - CScriptVar *a = new CScriptVar(l->tkStr, SCRIPTVAR_STRING); - l->match(LEX_STR); - return new CScriptVarLink(a); + CScriptVar *a = new CScriptVar(l->tkStr, SCRIPTVAR_STRING); + l->match(LEX_STR); + return new CScriptVarLink(a); } if (l->tk=='{') { - CScriptVar *contents = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT); - /* JSON-style object definition */ - l->match('{'); - while (l->tk != '}') { - string id = l->tkStr; - // we only allow strings or IDs on the left hand side of an initialisation - if (l->tk==LEX_STR) l->match(LEX_STR); - else l->match(LEX_ID); - l->match(':'); - if (execute) { - CScriptVarLink *a = base(execute); + CScriptVar *contents = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT); + /* JSON-style object definition */ + l->match('{'); + while (l->tk != '}') { + string id = l->tkStr; + // we only allow strings or IDs on the left hand side of an initialisation + if (l->tk==LEX_STR) l->match(LEX_STR); + else l->match(LEX_ID); + l->match(':'); + if (execute) { + CScriptVarLink *a = assignment(execute); contents->addChild(id, a->var); CLEAN(a); - } - // no need to clean here, as it will definitely be used - if (l->tk != '}') l->match(','); } + // no need to clean here, as it will definitely be used + if (l->tk != '}') l->match(','); + } - l->match('}'); - return new CScriptVarLink(contents); + l->match('}'); + return new CScriptVarLink(contents); } if (l->tk=='[') { - CScriptVar *contents = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_ARRAY); - /* JSON-style array */ - l->match('['); - int idx = 0; - while (l->tk != ']') { - if (execute) { - CScriptVarLink *a = base(execute); - contents->addChild(int2string(idx), a->var); - CLEAN(a); - } - // no need to clean here, as it will definitely be used - if (l->tk != ']') l->match(','); - idx++; + CScriptVar *contents = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_ARRAY); + /* JSON-style array */ + l->match('['); + int idx = 0; + while (l->tk != ']') { + if (execute) { + CScriptVarLink *a = assignment(execute); + contents->addChild(int2string(idx), a->var); + CLEAN(a); } - l->match(']'); - return new CScriptVarLink(contents); + // no need to clean here, as it will definitely be used + if (l->tk != ']') l->match(','); + idx++; + } + l->match(']'); + return new CScriptVarLink(contents); } if (l->tk==LEX_R_FUNCTION) { CScriptVarLink *funcVar = parseFunctionDefinition(); @@ -1650,40 +1765,35 @@ CScriptVarLink *CTinyJS::factor(bool &execute) { CScriptVarLink *CTinyJS::unary(bool &execute) { CScriptVarLink *a; if (l->tk=='-') { - l->match('-'); - a = unary(execute); - if (execute) { - CScriptVar zero(0); - CScriptVar *res = zero.mathsOp(a->var, '-'); - CREATE_LINK(a, res); - } - } else - if (l->tk=='+') { - l->match('+'); - a = unary(execute); - } else - if (l->tk=='!') { - l->match('!'); // binary not - a = unary(execute); - if (execute) { - CScriptVar zero(0); - CScriptVar *res = a->var->mathsOp(&zero, LEX_EQUAL); - CREATE_LINK(a, res); - } - } else - if (l->tk=='~') { - l->match('~'); // binary neg - a = unary(execute); - if (execute) { - CScriptVar *res = a->var->mathsOp(NULL, '~'); - CREATE_LINK(a, res); - } - } else - // pre increment/decrement - if (l->tk==LEX_PLUSPLUS || l->tk==LEX_MINUSMINUS) { + l->match('-'); + a = unary(execute); + if (execute) { + CScriptVar zero(0); + CScriptVar *res = zero.mathsOp(a->var, '-'); + CREATE_LINK(a, res); + } + } else if (l->tk=='+') { + l->match('+'); + a = unary(execute); + } else if (l->tk=='!') { + l->match('!'); // binary not + a = unary(execute); + if (execute) { + CScriptVar zero(0); + CScriptVar *res = a->var->mathsOp(&zero, LEX_EQUAL); + CREATE_LINK(a, res); + } + } else if (l->tk=='~') { + l->match('~'); // binary neg + a = unary(execute); + if (execute) { + CScriptVar *res = a->var->mathsOp(NULL, '~'); + CREATE_LINK(a, res); + } + } else if (l->tk==LEX_PLUSPLUS || l->tk==LEX_MINUSMINUS) { int op = l->tk; - l->match(l->tk); - a = factor(execute); + l->match(l->tk); // pre increment/decrement + a = factor(execute); if (execute) { if(a->owned) { @@ -1696,13 +1806,13 @@ CScriptVarLink *CTinyJS::unary(bool &execute) { } } else - a = factor(execute); + a = factor(execute); // post increment/decrement if (l->tk==LEX_PLUSPLUS || l->tk==LEX_MINUSMINUS) { int op = l->tk; - l->match(l->tk); - if (execute) { + l->match(l->tk); + if (execute) { if(a->owned) { // TRACE("post-increment of %s and a is %sthe owner\n", a->name.c_str(), a->owned?"":"not "); @@ -1724,7 +1834,7 @@ CScriptVarLink *CTinyJS::unary(bool &execute) { // errorString << " at " << (l->getPosition(l->tokenStart-2)); throw new CScriptException(errorString.str()); } - } + } } @@ -1734,14 +1844,14 @@ CScriptVarLink *CTinyJS::unary(bool &execute) { CScriptVarLink *CTinyJS::term(bool &execute) { CScriptVarLink *a = unary(execute); while (l->tk=='*' || l->tk=='/' || l->tk=='%') { - int op = l->tk; - l->match(l->tk); - CScriptVarLink *b = unary(execute); - if (execute) { - CScriptVar *res = a->var->mathsOp(b->var, op); - CREATE_LINK(a, res); - } - CLEAN(b); + int op = l->tk; + l->match(l->tk); + CScriptVarLink *b = unary(execute); + if (execute) { + CScriptVar *res = a->var->mathsOp(b->var, op); + CREATE_LINK(a, res); + } + CLEAN(b); } return a; } @@ -1749,15 +1859,15 @@ CScriptVarLink *CTinyJS::term(bool &execute) { CScriptVarLink *CTinyJS::expression(bool &execute) { CScriptVarLink *a = term(execute); while (l->tk=='+' || l->tk=='-') { - int op = l->tk; - l->match(l->tk); - CScriptVarLink *b = term(execute); - if (execute) { - // not in-place, so just replace - CScriptVar *res = a->var->mathsOp(b->var, op); - CREATE_LINK(a, res); - } - CLEAN(b); + int op = l->tk; + l->match(l->tk); + CScriptVarLink *b = term(execute); + if (execute) { + // not in-place, so just replace + CScriptVar *res = a->var->mathsOp(b->var, op); + CREATE_LINK(a, res); + } + CLEAN(b); } return a; } @@ -1765,16 +1875,16 @@ CScriptVarLink *CTinyJS::expression(bool &execute) { CScriptVarLink *CTinyJS::binary_shift(bool &execute) { CScriptVarLink *a = expression(execute); while (l->tk==LEX_LSHIFT || l->tk==LEX_RSHIFT) { - int op = l->tk; - l->match(l->tk); + int op = l->tk; + l->match(l->tk); CScriptVarLink *b = expression(execute); - if (execute) { - // not in-place, so just replace - CScriptVar *res = a->var->mathsOp(b->var, op); - CREATE_LINK(a, res); - } - CLEAN(b); + if (execute) { + // not in-place, so just replace + CScriptVar *res = a->var->mathsOp(b->var, op); + CREATE_LINK(a, res); + } + CLEAN(b); } return a; } @@ -1873,26 +1983,40 @@ CScriptVarLink *CTinyJS::condition(bool &execute) } CScriptVarLink *CTinyJS::assignment(bool &execute) { + CLEAN(tempScope); tempScope=NULL; CScriptVarLink *lhs = condition(execute); if (l->tk=='=' || l->tk==LEX_PLUSEQUAL || l->tk==LEX_MINUSEQUAL || - l->tk==LEX_LSHIFTEQUAL || l->tk==LEX_RSHIFTEQUAL || - l->tk==LEX_ANDEQUAL || l->tk==LEX_OREQUAL || l->tk==LEX_XOREQUAL) { - /* If we're assigning to this and we don't have a parent, - * add it to the symbol table root as per JavaScript. */ - if (execute && !lhs->owned) { - if (lhs->name.length()>0) { - CScriptVarLink *realLhs = root->addChildNoDup(lhs->name, lhs->var); - CLEAN(lhs); - lhs = realLhs; - } else - TRACE("Trying to assign to an un-named type\n"); - } - + l->tk==LEX_LSHIFTEQUAL || l->tk==LEX_RSHIFTEQUAL || + l->tk==LEX_ANDEQUAL || l->tk==LEX_OREQUAL || l->tk==LEX_XOREQUAL) { int op = l->tk; + int pos = l->tokenEnd; l->match(l->tk); CScriptVarLink *rhs = assignment(execute); if (execute) { + if (!lhs->owned && lhs->name.length()==0) { + CLEAN(tempScope); tempScope=NULL; + CLEAN(lhs); + CLEAN(rhs); + throw new CScriptException("invalid assignment left-hand side", pos); + } else if (op != '=' && !lhs->owned) { + string message = lhs->name + " is not defined"; + if(!throwError(execute, message)) { + CLEAN(tempScope); tempScope=NULL; + CLEAN(lhs); + CLEAN(rhs); + throw new CScriptException(message, pos); + } + } if (op=='=') { + if (!lhs->owned) { + CScriptVarLink *realLhs; + if(tempScope) + realLhs = tempScope->var->addChildNoDup(lhs->name, lhs->var); + else + realLhs = root->addChildNoDup(lhs->name, lhs->var); + CLEAN(lhs); + lhs = realLhs; + } lhs->replaceWith(rhs); } else if (op==LEX_PLUSEQUAL) { CScriptVar *res = lhs->var->mathsOp(rhs->var, '+'); @@ -1919,6 +2043,14 @@ CScriptVarLink *CTinyJS::assignment(bool &execute) { } CLEAN(rhs); } + else if(lhs->name.length()>0 && !lhs->owned && tempScope==NULL) { + string message = lhs->name + " is not defined"; + if(!throwError(execute, message)) { + CLEAN(lhs); + throw new CScriptException(message); + } + } + CLEAN(tempScope); tempScope=NULL; return lhs; } CScriptVarLink *CTinyJS::base(bool &execute) { @@ -1949,7 +2081,6 @@ void CTinyJS::block(bool &execute) { l->match(l->tk); } } - } void CTinyJS::statement(bool &execute) { @@ -1979,18 +2110,10 @@ void CTinyJS::statement(bool &execute) { for(;;) { CScriptVarLink *a = 0; - if (execute) - a = scopes.back()->findChildOrCreate(l->tkStr); + string var = l->tkStr; l->match(LEX_ID); - // now do stuff defined with dots - while (l->tk == '.') { - l->match('.'); - if (execute) { - CScriptVarLink *lastA = a; - a = lastA->var->findChildOrCreate(l->tkStr); - } - l->match(LEX_ID); - } + if (execute) + a = scopes.back()->findChildOrCreate(var); // sort out initialiser if (l->tk == '=') { l->match('='); @@ -2004,7 +2127,7 @@ void CTinyJS::statement(bool &execute) { else break; } - l->match(';'); + l->match(';'); } else if (l->tk==LEX_R_IF) { l->match(LEX_R_IF); l->match('('); @@ -2291,6 +2414,69 @@ void CTinyJS::statement(bool &execute) { scopes.back()->addChildNoDup(funcVar->name, funcVar->var); } CLEAN(funcVar); + } else if (l->tk==LEX_R_TRY) { + l->match(LEX_R_TRY); + bool old_execute = execute; + // save runtimeFlags + int old_runtimeFlags = runtimeFlags & RUNTIME_THROW_MASK; + // set runtimeFlags + runtimeFlags = (runtimeFlags & ~RUNTIME_THROW_MASK) | RUNTIME_CANTHROW; + + block(execute); +//try{print("try1");try{ print("try2");try{print("try3");throw 3;}catch(a){print("catch3");throw 3;}finally{print("finalli3");}print("after3");}catch(a){print("catch2");}finally{print("finalli2");}print("after2");}catch(a){print("catch1");}finally{print("finalli1");}print("after1"); + bool isThrow = (runtimeFlags & RUNTIME_THROW) != 0; + if(isThrow) execute = old_execute; + + // restore runtimeFlags + runtimeFlags = (runtimeFlags & ~RUNTIME_THROW_MASK) | old_runtimeFlags; + + if(l->tk != LEX_R_FINALLY) // expect catch + { + l->match(LEX_R_CATCH); + l->match('('); + string exeption_var_name = l->tkStr; + l->match(LEX_ID); + l->match(')'); + if(isThrow) { + // string exeption_var_name = + CScriptVar *catchScope = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_FUNCTION); + catchScope->addChild(exeption_var_name, exeption->var); + CLEAN(exeption); + scopes.push_back(catchScope); + + block(execute); + + scopes.pop_back(); + delete catchScope; + } else { + bool noexecute = false; + this->block(noexecute); + } + } + if(l->tk == LEX_R_FINALLY) { + l->match(LEX_R_FINALLY); + bool finally_execute = isThrow ? true : execute; + block(finally_execute); + ; + } + } else if (l->tk==LEX_R_THROW) { + int tokenStart = l->tokenStart; + l->match(LEX_R_THROW); + CScriptVarLink *a = base(execute); + if(execute) { + if(runtimeFlags & RUNTIME_CANTHROW) { + runtimeFlags |= RUNTIME_THROW; + execute = false; + exeption = new CScriptVarLink(a->var); + } + else + { + string exeptionStr = a->var->getString(); + CLEAN(a); + throw new CScriptException("uncaught exeption: "+exeptionStr, tokenStart); + } + } + CLEAN(a); } else l->match(LEX_EOF); } diff --git a/TinyJS.h b/TinyJS.h index 1757cc3..ace7f83 100755 --- a/TinyJS.h +++ b/TinyJS.h @@ -30,6 +30,11 @@ #include #include +#ifndef __GNUC__ +# define __attribute__(x) +#endif + + #undef TRACE #ifndef TRACE #define TRACE printf @@ -80,6 +85,10 @@ enum LEX_TYPES { LEX_R_NULL, LEX_R_UNDEFINED, LEX_R_NEW, + LEX_R_TRY, + LEX_R_CATCH, + LEX_R_FINALLY, + LEX_R_THROW, LEX_R_LIST_END /* always the last entry */ }; @@ -94,9 +103,11 @@ enum SCRIPTVAR_FLAGS { SCRIPTVAR_BOOLEAN = 32, // boolean SCRIPTVAR_STRING = 64, // string SCRIPTVAR_NULL = 128, // it seems null is its own data type + SCRIPTVAR_INFINITY = 256, // it seems infinity is its own data type + SCRIPTVAR_NAN = 512, // it seems NaN is its own data type - SCRIPTVAR_NATIVE_FNC = 256, // to specify this is a native function - SCRIPTVAR_NATIVE_MFNC = 512, // to specify this is a native function from class->memberFunc + SCRIPTVAR_NATIVE_FNC = 1024, // to specify this is a native function + SCRIPTVAR_NATIVE_MFNC = 2048, // to specify this is a native function from class->memberFunc SCRIPTVAR_NUMERICMASK = SCRIPTVAR_NULL | SCRIPTVAR_DOUBLE | SCRIPTVAR_INTEGER | @@ -108,7 +119,10 @@ enum SCRIPTVAR_FLAGS { SCRIPTVAR_FUNCTION | SCRIPTVAR_OBJECT | SCRIPTVAR_ARRAY | - SCRIPTVAR_NULL, + SCRIPTVAR_NULL | + SCRIPTVAR_INFINITY | + SCRIPTVAR_NAN, + SCRIPTVAR_NATIVE = SCRIPTVAR_NATIVE_FNC | SCRIPTVAR_NATIVE_MFNC, }; @@ -118,9 +132,12 @@ enum RUNTIME_FLAGS { RUNTIME_CANCONTINUE = 4, RUNTIME_CONTINUE = 8, RUNTIME_NEW = 16, + RUNTIME_CANTHROW = 32, + RUNTIME_THROW = 64, }; #define RUNTIME_LOOP_MASK (RUNTIME_CANBREAK | RUNTIME_BREAK | RUNTIME_CANCONTINUE | RUNTIME_CONTINUE) +#define RUNTIME_THROW_MASK (RUNTIME_CANTHROW | RUNTIME_THROW) #define TINYJS_RETURN_VAR "return" #define TINYJS_PROTOTYPE_CLASS "prototype" @@ -133,7 +150,8 @@ std::string getJSString(const std::string &str); class CScriptException { public: std::string text; - CScriptException(const std::string &exceptionText); + int pos; + CScriptException(const std::string &exceptionText, int Pos=-1); }; class CScriptLex @@ -247,6 +265,8 @@ class CScriptVar bool isNative_ClassMemberFnc() { return (flags&SCRIPTVAR_NATIVE_MFNC)!=0; } bool isUndefined() { return (flags & SCRIPTVAR_VARTYPEMASK) == SCRIPTVAR_UNDEFINED; } bool isNull() { return (flags & SCRIPTVAR_NULL)!=0; } + bool isNaN() { return (flags&SCRIPTVAR_NAN)!=0; } + bool isInfinity() { return (flags&SCRIPTVAR_INFINITY)!=0; } bool isBasic() { return Childs.empty(); } ///< Is this *not* an array/object/etc CScriptVar *mathsOp(CScriptVar *b, int op); ///< do a maths op with another script variable @@ -359,6 +379,9 @@ class CTinyJS { /// Get the value of the given variable, or return 0 const std::string *getVariable(const std::string &path); + /// throws an Error + bool throwError(bool &execute, const std::string &message); + /// Send all variables to stdout void trace(); @@ -370,7 +393,8 @@ class CTinyJS { CScriptVar *stringClass; /// Built in string class CScriptVar *objectClass; /// Built in object class CScriptVar *arrayClass; /// Built in array class - + CScriptVarLink *tempScope; /// is temporary used by the '.' and '[' operator NULL meens the root-scope + CScriptVarLink *exeption; /// containing the exeption var by (runtimeFlags&RUNTIME_THROW) == true; // parsing - in order of precedence CScriptVarLink *factor(bool &execute); CScriptVarLink *unary(bool &execute); From ab3a765c54cc245e3ef3ce30fd735c1cff0a9e95 Mon Sep 17 00:00:00 2001 From: armin Date: Mon, 13 Sep 2010 14:05:10 +0000 Subject: [PATCH 07/43] added: for(... in ...) added: for each(... in ...) fixed: wrong runtimeFlags by break; and continue; when execute==false fixed: wrong runtimeFlags by throwing an error in loops --- TinyJS.cpp | 272 +++++++++++++++++++++++++++++++++-------------------- TinyJS.h | 2 +- 2 files changed, 173 insertions(+), 101 deletions(-) diff --git a/TinyJS.cpp b/TinyJS.cpp index 936b18f..f3227e8 100755 --- a/TinyJS.cpp +++ b/TinyJS.cpp @@ -309,8 +309,8 @@ CScriptLex::~CScriptLex(void) free((void*)data); } -void CScriptLex::reset() { - dataPos = dataStart; +void CScriptLex::reset(int toPos) { + dataPos = dataStart+toPos; tokenStart = 0; tokenEnd = 0; tokenLastEnd = 0; @@ -2147,7 +2147,7 @@ void CTinyJS::statement(bool &execute) { l->match(LEX_R_DO); bool loopCond = true; bool old_execute = execute; - int old_runtimeFlags = runtimeFlags; + int old_loop_runtimeFlags = runtimeFlags & RUNTIME_LOOP_MASK; runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | RUNTIME_CANBREAK | RUNTIME_CANCONTINUE; int whileBodyStart = l->tokenStart; int whileCondStart = 0; @@ -2166,7 +2166,7 @@ void CTinyJS::statement(bool &execute) { if(!whileBody) whileBody = l->getSubLex(whileBodyStart); if(old_execute && !execute) { - // break; + // break or continue if(runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE)) { execute = old_execute; @@ -2174,7 +2174,7 @@ void CTinyJS::statement(bool &execute) { loopCond = false; runtimeFlags &= ~(RUNTIME_BREAK | RUNTIME_CONTINUE); } - // return, break or continue; + // other stuff e.g return, throw } if(!whileCond) @@ -2199,16 +2199,16 @@ void CTinyJS::statement(bool &execute) { oldLex = l; } } - runtimeFlags = old_runtimeFlags; - l = oldLex; - delete whileCond; - delete whileBody; + runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | old_loop_runtimeFlags; + l = oldLex; + delete whileCond; + delete whileBody; - if (loopCount<=0) { - root->trace(); - TRACE("WHILE Loop exceeded %d iterations at %s\n", TINYJS_LOOP_MAX_ITERATIONS, l->getPosition(l->tokenLastEnd).c_str()); - throw new CScriptException("LOOP_ERROR"); - } + if (loopCount<=0) { + root->trace(); + TRACE("WHILE Loop exceeded %d iterations at %s\n", TINYJS_LOOP_MAX_ITERATIONS, l->getPosition(l->tokenLastEnd).c_str()); + throw new CScriptException("LOOP_ERROR"); + } } else if (l->tk==LEX_R_WHILE) { // We do repetition by pulling out the string representing our statement // there's definitely some opportunity for optimisation here @@ -2224,14 +2224,14 @@ void CTinyJS::statement(bool &execute) { int whileBodyStart = l->tokenStart; bool old_execute = execute; - int old_runtimeFlags = runtimeFlags; + int old_loop_runtimeFlags = runtimeFlags & RUNTIME_LOOP_MASK; runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | RUNTIME_CANBREAK | RUNTIME_CANCONTINUE; statement(loopCond ? execute : noexecute); CScriptLex *whileBody = l->getSubLex(whileBodyStart); CScriptLex *oldLex = l; if(loopCond && old_execute != execute) { - // break; + // break or continue if(runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE)) { execute = old_execute; @@ -2239,7 +2239,7 @@ void CTinyJS::statement(bool &execute) { loopCond = false; runtimeFlags &= ~(RUNTIME_BREAK | RUNTIME_CONTINUE); } - // return, break or continue; + // other stuff e.g return, throw } int loopCount = TINYJS_LOOP_MAX_ITERATIONS; @@ -2255,7 +2255,7 @@ void CTinyJS::statement(bool &execute) { statement(execute); if(!execute) { - // break; + // break or continue if(runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE)) { execute = old_execute; @@ -2263,11 +2263,11 @@ void CTinyJS::statement(bool &execute) { loopCond = false; runtimeFlags &= ~(RUNTIME_BREAK | RUNTIME_CONTINUE); } - // return, break or continue; + // other stuff e.g return, throw } } } - runtimeFlags = old_runtimeFlags; + runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | old_loop_runtimeFlags; l = oldLex; delete whileCond; delete whileBody; @@ -2279,104 +2279,176 @@ void CTinyJS::statement(bool &execute) { } } else if (l->tk==LEX_R_FOR) { l->match(LEX_R_FOR); + bool for_each_in; + int error_pos = -1; + if((for_each_in = l->tk == LEX_ID && l->tkStr == "each")) l->match(LEX_ID); l->match('('); - statement(execute); // initialisation - //l->match(';'); - bool noexecute = false; - int forCondStart = l->tokenStart; - bool cond_empty = true; - bool loopCond = true; // Empty Condition -->always true - CScriptVarLink *cond; - if(l->tk != ';') - { - cond_empty = false; - cond = base(execute); // condition - loopCond = execute && cond->var->getBool(); - CLEAN(cond); - } - CScriptLex *forCond = l->getSubLex(forCondStart); - l->match(';'); - bool iter_empty = true; - CScriptLex *forIter=NULL; - if(l->tk != ')') - { - iter_empty = false; - int forIterStart = l->tokenStart; - base(noexecute); // iterator - forIter = l->getSubLex(forIterStart); - } - l->match(')'); - int forBodyStart = l->tokenStart; - bool old_execute = execute; - int old_runtimeFlags = runtimeFlags; - runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | RUNTIME_CANBREAK | RUNTIME_CANCONTINUE; - statement(loopCond ? execute : noexecute); - CScriptLex *forBody = l->getSubLex(forBodyStart); - CScriptLex *oldLex = l; - if(loopCond && old_execute != execute) - { - // break; - if(runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE)) - { - execute = old_execute; - if(runtimeFlags & RUNTIME_BREAK) - loopCond = false; - runtimeFlags &= ~(RUNTIME_BREAK | RUNTIME_CONTINUE); + int pos=l->tokenStart; + bool for_in = false; + CScriptVarLink *for_var = NULL; + CScriptVarLink *for_var_tempScope = NULL; + CScriptVarLink *for_in_var = NULL; + if(l->tk == LEX_ID) { + for_var = factor(execute); + for_var_tempScope = tempScope; + +// l->match(LEX_ID); + if(l->tk == LEX_ID && l->tkStr == "in") { + for_in = true; + l->match(LEX_ID); + try { + for_in_var = factor(execute); + } catch (CScriptException *e) { + CLEAN(for_var); + throw e; + } + if (l->tk != ')') { + CLEAN(for_var); + CLEAN(for_in_var); + } + l->match(')'); + } else { + error_pos = l->tokenEnd; + l->reset(pos); } - // return, break or continue; - } - - if (loopCond && !iter_empty) { - forIter->reset(); - l = forIter; - base(execute); - } - int loopCount = TINYJS_LOOP_MAX_ITERATIONS; - while (execute && loopCond && loopCount-->0) { - if(!cond_empty) - { - forCond->reset(); - l = forCond; - cond = base(execute); - loopCond = cond->var->getBool(); - CLEAN(cond); + } + if(for_each_in && !for_in) + throw new CScriptException("invalid for each loop", error_pos); + else if (for_in) { + if (execute && !for_var->owned) { + CScriptVarLink *real_for_var; + if(tempScope) + real_for_var = tempScope->var->addChildNoDup(for_var->name, for_var->var); + else + real_for_var = root->addChildNoDup(for_var->name, for_var->var); + CLEAN(tempScope); tempScope=NULL; + CLEAN(for_var); + for_var = real_for_var; } - if (execute && loopCond) { - forBody->reset(); - l = forBody; + int forBodyStart = l->tokenStart; + bool old_execute = execute; + int old_loop_runtimeFlags = runtimeFlags & RUNTIME_LOOP_MASK; + runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | RUNTIME_CANBREAK | RUNTIME_CANCONTINUE; + for(SCRIPTVAR_CHILDS::iterator it = for_in_var->var->Childs.begin(); execute && it != for_in_var->var->Childs.end(); ++it) { + if (for_each_in) + for_var->replaceWith(it->second->var); + else + for_var->replaceWith(new CScriptVar(it->first)); + l->reset(forBodyStart); statement(execute); if(!execute) { - // break or continue; + // break or continue if(runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE)) { execute = old_execute; if(runtimeFlags & RUNTIME_BREAK) - loopCond = false; + break; runtimeFlags &= ~(RUNTIME_BREAK | RUNTIME_CONTINUE); } - // return nothing to do + // other stuff e.g return, throw } } - if (execute && loopCond && !iter_empty) { + runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | old_loop_runtimeFlags; + } else { + statement(execute); // initialisation + //l->match(';'); + bool noexecute = false; + int forCondStart = l->tokenStart; + bool cond_empty = true; + bool loopCond = true; // Empty Condition -->always true + CScriptVarLink *cond; + if(l->tk != ';') + { + cond_empty = false; + cond = base(execute); // condition + loopCond = execute && cond->var->getBool(); + CLEAN(cond); + } + CScriptLex *forCond = l->getSubLex(forCondStart); + l->match(';'); + bool iter_empty = true; + CScriptLex *forIter=NULL; + if(l->tk != ')') + { + iter_empty = false; + int forIterStart = l->tokenStart; + base(noexecute); // iterator + forIter = l->getSubLex(forIterStart); + } + l->match(')'); + int forBodyStart = l->tokenStart; + bool old_execute = execute; + int old_loop_runtimeFlags = runtimeFlags & RUNTIME_LOOP_MASK; + runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | RUNTIME_CANBREAK | RUNTIME_CANCONTINUE; + statement(loopCond ? execute : noexecute); + CScriptLex *forBody = l->getSubLex(forBodyStart); + CScriptLex *oldLex = l; + if(loopCond && old_execute != execute) + { + // break or continue + if(runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE)) + { + execute = old_execute; + if(runtimeFlags & RUNTIME_BREAK) + loopCond = false; + runtimeFlags &= ~(RUNTIME_BREAK | RUNTIME_CONTINUE); + } + // other stuff e.g return, throw + } + + if (loopCond && !iter_empty) { forIter->reset(); l = forIter; base(execute); } - } - runtimeFlags = old_runtimeFlags; - l = oldLex; - delete forCond; - delete forIter; - delete forBody; - if (loopCount<=0) { - root->trace(); - TRACE("FOR Loop exceeded %d iterations at %s\n", TINYJS_LOOP_MAX_ITERATIONS, l->getPosition(l->tokenLastEnd).c_str()); - throw new CScriptException("LOOP_ERROR"); + int loopCount = TINYJS_LOOP_MAX_ITERATIONS; + while (execute && loopCond && loopCount-->0) { + if(!cond_empty) + { + forCond->reset(); + l = forCond; + cond = base(execute); + loopCond = cond->var->getBool(); + CLEAN(cond); + } + if (execute && loopCond) { + forBody->reset(); + l = forBody; + statement(execute); + if(!execute) + { + // break or continue; + if(runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE)) + { + execute = old_execute; + if(runtimeFlags & RUNTIME_BREAK) + loopCond = false; + runtimeFlags &= ~(RUNTIME_BREAK | RUNTIME_CONTINUE); + } + // return nothing to do + } + } + if (execute && loopCond && !iter_empty) { + forIter->reset(); + l = forIter; + base(execute); + } + } + runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | old_loop_runtimeFlags; + l = oldLex; + delete forCond; + delete forIter; + delete forBody; + if (loopCount<=0) { + root->trace(); + TRACE("FOR Loop exceeded %d iterations at %s\n", TINYJS_LOOP_MAX_ITERATIONS, l->getPosition(l->tokenLastEnd).c_str()); + throw new CScriptException("LOOP_ERROR"); + } } } else if (l->tk==LEX_R_BREAK) { l->match(LEX_R_BREAK); - if(runtimeFlags & RUNTIME_CANBREAK) + if(execute && runtimeFlags & RUNTIME_CANBREAK) { runtimeFlags |= RUNTIME_BREAK; execute = false; @@ -2384,7 +2456,7 @@ void CTinyJS::statement(bool &execute) { l->match(';'); } else if (l->tk==LEX_R_CONTINUE) { l->match(LEX_R_CONTINUE); - if(runtimeFlags & RUNTIME_CANCONTINUE) + if(execute && runtimeFlags & RUNTIME_CANCONTINUE) { runtimeFlags |= RUNTIME_CONTINUE; execute = false; @@ -2418,7 +2490,7 @@ void CTinyJS::statement(bool &execute) { l->match(LEX_R_TRY); bool old_execute = execute; // save runtimeFlags - int old_runtimeFlags = runtimeFlags & RUNTIME_THROW_MASK; + int old_throw_runtimeFlags = runtimeFlags & RUNTIME_THROW_MASK; // set runtimeFlags runtimeFlags = (runtimeFlags & ~RUNTIME_THROW_MASK) | RUNTIME_CANTHROW; @@ -2428,7 +2500,7 @@ void CTinyJS::statement(bool &execute) { if(isThrow) execute = old_execute; // restore runtimeFlags - runtimeFlags = (runtimeFlags & ~RUNTIME_THROW_MASK) | old_runtimeFlags; + runtimeFlags = (runtimeFlags & ~RUNTIME_THROW_MASK) | old_throw_runtimeFlags; if(l->tk != LEX_R_FINALLY) // expect catch { diff --git a/TinyJS.h b/TinyJS.h index ace7f83..9a3c3c6 100755 --- a/TinyJS.h +++ b/TinyJS.h @@ -170,7 +170,7 @@ class CScriptLex void match(int expected_tk); ///< Lexical match wotsit static std::string getTokenStr(int token); ///< Get the string representation of the given token - void reset(); ///< Reset this lex so we can start again + void reset(int toPos=0); ///< Reset this lex so we can start again std::string getSubString(int pos); ///< Return a sub-string from the given position up until right now CScriptLex *getSubLex(int lastPosition); ///< Return a sub-lexer from the given position up until right now From 5a03a71e0275d99ff4ff271735a1a74bca8addd0 Mon Sep 17 00:00:00 2001 From: armin Date: Mon, 13 Sep 2010 21:13:09 +0000 Subject: [PATCH 08/43] added: literals for Infinity & NaN fixed: math Infinity/Infinity & Infinity-Infinity results in NaN fixed: math with undefined results every in NaN --- TinyJS.cpp | 43 ++++++++++++++++++++++++++----------------- TinyJS.h | 2 ++ 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/TinyJS.cpp b/TinyJS.cpp index f3227e8..ebe5462 100755 --- a/TinyJS.cpp +++ b/TinyJS.cpp @@ -377,6 +377,8 @@ string CScriptLex::getTokenStr(int token) { case LEX_R_FALSE : return "false"; case LEX_R_NULL : return "null"; case LEX_R_UNDEFINED : return "undefined"; + case LEX_R_INFINITY : return "Infinity"; + case LEX_R_NAN : return "NaN"; case LEX_R_NEW : return "new"; case LEX_R_TRY : return "try"; case LEX_R_CATCH : return "catch"; @@ -440,6 +442,8 @@ void CScriptLex::getNextToken() { else if (tkStr=="false") tk = LEX_R_FALSE; else if (tkStr=="null") tk = LEX_R_NULL; else if (tkStr=="undefined") tk = LEX_R_UNDEFINED; + else if (tkStr=="Infinity") tk = LEX_R_INFINITY; + else if (tkStr=="NaN") tk = LEX_R_NAN; else if (tkStr=="new") tk = LEX_R_NEW; else if (tkStr=="try") tk = LEX_R_TRY; else if (tkStr=="catch") tk = LEX_R_CATCH; @@ -1042,12 +1046,17 @@ CScriptVar *CScriptVar::mathsOp(CScriptVar *b, int op) { return new CScriptVar(!eql); } // do maths... - if (a->isUndefined() && b->isUndefined()) { - if (op == LEX_EQUAL) return new CScriptVar(true); - else if (op == LEX_NEQUAL) return new CScriptVar(false); - else return new CScriptVar(); // undefined - } else if ((a->isNumeric() || a->isUndefined()) && - (b->isNumeric() || b->isUndefined())) { + if((a->isNaN() || a->isUndefined() || b->isNaN() || b->isUndefined()) && !a->isString() && !b->isString()) { + switch (op) { + case LEX_NEQUAL: return new CScriptVar(!(a->isUndefined() && b->isUndefined())); + case LEX_EQUAL: return new CScriptVar((a->isUndefined() && b->isUndefined())); + case LEX_GEQUAL: + case LEX_LEQUAL: + case '<': + case '>': return new CScriptVar(false); + default: return new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_NAN); + } + } else if (a->isNumeric() && b->isNumeric()) { if (!a->isDouble() && !b->isDouble()) { // use ints return DoMaths(a, b, op); @@ -1063,18 +1072,10 @@ CScriptVar *CScriptVar::mathsOp(CScriptVar *b, int op) { case LEX_NEQUAL: return new CScriptVar(a->isInfinity() != b->isInfinity()); case '<': case '>': return new CScriptVar(false); + case '/': + case '-': if(a->isInfinity() && b->isInfinity()) return new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_NAN); default: return new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_INFINITY); } - } else if((a->isNaN() || b->isNaN()) && !a->isString() && !b->isString()) { - switch (op) { - case LEX_NEQUAL: return new CScriptVar(true); - case LEX_EQUAL: - case LEX_GEQUAL: - case LEX_LEQUAL: - case '<': - case '>': return new CScriptVar(false); - default: return new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_NAN); - } } else if (a->isArray()) { /* Just check pointers */ switch (op) { @@ -1089,7 +1090,7 @@ CScriptVar *CScriptVar::mathsOp(CScriptVar *b, int op) { case LEX_NEQUAL: return new CScriptVar(a!=b); default: return new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_NAN); } - } else { + } else { //Strings string da = a->getString(); string db = b->getString(); if((a->isNumeric() || b->isNumeric()) && op != '+') @@ -1466,6 +1467,14 @@ CScriptVarLink *CTinyJS::factor(bool &execute) { l->match(LEX_R_UNDEFINED); return new CScriptVarLink(new CScriptVar(TINYJS_BLANK_DATA,SCRIPTVAR_UNDEFINED)); } + if (l->tk==LEX_R_INFINITY) { + l->match(LEX_R_INFINITY); + return new CScriptVarLink(new CScriptVar(TINYJS_BLANK_DATA,SCRIPTVAR_INFINITY)); + } + if (l->tk==LEX_R_NAN) { + l->match(LEX_R_NAN); + return new CScriptVarLink(new CScriptVar(TINYJS_BLANK_DATA,SCRIPTVAR_NAN)); + } if (l->tk==LEX_ID || l->tk=='(') { CScriptVarLink *a; int op = l->tk; diff --git a/TinyJS.h b/TinyJS.h index 9a3c3c6..dd8a1de 100755 --- a/TinyJS.h +++ b/TinyJS.h @@ -84,6 +84,8 @@ enum LEX_TYPES { LEX_R_FALSE, LEX_R_NULL, LEX_R_UNDEFINED, + LEX_R_INFINITY, + LEX_R_NAN, LEX_R_NEW, LEX_R_TRY, LEX_R_CATCH, From bd943d9197bc3ab72b607e19de457d9c1692c75b Mon Sep 17 00:00:00 2001 From: armin Date: Mon, 13 Sep 2010 22:41:20 +0000 Subject: [PATCH 09/43] fixed: maths double with int fixed: compiling with GCC --- TinyJS.cpp | 10 +++++++++- TinyJS_Functions.cpp | 12 ++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/TinyJS.cpp b/TinyJS.cpp index ebe5462..d3e6d31 100755 --- a/TinyJS.cpp +++ b/TinyJS.cpp @@ -239,7 +239,15 @@ void replace(string &str, char textFrom, const char *textTo) { p = str.find(textFrom, p+sLen); } } +#ifdef __GNUC__ +string int2string(int intData) { + char buffer[32]; + sprintf_s(buffer, sizeof(buffer), "%d", intData); + return buffer; +} +#else string &int2string(int intData, string &inString=string()); +#endif string &int2string(int intData, string &inString) { char buffer[32]; sprintf_s(buffer, sizeof(buffer), "%d", intData); @@ -918,7 +926,7 @@ bool CScriptVar::getBool() { double CScriptVar::getDouble() { if (isDouble()) return doubleData; - if (isInt() || isBool()) intData; + if (isInt() || isBool()) return intData; // if (isNull()) return 0.0; // if (isUndefined()) return 0.0; if (isString()) return strtod(data.c_str(),0); diff --git a/TinyJS_Functions.cpp b/TinyJS_Functions.cpp index 4585268..7712d98 100755 --- a/TinyJS_Functions.cpp +++ b/TinyJS_Functions.cpp @@ -38,6 +38,18 @@ # endif #endif +#ifdef __GNUC__ +# define UNUSED(x) __attribute__((__unused__)) +#elif defined(_MSC_VER) +# ifndef UNUSED +# define UNUSED(x) x +# pragma warning( disable : 4100 ) /* unreferenced formal parameter */ +# endif +#else +# define UNUSED(x) x +#endif + + using namespace std; // ----------------------------------------------- Actual Functions From df13f50178ee82c972630e054a59c996406898c5 Mon Sep 17 00:00:00 2001 From: armin Date: Thu, 16 Sep 2010 20:30:54 +0000 Subject: [PATCH 10/43] fixed: full right-hand-check every throws an error when use a undefined Var fixed: wrong execute state when throw, exit, continue in a finally block added: operators *=, /=, %=, typeof, delete and void added: token for "in"-operator (NOTE:currently suported only by for...in ) added: token for "instaceof" (NOTE: instanceof is currently unsuported) changed: added a smart-pointer-class for CScripVarLink-pointers. The big thought for this smart-pointer is the replacement of the macro CLEAN(x), because this Pointer cleans itself (no more memoryleaks -;) ) per deconstructor or when assign a new CScripVarLink-pointer. Also the macro CREATE_LINK is replaced. A special operator (<<) is a shortcut for replaceWith(...). --- TinyJS.cpp | 737 ++++++++++++++++++++++++++++------------------------- TinyJS.h | 75 ++++-- 2 files changed, 450 insertions(+), 362 deletions(-) diff --git a/TinyJS.cpp b/TinyJS.cpp index d3e6d31..2dc5482 100755 --- a/TinyJS.cpp +++ b/TinyJS.cpp @@ -132,11 +132,61 @@ # define ASSERT(X) assert(X) #endif /* Frees the given link IF it isn't owned by anything else */ -#define CLEAN(x) do{ CScriptVarLink *__v = x; if (__v && !__v->owned) { delete __v;} }while(0) +//#define CLEAN(x) do{ CScriptVarLink *__v = x; if (__v && !__v->owned) { delete __v;} }while(0) /* Create a LINK to point to VAR and free the old link. * BUT this is more clever - it tries to keep the old link if it's not owned to save allocations */ -#define CREATE_LINK(LINK, VAR) do{ if (!LINK || LINK->owned) LINK = new CScriptVarLink(VAR); else {LINK->replaceWith(VAR);LINK->name=TINYJS_TEMP_NAME;} }while(0) +//#define CREATE_LINK(LINK, VAR) do{ if (!LINK || LINK->owned) LINK = new CScriptVarLink(VAR); else {LINK->replaceWith(VAR);LINK->name=TINYJS_TEMP_NAME;} }while(0) + +// corresponds "CScriptVarLink *link=0;" and "CScriptVarLink *link=Link" +inline CScriptVarSmartLink::CScriptVarSmartLink () : link(0){} +inline CScriptVarSmartLink::CScriptVarSmartLink (CScriptVarLink *Link) : link(Link) {} + +// this constructor replace "CScriptVarLink *link=0 ; CREATE_LINK(link, Var);" +inline CScriptVarSmartLink::CScriptVarSmartLink (CScriptVar *Var) { + *this = Var; +} +// this operator replace "CREATE_LINK(link, Var);" +inline CScriptVarSmartLink &CScriptVarSmartLink::operator = (CScriptVar *Var) { + if (!link || link->owned) + link = new CScriptVarLink(Var); + else { + link->replaceWith(Var); + link->name=TINYJS_TEMP_NAME; + } + return *this; +} +// the deconstructor replace the macro CLEAN(x) +inline CScriptVarSmartLink::~CScriptVarSmartLink () { + if(link && !link->owned) + delete link; +} +// the copy-stuff has a special thing - +// when copying a SmartLink to an other then the right hand side will lost your link +inline CScriptVarSmartLink::CScriptVarSmartLink (const CScriptVarSmartLink &Link) { + *this = Link; +} +inline CScriptVarSmartLink &CScriptVarSmartLink::operator = (const CScriptVarSmartLink &Link) { + link = Link.link; + ((CScriptVarSmartLink &)Link).link = 0; // explicit cast to a non const ref + return *this; +} + +// this operator corresponds "CLEAN(link); link = Link;" +inline CScriptVarSmartLink &CScriptVarSmartLink::operator = (CScriptVarLink *Link) { + if(link && !link->owned) delete link; + link = Link; + return *this; +} + +// this operator corresponds "link->replaceWith(Link->var)" +inline CScriptVarSmartLink &CScriptVarSmartLink::operator <<(CScriptVarSmartLink &Link) { +#if _DEBUG + ASSERT(link && Link.link); +#endif + link->replaceWith(Link->var); + return *this; +} using namespace std; @@ -370,12 +420,17 @@ string CScriptLex::getTokenStr(int token) { case LEX_OREQUAL : return "|="; case LEX_OROR : return "||"; case LEX_XOREQUAL : return "^="; + case LEX_ASTERISKEQUAL : return "*="; + case LEX_SLASHEQUAL : return "/="; + case LEX_PERCENTEQUAL : return "%="; + // reserved words case LEX_R_IF : return "if"; case LEX_R_ELSE : return "else"; case LEX_R_DO : return "do"; case LEX_R_WHILE : return "while"; case LEX_R_FOR : return "for"; + case LEX_R_IN: return "in"; case LEX_R_BREAK : return "break"; case LEX_R_CONTINUE : return "continue"; case LEX_R_FUNCTION : return "function"; @@ -392,6 +447,10 @@ string CScriptLex::getTokenStr(int token) { case LEX_R_CATCH : return "catch"; case LEX_R_FINALLY : return "finally"; case LEX_R_THROW : return "throw"; + case LEX_R_TYPEOF : return "typeof"; + case LEX_R_VOID : return "void"; + case LEX_R_DELETE : return "delete"; + case LEX_R_INSTANCEOF : return "instanceof"; } ostringstream msg; @@ -441,6 +500,7 @@ void CScriptLex::getNextToken() { else if (tkStr=="do") tk = LEX_R_DO; else if (tkStr=="while") tk = LEX_R_WHILE; else if (tkStr=="for") tk = LEX_R_FOR; + else if (tkStr=="in") tk = LEX_R_IN; else if (tkStr=="break") tk = LEX_R_BREAK; else if (tkStr=="continue") tk = LEX_R_CONTINUE; else if (tkStr=="function") tk = LEX_R_FUNCTION; @@ -457,6 +517,10 @@ void CScriptLex::getNextToken() { else if (tkStr=="catch") tk = LEX_R_CATCH; else if (tkStr=="finally") tk = LEX_R_FINALLY; else if (tkStr=="throw") tk = LEX_R_THROW; + else if (tkStr=="typeof") tk = LEX_R_TYPEOF; + else if (tkStr=="void") tk = LEX_R_VOID; + else if (tkStr=="delete") tk = LEX_R_DELETE; + else if (tkStr=="instanceof") tk = LEX_R_INSTANCEOF; } else if (isNumeric(currCh) || (currCh=='.' && isNumeric(nextCh))) { // Numbers if(currCh=='.') tkStr+='0'; bool isHex = false; @@ -544,52 +608,73 @@ void CScriptLex::getNextToken() { tk = LEX_NTYPEEQUAL; getNextCh(); } - } else if (tk=='<' && currCh=='=') { - tk = LEX_LEQUAL; - getNextCh(); - } else if (tk=='<' && currCh=='<') { - tk = LEX_LSHIFT; - getNextCh(); - if (currCh=='=') { // <<= - tk = LEX_LSHIFTEQUAL; + } else if (tk=='<') { + if (currCh=='=') { // <= + tk = LEX_LEQUAL; getNextCh(); + } else if (currCh=='<') { // << + tk = LEX_LSHIFT; + getNextCh(); + if (currCh=='=') { // <<= + tk = LEX_LSHIFTEQUAL; + getNextCh(); + } } - } else if (tk=='>' && currCh=='=') { - tk = LEX_GEQUAL; - getNextCh(); - } else if (tk=='>' && currCh=='>') { - tk = LEX_RSHIFT; - getNextCh(); - if (currCh=='=') { // <<= - tk = LEX_RSHIFTEQUAL; + } else if (tk=='>') { + if (currCh=='=') { // >= + tk = LEX_GEQUAL; getNextCh(); + } else if (currCh=='>') { // >> + tk = LEX_RSHIFT; + getNextCh(); + if (currCh=='=') { // >>= + tk = LEX_RSHIFTEQUAL; + getNextCh(); + } } - } else if (tk=='+' && currCh=='=') { - tk = LEX_PLUSEQUAL; - getNextCh(); - } else if (tk=='-' && currCh=='=') { - tk = LEX_MINUSEQUAL; - getNextCh(); - } else if (tk=='+' && currCh=='+') { - tk = LEX_PLUSPLUS; - getNextCh(); - } else if (tk=='-' && currCh=='-') { - tk = LEX_MINUSMINUS; - getNextCh(); - } else if (tk=='&' && currCh=='=') { - tk = LEX_ANDEQUAL; - getNextCh(); - } else if (tk=='&' && currCh=='&') { - tk = LEX_ANDAND; + } else if (tk=='+') { + if (currCh=='=') { // += + tk = LEX_PLUSEQUAL; + getNextCh(); + } else if (currCh=='+') { // ++ + tk = LEX_PLUSPLUS; + getNextCh(); + } + } else if (tk=='-') { + if (currCh=='=') { // -= + tk = LEX_MINUSEQUAL; + getNextCh(); + } else if (currCh=='-') { // -- + tk = LEX_MINUSMINUS; + getNextCh(); + } + } else if (tk=='&') { + if (currCh=='=') { // &= + tk = LEX_ANDEQUAL; + getNextCh(); + } else if (currCh=='&') { // && + tk = LEX_ANDAND; + getNextCh(); + } + } else if (tk=='|') { + if (currCh=='=') { // |= + tk = LEX_OREQUAL; + getNextCh(); + } else if (currCh=='|') { // || + tk = LEX_OROR; + getNextCh(); + } + } else if (tk=='^' && currCh=='=') { + tk = LEX_XOREQUAL; getNextCh(); - } else if (tk=='|' && currCh=='=') { - tk = LEX_OREQUAL; + } else if (tk=='*' && currCh=='=') { + tk = LEX_ASTERISKEQUAL; getNextCh(); - } else if (tk=='|' && currCh=='|') { - tk = LEX_OROR; + } else if (tk=='/' && currCh=='=') { + tk = LEX_SLASHEQUAL; getNextCh(); - } else if (tk=='^' && currCh=='=') { - tk = LEX_XOREQUAL; + } else if (tk=='%' && currCh=='=') { + tk = LEX_PERCENTEQUAL; getNextCh(); } } @@ -648,10 +733,9 @@ CScriptVarLink::CScriptVarLink(CScriptVar *var, const std::string &name) { mark_allocated(this); #endif this->name = name; - this->nextSibling = 0; - this->prevSibling = 0; this->var = var->ref(); - this->owned = NULL; + this->owned = 0; + this->dontDelete = false; } CScriptVarLink::CScriptVarLink(const CScriptVarLink &link) { @@ -660,10 +744,9 @@ CScriptVarLink::CScriptVarLink(const CScriptVarLink &link) { mark_allocated(this); #endif this->name = link.name; - this->nextSibling = 0; - this->prevSibling = 0; this->var = link.var->ref(); - this->owned = NULL; + this->owned = 0; + this->dontDelete = false; } CScriptVarLink::~CScriptVarLink() { @@ -842,24 +925,16 @@ CScriptVarLink *CScriptVar::addChildNoDup(const std::string &childName, CScriptV return v; } -void CScriptVar::removeChild(CScriptVar *child) { - for(SCRIPTVAR_CHILDS::iterator it = Childs.begin(); it != Childs.end(); ++it) { - if(it->second->var == child) { - removeLink(it->second); - break; - } - } - ASSERT(0);// removeChild - the child is not atached to this var -} - -void CScriptVar::removeLink(CScriptVarLink *link) { - if (!link) return; +bool CScriptVar::removeLink(CScriptVarLink *&link) { + if (!link) return false; #ifdef _DEBUG if(findChild(link->name) != link) ASSERT(0); // removeLink - the link is not atached to this var #endif Childs.erase(link->name); delete link; + link = 0; + return true; } void CScriptVar::removeAllChildren() { @@ -1223,6 +1298,19 @@ string CScriptVar::getParsableString() { return "null"; return "undefined"; } +string CScriptVar::getVarType() { + if(this->isBool()) + return "boolean"; + else if(this->isFunction()) + return "function"; + else if(this->isNumeric() || this->isInfinity() || this->isNaN()) + return "number"; + else if(this->isString()) + return "string"; + else if(this->isUndefined()) + return "undefined"; + return "object"; // Objcect / Array / null +} void CScriptVar::getJSON(ostringstream &destination, const string linePrefix) { if (isObject()) { @@ -1319,14 +1407,14 @@ CTinyJS::~CTinyJS() { #endif } -bool CTinyJS::throwError(bool &execute, const string &message) { +void CTinyJS::throwError(bool &execute, const string &message, int pos /*=-1*/) { if(execute && (runtimeFlags & RUNTIME_CANTHROW)) { - this->exeption = new CScriptVarLink(new CScriptVar(message)); + exeption = (new CScriptVar(message))->ref(); runtimeFlags |= RUNTIME_THROW; execute = false; - return true; + return; } - return false; + throw new CScriptException(message, pos); } void CTinyJS::trace() { @@ -1362,11 +1450,10 @@ CScriptVarLink CTinyJS::evaluateComplex(const string &code) { l = new CScriptLex(code); scopes.clear(); scopes.push_back(root); - CScriptVarLink *v = 0; + CScriptVarSmartLink v; try { bool execute = true; do { - CLEAN(v); v = base(execute); if (l->tk!=LEX_EOF) l->match(';'); } while (l->tk!=LEX_EOF); @@ -1384,7 +1471,6 @@ CScriptVarLink CTinyJS::evaluateComplex(const string &code) { if (v) { CScriptVarLink r = *v; - CLEAN(v); return r; } // return undefined... @@ -1457,8 +1543,9 @@ CScriptVarLink *CTinyJS::parseFunctionDefinition() { funcVar->var->data = l->getSubString(funcBegin); return funcVar; } - -CScriptVarLink *CTinyJS::factor(bool &execute) { +// Precedence 2, Precedence 1 & literals +CScriptVarSmartLink CTinyJS::factor(bool &execute) { + tempScope = 0; if (l->tk==LEX_R_TRUE) { l->match(LEX_R_TRUE); return new CScriptVarLink(new CScriptVar(true)); @@ -1484,7 +1571,7 @@ CScriptVarLink *CTinyJS::factor(bool &execute) { return new CScriptVarLink(new CScriptVar(TINYJS_BLANK_DATA,SCRIPTVAR_NAN)); } if (l->tk==LEX_ID || l->tk=='(') { - CScriptVarLink *a; + CScriptVarSmartLink a; int op = l->tk; if(l->tk=='(') { @@ -1507,17 +1594,11 @@ CScriptVarLink *CTinyJS::factor(bool &execute) { l->match(LEX_ID); while (l->tk=='(' || l->tk=='.' || l->tk=='[') { if(execute && a->var->isUndefined()) { - CLEAN(tempScope); tempScope=NULL; - string message = path + " is undefined"; - if(!throwError(execute, message)) { - CLEAN(a); - throw new CScriptException(message, l->tokenEnd); - } + throwError(execute, path + " is undefined", l->tokenEnd); } if (l->tk=='(') { // ------------------------------------- Function Call if (execute) { if (!a->var->isFunction()) { - CLEAN(tempScope); tempScope=NULL; string errorMsg = "Expecting '"; errorMsg = errorMsg + a->name + "' to be a function"; throw new CScriptException(errorMsg.c_str()); @@ -1534,12 +1615,11 @@ CScriptVarLink *CTinyJS::factor(bool &execute) { int parameters_idx = 0; for(SCRIPTVAR_CHILDS::iterator it = a->var->Childs.begin(); it != a->var->Childs.end(); ++it) { if (l->tk!=')') { - CScriptVarLink *value; + CScriptVarSmartLink value; try { value = assignment(execute); path += value->var->getString(); } catch (CScriptException *e) { - CLEAN(tempScope); tempScope=NULL; delete parameters; delete functionRoot; throw e; @@ -1553,19 +1633,17 @@ CScriptVarLink *CTinyJS::factor(bool &execute) { functionRoot->addChild(it->first, parameters->addChild(int2string(parameters_idx++), value->var)->var); } } - CLEAN(value); } else { functionRoot->addChild(it->first, new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_UNDEFINED)); } if (l->tk!=')') { l->match(','); path+=','; } } while(l->tk!=')') { - CScriptVarLink *value; + CScriptVarSmartLink value; try { value = assignment(execute); path += value->var->getString(); } catch (CScriptException *e) { - CLEAN(tempScope); tempScope=NULL; delete parameters; delete functionRoot; throw e; @@ -1579,7 +1657,6 @@ CScriptVarLink *CTinyJS::factor(bool &execute) { parameters->addChild(int2string(parameters_idx++), value->var); } } - CLEAN(value); if (l->tk!=')') { l->match(','); path+=','; } } @@ -1637,8 +1714,7 @@ CScriptVarLink *CTinyJS::factor(bool &execute) { // function, but not executing - just parse args and be done l->match('('); while (l->tk != ')') { - CScriptVarLink *value = base(execute); - CLEAN(value); + CScriptVarSmartLink value = base(execute); if (l->tk!=')') l->match(','); } l->match(')'); @@ -1655,10 +1731,9 @@ CScriptVarLink *CTinyJS::factor(bool &execute) { l->match(LEX_ID); } else { l->match('['); - CScriptVarLink *index = expression(execute); + CScriptVarSmartLink index = expression(execute); name = index->var->getString(); path += "["+name+"]"; - CLEAN(index); l->match(']'); } @@ -1682,7 +1757,7 @@ CScriptVarLink *CTinyJS::factor(bool &execute) { parent = a->var; a = child; } else { - tempScope = a; + tempScope = a->var; a = new CScriptVarLink(new CScriptVar(), name); } } @@ -1712,9 +1787,8 @@ CScriptVarLink *CTinyJS::factor(bool &execute) { else l->match(LEX_ID); l->match(':'); if (execute) { - CScriptVarLink *a = assignment(execute); + CScriptVarSmartLink a = assignment(execute); contents->addChild(id, a->var); - CLEAN(a); } // no need to clean here, as it will definitely be used if (l->tk != '}') l->match(','); @@ -1730,9 +1804,8 @@ CScriptVarLink *CTinyJS::factor(bool &execute) { int idx = 0; while (l->tk != ']') { if (execute) { - CScriptVarLink *a = assignment(execute); + CScriptVarSmartLink a = assignment(execute); contents->addChild(int2string(idx), a->var); - CLEAN(a); } // no need to clean here, as it will definitely be used if (l->tk != ']') l->match(','); @@ -1779,33 +1852,61 @@ CScriptVarLink *CTinyJS::factor(bool &execute) { l->match(LEX_EOF); return 0; } -CScriptVarLink *CTinyJS::unary(bool &execute) { - CScriptVarLink *a; +// R-L: Precedence 4 (TODO: add typeof, void, delete) & Precedence 3 +CScriptVarSmartLink CTinyJS::unary(bool &execute) { + CScriptVarSmartLink a; if (l->tk=='-') { l->match('-'); a = unary(execute); if (execute) { + CheckRightHandVar(execute, a); CScriptVar zero(0); - CScriptVar *res = zero.mathsOp(a->var, '-'); - CREATE_LINK(a, res); + a = zero.mathsOp(a->var, '-'); } } else if (l->tk=='+') { l->match('+'); a = unary(execute); + CheckRightHandVar(execute, a); } else if (l->tk=='!') { l->match('!'); // binary not a = unary(execute); if (execute) { + CheckRightHandVar(execute, a); CScriptVar zero(0); - CScriptVar *res = a->var->mathsOp(&zero, LEX_EQUAL); - CREATE_LINK(a, res); + a = a->var->mathsOp(&zero, LEX_EQUAL); } } else if (l->tk=='~') { l->match('~'); // binary neg a = unary(execute); if (execute) { - CScriptVar *res = a->var->mathsOp(NULL, '~'); - CREATE_LINK(a, res); + CheckRightHandVar(execute, a); + a = a->var->mathsOp(NULL, '~'); + } + } else if (l->tk==LEX_R_TYPEOF) { + l->match(LEX_R_TYPEOF); // void + a = unary(execute); + if (execute) { + // !!! no right-hand-check by delete + a = new CScriptVarLink(new CScriptVar(a->var->getVarType(),SCRIPTVAR_STRING)); + } + } else if (l->tk==LEX_R_VOID) { + l->match(LEX_R_VOID); // void + a = unary(execute); + if (execute) { + CheckRightHandVar(execute, a); + a = new CScriptVarLink(new CScriptVar(TINYJS_BLANK_DATA,SCRIPTVAR_UNDEFINED)); + } + } else if (l->tk==LEX_R_DELETE) { + l->match(LEX_R_DELETE); // delete + a = unary(execute); + if (execute) { + // !!! no right-hand-check by delete + if(a->owned && !a->dontDelete) { + a->owned->removeLink(a.getLink()); // removes the link from owner + a = new CScriptVarLink(new CScriptVar(true)); + } + else + a = new CScriptVarLink(new CScriptVar(false)); } } else if (l->tk==LEX_PLUSPLUS || l->tk==LEX_MINUSMINUS) { int op = l->tk; @@ -1818,13 +1919,12 @@ CScriptVarLink *CTinyJS::unary(bool &execute) { CScriptVar *res = a->var->mathsOp(&one, op==LEX_PLUSPLUS ? '+' : '-'); // in-place add/subtract a->replaceWith(res); - CREATE_LINK(a, res); + a = res; } } - } - else + } else { a = factor(execute); - + } // post increment/decrement if (l->tk==LEX_PLUSPLUS || l->tk==LEX_MINUSMINUS) { int op = l->tk; @@ -1837,247 +1937,231 @@ CScriptVarLink *CTinyJS::unary(bool &execute) { CScriptVar *res = a->var->ref(); CScriptVar *new_a = a->var->mathsOp(&one, op==LEX_PLUSPLUS ? '+' : '-'); a->replaceWith(new_a); - CREATE_LINK(a, res); + a = res; res->unref(); } - else - { - ostringstream errorString; - errorString << "Post-" << (l->tk==LEX_PLUSPLUS ? "Increment" : "Decrement") << ": "; - if(a->name.length()) - errorString << a->name << " is not defined"; - else - errorString << "invalid " << (l->tk==LEX_PLUSPLUS ? "increment" : "decrement") << " operand"; -// errorString << " at " << (l->getPosition(l->tokenStart-2)); - throw new CScriptException(errorString.str()); - } } } - - return a; } -CScriptVarLink *CTinyJS::term(bool &execute) { - CScriptVarLink *a = unary(execute); - while (l->tk=='*' || l->tk=='/' || l->tk=='%') { - int op = l->tk; - l->match(l->tk); - CScriptVarLink *b = unary(execute); - if (execute) { - CScriptVar *res = a->var->mathsOp(b->var, op); - CREATE_LINK(a, res); +// L->R: Precedence 5 +CScriptVarSmartLink CTinyJS::term(bool &execute) { + CScriptVarSmartLink a = unary(execute); + if (l->tk=='*' || l->tk=='/' || l->tk=='%') { + CheckRightHandVar(execute, a); + while (l->tk=='*' || l->tk=='/' || l->tk=='%') { + int op = l->tk; + l->match(l->tk); + CScriptVarSmartLink b = unary(execute); // L->R + if (execute) { + CheckRightHandVar(execute, b); + a = a->var->mathsOp(b->var, op); + } } - CLEAN(b); } return a; } -CScriptVarLink *CTinyJS::expression(bool &execute) { - CScriptVarLink *a = term(execute); - while (l->tk=='+' || l->tk=='-') { - int op = l->tk; - l->match(l->tk); - CScriptVarLink *b = term(execute); - if (execute) { - // not in-place, so just replace - CScriptVar *res = a->var->mathsOp(b->var, op); - CREATE_LINK(a, res); +// L->R: Precedence 6 +CScriptVarSmartLink CTinyJS::expression(bool &execute) { + CScriptVarSmartLink a = term(execute); + if (l->tk=='+' || l->tk=='-') { + CheckRightHandVar(execute, a); + while (l->tk=='+' || l->tk=='-') { + int op = l->tk; + l->match(l->tk); + CScriptVarSmartLink b = term(execute); // L->R + if (execute) { + CheckRightHandVar(execute, b); + // not in-place, so just replace + a = a->var->mathsOp(b->var, op); + } } - CLEAN(b); } return a; } -CScriptVarLink *CTinyJS::binary_shift(bool &execute) { - CScriptVarLink *a = expression(execute); - while (l->tk==LEX_LSHIFT || l->tk==LEX_RSHIFT) { - int op = l->tk; - l->match(l->tk); +// L->R: Precedence 7 (TODO: add >>>) +CScriptVarSmartLink CTinyJS::binary_shift(bool &execute) { + CScriptVarSmartLink a = expression(execute); + if (l->tk==LEX_LSHIFT || l->tk==LEX_RSHIFT) { + CheckRightHandVar(execute, a); + while (l->tk==LEX_LSHIFT || l->tk==LEX_RSHIFT) { + int op = l->tk; + l->match(l->tk); - CScriptVarLink *b = expression(execute); - if (execute) { - // not in-place, so just replace - CScriptVar *res = a->var->mathsOp(b->var, op); - CREATE_LINK(a, res); + CScriptVarSmartLink b = expression(execute); // L->R + if (execute) { + CheckRightHandVar(execute, a); + // not in-place, so just replace + a = a->var->mathsOp(b->var, op); + } } - CLEAN(b); } return a; } - -CScriptVarLink *CTinyJS::relation(bool &execute, int set, int set_n) { - CScriptVarLink *a = set_n ? relation(execute, set_n, 0) : binary_shift(execute); -// CScriptVarLink *a = expression(execute); - CScriptVarLink *b; - while ((set==LEX_EQUAL && (l->tk==LEX_EQUAL || l->tk==LEX_NEQUAL || l->tk==LEX_TYPEEQUAL || l->tk==LEX_NTYPEEQUAL)) +// L->R: Precedence 9 & Precedence 8 (TODO: add instanceof and in) +CScriptVarSmartLink CTinyJS::relation(bool &execute, int set, int set_n) { + CScriptVarSmartLink a = set_n ? relation(execute, set_n, 0) : binary_shift(execute); + if ((set==LEX_EQUAL && (l->tk==LEX_EQUAL || l->tk==LEX_NEQUAL || l->tk==LEX_TYPEEQUAL || l->tk==LEX_NTYPEEQUAL)) || (set=='<' && (l->tk==LEX_LEQUAL || l->tk==LEX_GEQUAL || l->tk=='<' || l->tk=='>'))) { - int op = l->tk; - l->match(l->tk); - b = set_n ? relation(execute, set_n, 0) : binary_shift(execute); - if (execute) { - CScriptVar *res = a->var->mathsOp(b->var, op); - CREATE_LINK(a,res); + CheckRightHandVar(execute, a); + while ((set==LEX_EQUAL && (l->tk==LEX_EQUAL || l->tk==LEX_NEQUAL || l->tk==LEX_TYPEEQUAL || l->tk==LEX_NTYPEEQUAL)) + || (set=='<' && (l->tk==LEX_LEQUAL || l->tk==LEX_GEQUAL || l->tk=='<' || l->tk=='>'))) { + int op = l->tk; + l->match(l->tk); + CScriptVarSmartLink b = set_n ? relation(execute, set_n, 0) : binary_shift(execute); // L->R + if (execute) { + CheckRightHandVar(execute, b); + a = a->var->mathsOp(b->var, op); + } } - CLEAN(b); } return a; } -CScriptVarLink *CTinyJS::logic_binary(bool &execute, int op, int op_n1, int op_n2) { - CScriptVarLink *a = op_n1 ? logic_binary(execute, op_n1, op_n2, 0) : relation(execute); - CScriptVarLink *b; - while (l->tk==op) { - l->match(l->tk); - b = op_n1 ? logic_binary(execute, op_n1, op_n2, 0) : relation(execute); - if (execute) { - CScriptVar *res = a->var->mathsOp(b->var, op); - CREATE_LINK(a, res); +// L->R: Precedence 12, Precedence 11 & Precedence 10 +CScriptVarSmartLink CTinyJS::logic_binary(bool &execute, int op, int op_n1, int op_n2) { + CScriptVarSmartLink a = op_n1 ? logic_binary(execute, op_n1, op_n2, 0) : relation(execute); + if (l->tk==op) { + CheckRightHandVar(execute, a); + while (l->tk==op) { + l->match(l->tk); + CScriptVarSmartLink b = op_n1 ? logic_binary(execute, op_n1, op_n2, 0) : relation(execute); // L->R + if (execute) { + CheckRightHandVar(execute, b); + a = a->var->mathsOp(b->var, op); + } } - CLEAN(b); } return a; } -CScriptVarLink *CTinyJS::logic(bool &execute, int op, int op_n) { - CScriptVarLink *a = op_n ? logic(execute, op_n, 0) : logic_binary(execute); - CScriptVarLink *b; - while (l->tk==op) { - bool noexecute = false; - int bop = l->tk; - l->match(l->tk); - bool shortCircuit = false; - bool boolean = false; - // if we have short-circuit ops, then if we know the outcome - // we don't bother to execute the other op. Even if not - // we need to tell mathsOp it's an & or | - if (op==LEX_ANDAND) { - bop = '&'; - shortCircuit = !a->var->getBool(); - boolean = true; - } else if (op==LEX_OROR) { - bop = '|'; - shortCircuit = a->var->getBool(); - boolean = true; - } - b = op_n ? logic(shortCircuit ? noexecute : execute, op_n, 0) : logic_binary(shortCircuit ? noexecute : execute); - if (execute && !shortCircuit) { - if (boolean) { - CScriptVar *newa = new CScriptVar(a->var->getBool()); - CScriptVar *newb = new CScriptVar(b->var->getBool()); - CREATE_LINK(a, newa); - CREATE_LINK(b, newb); +// L->R: Precedence 14 & Precedence 13 +CScriptVarSmartLink CTinyJS::logic(bool &execute, int op, int op_n) { + CScriptVarSmartLink a = op_n ? logic(execute, op_n, 0) : logic_binary(execute); + if (l->tk==op) { + CheckRightHandVar(execute, a); + while (l->tk==op) { + CheckRightHandVar(execute, a); + bool noexecute = false; + int bop = l->tk; + l->match(l->tk); + bool shortCircuit = false; + bool boolean = false; + // if we have short-circuit ops, then if we know the outcome + // we don't bother to execute the other op. Even if not + // we need to tell mathsOp it's an & or | + if (op==LEX_ANDAND) { + bop = '&'; + shortCircuit = !a->var->getBool(); + boolean = true; + } else if (op==LEX_OROR) { + bop = '|'; + shortCircuit = a->var->getBool(); + boolean = true; + } + CScriptVarSmartLink b = op_n ? logic(shortCircuit ? noexecute : execute, op_n, 0) : logic_binary(shortCircuit ? noexecute : execute); // L->R + CheckRightHandVar(execute, b); + if (execute && !shortCircuit) { + if (boolean) { + CheckRightHandVar(execute, b); + a = new CScriptVar(a->var->getBool()); + b = new CScriptVar(b->var->getBool()); + } + a = a->var->mathsOp(b->var, bop); } - CScriptVar *res = a->var->mathsOp(b->var, bop); - CREATE_LINK(a, res); } - CLEAN(b); } return a; } -CScriptVarLink *CTinyJS::condition(bool &execute) + +// R->L: Precedence 15 +CScriptVarSmartLink CTinyJS::condition(bool &execute) { - CScriptVarLink *a = logic(execute); + CScriptVarSmartLink a = logic(execute); if (l->tk=='?') { + CheckRightHandVar(execute, a); l->match(l->tk); bool cond = a->var->getBool(); - CLEAN(a); - CScriptVarLink *b; + CScriptVarSmartLink b; bool noexecute=false; - a = condition(cond ? execute : noexecute ); + a = condition(cond ? execute : noexecute ); // R->L + CheckRightHandVar(execute, a); l->match(':'); - b = condition(cond ? noexecute : execute); - if(cond) - CLEAN(b); - else - { - CLEAN(a); + b = condition(cond ? noexecute : execute); // R-L + CheckRightHandVar(execute, b); + if(!cond) a=b; - } } return a; } -CScriptVarLink *CTinyJS::assignment(bool &execute) { - CLEAN(tempScope); tempScope=NULL; - CScriptVarLink *lhs = condition(execute); +// R->L: Precedence 16 +CScriptVarSmartLink CTinyJS::assignment(bool &execute) { + CScriptVarSmartLink lhs = condition(execute); + CScriptVar *lhs_tempScope = tempScope; if (l->tk=='=' || l->tk==LEX_PLUSEQUAL || l->tk==LEX_MINUSEQUAL || + l->tk==LEX_ASTERISKEQUAL || l->tk==LEX_SLASHEQUAL || l->tk==LEX_PERCENTEQUAL || l->tk==LEX_LSHIFTEQUAL || l->tk==LEX_RSHIFTEQUAL || - l->tk==LEX_ANDEQUAL || l->tk==LEX_OREQUAL || l->tk==LEX_XOREQUAL) { + l->tk==LEX_ANDEQUAL || l->tk==LEX_XOREQUAL || l->tk==LEX_OREQUAL) { int op = l->tk; int pos = l->tokenEnd; l->match(l->tk); - CScriptVarLink *rhs = assignment(execute); + CScriptVarSmartLink rhs = assignment(execute); // R->L if (execute) { if (!lhs->owned && lhs->name.length()==0) { - CLEAN(tempScope); tempScope=NULL; - CLEAN(lhs); - CLEAN(rhs); throw new CScriptException("invalid assignment left-hand side", pos); } else if (op != '=' && !lhs->owned) { - string message = lhs->name + " is not defined"; - if(!throwError(execute, message)) { - CLEAN(tempScope); tempScope=NULL; - CLEAN(lhs); - CLEAN(rhs); - throw new CScriptException(message, pos); - } + throwError(execute, lhs->name + " is not defined", pos); } if (op=='=') { if (!lhs->owned) { CScriptVarLink *realLhs; - if(tempScope) - realLhs = tempScope->var->addChildNoDup(lhs->name, lhs->var); + if(lhs_tempScope) + realLhs = lhs_tempScope->addChildNoDup(lhs->name, lhs->var); else realLhs = root->addChildNoDup(lhs->name, lhs->var); - CLEAN(lhs); lhs = realLhs; } - lhs->replaceWith(rhs); - } else if (op==LEX_PLUSEQUAL) { - CScriptVar *res = lhs->var->mathsOp(rhs->var, '+'); - lhs->replaceWith(res); - } else if (op==LEX_MINUSEQUAL) { - CScriptVar *res = lhs->var->mathsOp(rhs->var, '-'); - lhs->replaceWith(res); - } else if (op==LEX_LSHIFTEQUAL) { - CScriptVar *res = lhs->var->mathsOp(rhs->var, LEX_LSHIFT); - lhs->replaceWith(res); - } else if (op==LEX_RSHIFTEQUAL) { - CScriptVar *res = lhs->var->mathsOp(rhs->var, LEX_RSHIFT); - lhs->replaceWith(res); - } else if (op==LEX_ANDEQUAL) { - CScriptVar *res = lhs->var->mathsOp(rhs->var, '&'); - lhs->replaceWith(res); - } else if (op==LEX_OREQUAL) { - CScriptVar *res = lhs->var->mathsOp(rhs->var, '|'); - lhs->replaceWith(res); - } else if (op==LEX_XOREQUAL) { - CScriptVar *res = lhs->var->mathsOp(rhs->var, '^'); - lhs->replaceWith(res); - } + lhs << rhs; + } else if (op==LEX_PLUSEQUAL) + lhs->replaceWith(lhs->var->mathsOp(rhs->var, '+')); + else if (op==LEX_MINUSEQUAL) + lhs->replaceWith(lhs->var->mathsOp(rhs->var, '-')); + else if (op==LEX_ASTERISKEQUAL) + lhs->replaceWith(lhs->var->mathsOp(rhs->var, '*')); + else if (op==LEX_SLASHEQUAL) + lhs->replaceWith(lhs->var->mathsOp(rhs->var, '/')); + else if (op==LEX_PERCENTEQUAL) + lhs->replaceWith(lhs->var->mathsOp(rhs->var, '%')); + else if (op==LEX_LSHIFTEQUAL) + lhs->replaceWith(lhs->var->mathsOp(rhs->var, LEX_LSHIFT)); + else if (op==LEX_RSHIFTEQUAL) + lhs->replaceWith(lhs->var->mathsOp(rhs->var, LEX_RSHIFT)); + else if (op==LEX_ANDEQUAL) + lhs->replaceWith(lhs->var->mathsOp(rhs->var, '&')); + else if (op==LEX_OREQUAL) + lhs->replaceWith(lhs->var->mathsOp(rhs->var, '|')); + else if (op==LEX_XOREQUAL) + lhs->replaceWith(lhs->var->mathsOp(rhs->var, '^')); } - CLEAN(rhs); } - else if(lhs->name.length()>0 && !lhs->owned && tempScope==NULL) { - string message = lhs->name + " is not defined"; - if(!throwError(execute, message)) { - CLEAN(lhs); - throw new CScriptException(message); - } + else if(lhs->name.length()>0 && !lhs->owned && lhs_tempScope==NULL) { + throwError(execute, lhs->name + " is not defined"); } - CLEAN(tempScope); tempScope=NULL; return lhs; } -CScriptVarLink *CTinyJS::base(bool &execute) { - CScriptVarLink *a; +// L->R: Precedence 17 +CScriptVarSmartLink CTinyJS::base(bool &execute) { + CScriptVarSmartLink a; for(;;) { - a = assignment(execute); + a = assignment(execute); // L->R if (l->tk == ',') { l->match(','); - CLEAN(a); } else break; } @@ -2101,19 +2185,7 @@ void CTinyJS::block(bool &execute) { } void CTinyJS::statement(bool &execute) { - if (l->tk==LEX_ID || - l->tk==LEX_INT || - l->tk==LEX_FLOAT || - l->tk==LEX_STR || - l->tk==LEX_PLUSPLUS || - l->tk==LEX_MINUSMINUS || - l->tk=='+' || - l->tk=='-' || - l->tk=='(') { - /* Execute a simple statement that only contains basic arithmetic... */ - CLEAN(base(execute)); - l->match(';'); - } else if (l->tk=='{') { + if (l->tk=='{') { /* A block of code */ block(execute); } else if (l->tk==';') { @@ -2126,18 +2198,18 @@ void CTinyJS::statement(bool &execute) { l->match(LEX_R_VAR); for(;;) { - CScriptVarLink *a = 0; + CScriptVarSmartLink a; string var = l->tkStr; l->match(LEX_ID); - if (execute) + if (execute) { a = scopes.back()->findChildOrCreate(var); - // sort out initialiser + a->dontDelete = true; + }// sort out initialiser if (l->tk == '=') { l->match('='); - CScriptVarLink *var = assignment(execute); + CScriptVarSmartLink var = assignment(execute); if (execute) - a->replaceWith(var); - CLEAN(var); + a << var; } if (l->tk == ',') l->match(','); @@ -2148,10 +2220,9 @@ void CTinyJS::statement(bool &execute) { } else if (l->tk==LEX_R_IF) { l->match(LEX_R_IF); l->match('('); - CScriptVarLink *var = base(execute); + CScriptVarSmartLink var = base(execute); l->match(')'); bool cond = execute && var->var->getBool(); - CLEAN(var); bool noexecute = false; // because we need to be abl;e to write to it statement(cond ? execute : noexecute); if (l->tk==LEX_R_ELSE) { @@ -2205,9 +2276,8 @@ void CTinyJS::statement(bool &execute) { whileCond->reset(); l = whileCond; } - CScriptVarLink *cond = base(execute); + CScriptVarSmartLink cond = base(execute); loopCond = execute && cond->var->getBool(); - CLEAN(cond); if(!whileCond) { whileCond = l->getSubLex(whileCondStart); @@ -2233,9 +2303,8 @@ void CTinyJS::statement(bool &execute) { l->match('('); int whileCondStart = l->tokenStart; bool noexecute = false; - CScriptVarLink *cond = base(execute); + CScriptVarSmartLink cond = base(execute); bool loopCond = execute && cond->var->getBool(); - CLEAN(cond); CScriptLex *whileCond = l->getSubLex(whileCondStart); l->match(')'); int whileBodyStart = l->tokenStart; @@ -2265,7 +2334,6 @@ void CTinyJS::statement(bool &execute) { l = whileCond; cond = base(execute); loopCond = execute && cond->var->getBool(); - CLEAN(cond); if (loopCond) { whileBody->reset(); l = whileBody; @@ -2302,27 +2370,17 @@ void CTinyJS::statement(bool &execute) { l->match('('); int pos=l->tokenStart; bool for_in = false; - CScriptVarLink *for_var = NULL; - CScriptVarLink *for_var_tempScope = NULL; - CScriptVarLink *for_in_var = NULL; + CScriptVarSmartLink for_var; + CScriptVar *for_var_tempScope; + CScriptVarSmartLink for_in_var; if(l->tk == LEX_ID) { for_var = factor(execute); for_var_tempScope = tempScope; -// l->match(LEX_ID); - if(l->tk == LEX_ID && l->tkStr == "in") { + if(l->tk == LEX_R_IN) { for_in = true; - l->match(LEX_ID); - try { - for_in_var = factor(execute); - } catch (CScriptException *e) { - CLEAN(for_var); - throw e; - } - if (l->tk != ')') { - CLEAN(for_var); - CLEAN(for_in_var); - } + l->match(LEX_R_IN); + for_in_var = factor(execute); l->match(')'); } else { error_pos = l->tokenEnd; @@ -2334,12 +2392,10 @@ void CTinyJS::statement(bool &execute) { else if (for_in) { if (execute && !for_var->owned) { CScriptVarLink *real_for_var; - if(tempScope) - real_for_var = tempScope->var->addChildNoDup(for_var->name, for_var->var); + if(for_var_tempScope) + real_for_var = for_var_tempScope->addChildNoDup(for_var->name, for_var->var); else real_for_var = root->addChildNoDup(for_var->name, for_var->var); - CLEAN(tempScope); tempScope=NULL; - CLEAN(for_var); for_var = real_for_var; } int forBodyStart = l->tokenStart; @@ -2374,13 +2430,12 @@ void CTinyJS::statement(bool &execute) { int forCondStart = l->tokenStart; bool cond_empty = true; bool loopCond = true; // Empty Condition -->always true - CScriptVarLink *cond; + CScriptVarSmartLink cond; if(l->tk != ';') { cond_empty = false; cond = base(execute); // condition loopCond = execute && cond->var->getBool(); - CLEAN(cond); } CScriptLex *forCond = l->getSubLex(forCondStart); l->match(';'); @@ -2427,7 +2482,6 @@ void CTinyJS::statement(bool &execute) { l = forCond; cond = base(execute); loopCond = cond->var->getBool(); - CLEAN(cond); } if (execute && loopCond) { forBody->reset(); @@ -2481,28 +2535,27 @@ void CTinyJS::statement(bool &execute) { l->match(';'); } else if (l->tk==LEX_R_RETURN) { l->match(LEX_R_RETURN); - CScriptVarLink *result = 0; + CScriptVarSmartLink result; if (l->tk != ';') result = base(execute); if (execute) { - CScriptVarLink *resultVar = scopes.back()->findChild(TINYJS_RETURN_VAR); - if (resultVar) - resultVar->replaceWith(result); - else - TRACE("RETURN statement, but not in a function.\n"); - execute = false; + CScriptVarSmartLink resultVar = scopes.back()->findChild(TINYJS_RETURN_VAR); + if (resultVar) + resultVar << result; + else { + TRACE("RETURN statement, but not in a function.\n"); + execute = false; + } } - CLEAN(result); l->match(';'); } else if (l->tk==LEX_R_FUNCTION) { - CScriptVarLink *funcVar = parseFunctionDefinition(); + CScriptVarSmartLink funcVar = parseFunctionDefinition(); if (execute) { if (funcVar->name == TINYJS_TEMP_NAME) TRACE("Functions defined at statement-level are meant to have a name\n"); else scopes.back()->addChildNoDup(funcVar->name, funcVar->var); } - CLEAN(funcVar); } else if (l->tk==LEX_R_TRY) { l->match(LEX_R_TRY); bool old_execute = execute; @@ -2527,10 +2580,9 @@ void CTinyJS::statement(bool &execute) { l->match(LEX_ID); l->match(')'); if(isThrow) { - // string exeption_var_name = - CScriptVar *catchScope = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_FUNCTION); - catchScope->addChild(exeption_var_name, exeption->var); - CLEAN(exeption); + CScriptVar *catchScope = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT); + catchScope->addChild(exeption_var_name, exeption); + exeption->unref(); exeption = 0; scopes.push_back(catchScope); block(execute); @@ -2544,29 +2596,30 @@ void CTinyJS::statement(bool &execute) { } if(l->tk == LEX_R_FINALLY) { l->match(LEX_R_FINALLY); - bool finally_execute = isThrow ? true : execute; - block(finally_execute); + bool finally_execute = true; + block(isThrow ? finally_execute : execute); ; } } else if (l->tk==LEX_R_THROW) { int tokenStart = l->tokenStart; l->match(LEX_R_THROW); - CScriptVarLink *a = base(execute); + CScriptVarSmartLink a = base(execute); if(execute) { if(runtimeFlags & RUNTIME_CANTHROW) { runtimeFlags |= RUNTIME_THROW; execute = false; - exeption = new CScriptVarLink(a->var); + exeption = a->var->ref(); } else - { - string exeptionStr = a->var->getString(); - CLEAN(a); - throw new CScriptException("uncaught exeption: "+exeptionStr, tokenStart); - } + throw new CScriptException("uncaught exeption: "+a->var->getString(), tokenStart); } - CLEAN(a); - } else l->match(LEX_EOF); + } else if(l->tk != LEX_EOF) { + /* Execute a simple statement that only contains basic arithmetic... */ + base(execute); + l->match(';'); + } else + l->match(LEX_EOF); + } /// Get the value of the given variable, or return 0 diff --git a/TinyJS.h b/TinyJS.h index dd8a1de..1238b50 100755 --- a/TinyJS.h +++ b/TinyJS.h @@ -68,6 +68,9 @@ enum LEX_TYPES { LEX_OREQUAL, LEX_OROR, LEX_XOREQUAL, + LEX_ASTERISKEQUAL, + LEX_SLASHEQUAL, + LEX_PERCENTEQUAL, // reserved words #define LEX_R_LIST_START LEX_R_IF LEX_R_IF, @@ -75,6 +78,7 @@ enum LEX_TYPES { LEX_R_DO, LEX_R_WHILE, LEX_R_FOR, + LEX_R_IN, LEX_R_BREAK, LEX_R_CONTINUE, LEX_R_FUNCTION, @@ -91,12 +95,16 @@ enum LEX_TYPES { LEX_R_CATCH, LEX_R_FINALLY, LEX_R_THROW, + LEX_R_TYPEOF, + LEX_R_VOID, + LEX_R_DELETE, + LEX_R_INSTANCEOF, LEX_R_LIST_END /* always the last entry */ }; enum SCRIPTVAR_FLAGS { - SCRIPTVAR_UNDEFINED = 0, + SCRIPTVAR_UNDEFINED = 0, SCRIPTVAR_FUNCTION = 1, SCRIPTVAR_OBJECT = 2, SCRIPTVAR_ARRAY = 4, @@ -201,10 +209,9 @@ class CScriptVarLink { public: std::string name; - CScriptVarLink *nextSibling; - CScriptVarLink *prevSibling; CScriptVar *var; CScriptVar *owned; // pointer to the owner CScriptVar + bool dontDelete; CScriptVarLink(CScriptVar *var, const std::string &name = TINYJS_TEMP_NAME); CScriptVarLink(const CScriptVarLink &link); ///< Copy constructor @@ -236,8 +243,7 @@ class CScriptVar CScriptVarLink *findChildOrCreateByPath(const std::string &path); ///< Tries to find a child with the given path (separated by dots) CScriptVarLink *addChild(const std::string &childName, CScriptVar *child=NULL); CScriptVarLink *addChildNoDup(const std::string &childName, CScriptVar *child=NULL); ///< add a child overwriting any with the same name - void removeChild(CScriptVar *child); - void removeLink(CScriptVarLink *link); ///< Remove a specific link (this is faster than finding via a child) + bool removeLink(CScriptVarLink *&link); ///< Remove a specific link (this is faster than finding via a child) void removeAllChildren(); CScriptVar *getArrayIndex(int idx); ///< The the value at an array index void setArrayIndex(int idx, CScriptVar *value); ///< Set the value at an array index @@ -249,6 +255,7 @@ class CScriptVar double getDouble(); const std::string &getString(); std::string getParsableString(); ///< get Data as a parsable javascript string + std::string getVarType(); ///< get Data as a parsable javascript string void setInt(int num); void setBool(bool val); void setDouble(double val); @@ -267,7 +274,7 @@ class CScriptVar bool isNative_ClassMemberFnc() { return (flags&SCRIPTVAR_NATIVE_MFNC)!=0; } bool isUndefined() { return (flags & SCRIPTVAR_VARTYPEMASK) == SCRIPTVAR_UNDEFINED; } bool isNull() { return (flags & SCRIPTVAR_NULL)!=0; } - bool isNaN() { return (flags&SCRIPTVAR_NAN)!=0; } + bool isNaN() { return (flags & SCRIPTVAR_NAN)!=0; } bool isInfinity() { return (flags&SCRIPTVAR_INFINITY)!=0; } bool isBasic() { return Childs.empty(); } ///< Is this *not* an array/object/etc @@ -329,6 +336,28 @@ class NativeFnc : public NativeFncBase void (native::*classFnc)(CScriptVar *v, void *userdata); }; +class CScriptVarSmartLink +{ +public: + CScriptVarSmartLink (); + CScriptVarSmartLink (CScriptVarLink *Link); + explicit CScriptVarSmartLink (CScriptVar *Var); + ~ CScriptVarSmartLink (); + // Copy + CScriptVarSmartLink (const CScriptVarSmartLink &Link); + CScriptVarSmartLink &operator = (const CScriptVarSmartLink &Link); + CScriptVarSmartLink &operator = (CScriptVarLink *Link); + CScriptVarSmartLink &operator = (CScriptVar *Var); + CScriptVarLink *operator ->() { return link; } + CScriptVarLink &operator *() { return *link; } + CScriptVarSmartLink &operator << (CScriptVarSmartLink &Link); + operator bool() { return link != 0; } + operator CScriptVarLink *() { return link; } + CScriptVarLink *&getLink() { return link; }; +private: + CScriptVarLink *link; +}; + class CTinyJS { public: CTinyJS(); @@ -382,7 +411,7 @@ class CTinyJS { const std::string *getVariable(const std::string &path); /// throws an Error - bool throwError(bool &execute, const std::string &message); + void throwError(bool &execute, const std::string &message, int pos=-1); /// Send all variables to stdout void trace(); @@ -395,20 +424,26 @@ class CTinyJS { CScriptVar *stringClass; /// Built in string class CScriptVar *objectClass; /// Built in object class CScriptVar *arrayClass; /// Built in array class - CScriptVarLink *tempScope; /// is temporary used by the '.' and '[' operator NULL meens the root-scope - CScriptVarLink *exeption; /// containing the exeption var by (runtimeFlags&RUNTIME_THROW) == true; + CScriptVar *tempScope; /// is temporary used by the '.' and '[' operator NULL meens the root-scope + CScriptVar *exeption; /// containing the exeption var by (runtimeFlags&RUNTIME_THROW) == true; + void CheckRightHandVar(bool &execute, CScriptVarSmartLink &link, int pos=-1) + { + if(execute && link && !tempScope && !link->owned && link->name.length()>0) + throwError(execute, link->name + " is not defined", pos); + } + // parsing - in order of precedence - CScriptVarLink *factor(bool &execute); - CScriptVarLink *unary(bool &execute); - CScriptVarLink *term(bool &execute); - CScriptVarLink *expression(bool &execute); - CScriptVarLink *binary_shift(bool &execute); - CScriptVarLink *relation(bool &execute, int set=LEX_EQUAL, int set_n='<'); - CScriptVarLink *logic_binary(bool &execute, int op='|', int op_n1='^', int op_n2='&'); - CScriptVarLink *logic(bool &execute, int op=LEX_OROR, int op_n=LEX_ANDAND); - CScriptVarLink *condition(bool &execute); - CScriptVarLink *assignment(bool &execute); - CScriptVarLink *base(bool &execute); + CScriptVarSmartLink factor(bool &execute); + CScriptVarSmartLink unary(bool &execute); + CScriptVarSmartLink term(bool &execute); + CScriptVarSmartLink expression(bool &execute); + CScriptVarSmartLink binary_shift(bool &execute); + CScriptVarSmartLink relation(bool &execute, int set=LEX_EQUAL, int set_n='<'); + CScriptVarSmartLink logic_binary(bool &execute, int op='|', int op_n1='^', int op_n2='&'); + CScriptVarSmartLink logic(bool &execute, int op=LEX_OROR, int op_n=LEX_ANDAND); + CScriptVarSmartLink condition(bool &execute); + CScriptVarSmartLink assignment(bool &execute); + CScriptVarSmartLink base(bool &execute); void block(bool &execute); void statement(bool &execute); // parsing utility functions From 0e8efd4b05aed62a637bac8a2a14cb5dc25e17f7 Mon Sep 17 00:00:00 2001 From: armin Date: Fri, 17 Sep 2010 13:45:48 +0000 Subject: [PATCH 11/43] added: switch, case & default --- TinyJS.cpp | 103 ++++++++++++++++++++++++++++++++++++++++------------- TinyJS.h | 15 ++++---- 2 files changed, 88 insertions(+), 30 deletions(-) diff --git a/TinyJS.cpp b/TinyJS.cpp index 2dc5482..e373d90 100755 --- a/TinyJS.cpp +++ b/TinyJS.cpp @@ -451,6 +451,9 @@ string CScriptLex::getTokenStr(int token) { case LEX_R_VOID : return "void"; case LEX_R_DELETE : return "delete"; case LEX_R_INSTANCEOF : return "instanceof"; + case LEX_R_SWITCH : return "switch"; + case LEX_R_CASE : return "case"; + case LEX_R_DEFAULT : return "default"; } ostringstream msg; @@ -521,6 +524,9 @@ void CScriptLex::getNextToken() { else if (tkStr=="void") tk = LEX_R_VOID; else if (tkStr=="delete") tk = LEX_R_DELETE; else if (tkStr=="instanceof") tk = LEX_R_INSTANCEOF; + else if (tkStr=="switch") tk = LEX_R_SWITCH; + else if (tkStr=="case") tk = LEX_R_CASE; + else if (tkStr=="default") tk = LEX_R_DEFAULT; } else if (isNumeric(currCh) || (currCh=='.' && isNumeric(nextCh))) { // Numbers if(currCh=='.') tkStr+='0'; bool isHex = false; @@ -734,7 +740,8 @@ CScriptVarLink::CScriptVarLink(CScriptVar *var, const std::string &name) { #endif this->name = name; this->var = var->ref(); - this->owned = 0; + this->owned = false; + this->owner = 0; this->dontDelete = false; } @@ -745,7 +752,8 @@ CScriptVarLink::CScriptVarLink(const CScriptVarLink &link) { #endif this->name = link.name; this->var = link.var->ref(); - this->owned = 0; + this->owned = false; + this->owner = 0; this->dontDelete = false; } @@ -905,7 +913,8 @@ CScriptVarLink *CScriptVar::addChild(const std::string &childName, CScriptVar *c ASSERT(0); // addCild - the child exists #endif CScriptVarLink *link = new CScriptVarLink(child, childName); - link->owned = this; + link->owned = true; + link->owner = this; return Childs[childName] = link; } CScriptVarLink *CScriptVar::addChildNoDup(const std::string &childName, CScriptVar *child) { @@ -915,10 +924,11 @@ CScriptVarLink *CScriptVar::addChildNoDup(const std::string &childName, CScriptV CScriptVarLink *v = findChild(childName); if (v) { - v->replaceWith(child); + v->replaceWith(child); } else { - CScriptVarLink *link = new CScriptVarLink(child, childName); - link->owned = this; + CScriptVarLink *link = new CScriptVarLink(child, childName); + link->owned = true; + link->owner = this; v = Childs[childName] = link; } @@ -1389,7 +1399,6 @@ CTinyJS::CTinyJS() { root->addChild("String", stringClass); root->addChild("Array", arrayClass); root->addChild("Object", objectClass); - tempScope = NULL; exeption = NULL; addNative("function eval(jsCode)", this, &CTinyJS::scEval); // execute the given string and return the resul } @@ -1545,7 +1554,6 @@ CScriptVarLink *CTinyJS::parseFunctionDefinition() { } // Precedence 2, Precedence 1 & literals CScriptVarSmartLink CTinyJS::factor(bool &execute) { - tempScope = 0; if (l->tk==LEX_R_TRUE) { l->match(LEX_R_TRUE); return new CScriptVarLink(new CScriptVar(true)); @@ -1757,8 +1765,9 @@ CScriptVarSmartLink CTinyJS::factor(bool &execute) { parent = a->var; a = child; } else { - tempScope = a->var; + CScriptVar *owner = a->var; a = new CScriptVarLink(new CScriptVar(), name); + a->owner = owner; } } } else ASSERT(0); @@ -1902,7 +1911,7 @@ CScriptVarSmartLink CTinyJS::unary(bool &execute) { if (execute) { // !!! no right-hand-check by delete if(a->owned && !a->dontDelete) { - a->owned->removeLink(a.getLink()); // removes the link from owner + a->owner->removeLink(a.getLink()); // removes the link from owner a = new CScriptVarLink(new CScriptVar(true)); } else @@ -2102,7 +2111,6 @@ CScriptVarSmartLink CTinyJS::condition(bool &execute) // R->L: Precedence 16 CScriptVarSmartLink CTinyJS::assignment(bool &execute) { CScriptVarSmartLink lhs = condition(execute); - CScriptVar *lhs_tempScope = tempScope; if (l->tk=='=' || l->tk==LEX_PLUSEQUAL || l->tk==LEX_MINUSEQUAL || l->tk==LEX_ASTERISKEQUAL || l->tk==LEX_SLASHEQUAL || l->tk==LEX_PERCENTEQUAL || l->tk==LEX_LSHIFTEQUAL || l->tk==LEX_RSHIFTEQUAL || @@ -2120,8 +2128,8 @@ CScriptVarSmartLink CTinyJS::assignment(bool &execute) { if (op=='=') { if (!lhs->owned) { CScriptVarLink *realLhs; - if(lhs_tempScope) - realLhs = lhs_tempScope->addChildNoDup(lhs->name, lhs->var); + if(lhs->owner) + realLhs = lhs->owner->addChildNoDup(lhs->name, lhs->var); else realLhs = root->addChildNoDup(lhs->name, lhs->var); lhs = realLhs; @@ -2149,7 +2157,7 @@ CScriptVarSmartLink CTinyJS::assignment(bool &execute) { lhs->replaceWith(lhs->var->mathsOp(rhs->var, '^')); } } - else if(lhs->name.length()>0 && !lhs->owned && lhs_tempScope==NULL) { + else if(lhs->name.length()>0 && !lhs->owned && !lhs->owner) { throwError(execute, lhs->name + " is not defined"); } return lhs; @@ -2168,7 +2176,6 @@ CScriptVarSmartLink CTinyJS::base(bool &execute) { return a; } void CTinyJS::block(bool &execute) { - // TODO: fast skip of blocks l->match('{'); if (execute) { while (l->tk && l->tk!='}') @@ -2371,11 +2378,9 @@ void CTinyJS::statement(bool &execute) { int pos=l->tokenStart; bool for_in = false; CScriptVarSmartLink for_var; - CScriptVar *for_var_tempScope; CScriptVarSmartLink for_in_var; if(l->tk == LEX_ID) { for_var = factor(execute); - for_var_tempScope = tempScope; if(l->tk == LEX_R_IN) { for_in = true; @@ -2392,8 +2397,8 @@ void CTinyJS::statement(bool &execute) { else if (for_in) { if (execute && !for_var->owned) { CScriptVarLink *real_for_var; - if(for_var_tempScope) - real_for_var = for_var_tempScope->addChildNoDup(for_var->name, for_var->var); + if(for_var->owner) + real_for_var = for_var->owner->addChildNoDup(for_var->name, for_var->var); else real_for_var = root->addChildNoDup(for_var->name, for_var->var); for_var = real_for_var; @@ -2524,6 +2529,8 @@ void CTinyJS::statement(bool &execute) { runtimeFlags |= RUNTIME_BREAK; execute = false; } + else + throw new CScriptException("'break' must be inside loop or switch"); l->match(';'); } else if (l->tk==LEX_R_CONTINUE) { l->match(LEX_R_CONTINUE); @@ -2532,6 +2539,8 @@ void CTinyJS::statement(bool &execute) { runtimeFlags |= RUNTIME_CONTINUE; execute = false; } + else + throw new CScriptException("'continue' must be inside loop"); l->match(';'); } else if (l->tk==LEX_R_RETURN) { l->match(LEX_R_RETURN); @@ -2542,17 +2551,16 @@ void CTinyJS::statement(bool &execute) { CScriptVarSmartLink resultVar = scopes.back()->findChild(TINYJS_RETURN_VAR); if (resultVar) resultVar << result; - else { - TRACE("RETURN statement, but not in a function.\n"); - execute = false; - } + else + throw new CScriptException("'return' statement, but not in a function."); + execute = false; } l->match(';'); } else if (l->tk==LEX_R_FUNCTION) { CScriptVarSmartLink funcVar = parseFunctionDefinition(); if (execute) { if (funcVar->name == TINYJS_TEMP_NAME) - TRACE("Functions defined at statement-level are meant to have a name\n"); + throw new CScriptException("Functions defined at statement-level are meant to have a name."); else scopes.back()->addChildNoDup(funcVar->name, funcVar->var); } @@ -2613,6 +2621,53 @@ void CTinyJS::statement(bool &execute) { else throw new CScriptException("uncaught exeption: "+a->var->getString(), tokenStart); } + } else if (l->tk==LEX_R_SWITCH) { + l->match(LEX_R_SWITCH); + l->match('('); + CScriptVarSmartLink SwitchValue = base(execute); + l->match(')'); + if(execute) { + // save runtimeFlags + int old_switch_runtimeFlags = runtimeFlags & RUNTIME_BREAK_MASK; + // set runtimeFlags + runtimeFlags = (runtimeFlags & ~RUNTIME_BREAK_MASK) | RUNTIME_CANBREAK; + bool noexecute = false; + bool old_execute = execute = false; + l->match('{'); + if(l->tk == LEX_R_CASE || l->tk == LEX_R_DEFAULT || l->tk == '}') { + + while (l->tk && l->tk!='}') { + if(l->tk == LEX_R_CASE) { + l->match(LEX_R_CASE); + if(execute) { + base(noexecute); + } else { + if(old_execute == execute) + old_execute = execute = true; + CScriptVarSmartLink CaseValue = base(execute); + if(execute) { + CaseValue = CaseValue->var->mathsOp(SwitchValue->var, LEX_EQUAL); + old_execute = execute = CaseValue->var->getBool(); + } + } + l->match(':'); + } else if(l->tk == LEX_R_DEFAULT) { + l->match(LEX_R_DEFAULT); + l->match(':'); + if(!old_execute && !execute ) + old_execute = execute = true; + } + statement(execute); + } + l->match('}'); + if(runtimeFlags & RUNTIME_BREAK) + execute = true; + // restore runtimeFlags + runtimeFlags = (runtimeFlags & ~RUNTIME_BREAK_MASK) | old_switch_runtimeFlags; + } else + throw new CScriptException("invalid switch statement"); + } else + block(execute); } else if(l->tk != LEX_EOF) { /* Execute a simple statement that only contains basic arithmetic... */ base(execute); diff --git a/TinyJS.h b/TinyJS.h index 1238b50..a6fac5a 100755 --- a/TinyJS.h +++ b/TinyJS.h @@ -99,6 +99,9 @@ enum LEX_TYPES { LEX_R_VOID, LEX_R_DELETE, LEX_R_INSTANCEOF, + LEX_R_SWITCH, + LEX_R_CASE, + LEX_R_DEFAULT, LEX_R_LIST_END /* always the last entry */ }; @@ -144,11 +147,11 @@ enum RUNTIME_FLAGS { RUNTIME_NEW = 16, RUNTIME_CANTHROW = 32, RUNTIME_THROW = 64, + RUNTIME_BREAK_MASK = RUNTIME_CANBREAK | RUNTIME_BREAK, + RUNTIME_LOOP_MASK = RUNTIME_BREAK_MASK | RUNTIME_CANCONTINUE | RUNTIME_CONTINUE, + RUNTIME_THROW_MASK = RUNTIME_CANTHROW | RUNTIME_THROW, }; -#define RUNTIME_LOOP_MASK (RUNTIME_CANBREAK | RUNTIME_BREAK | RUNTIME_CANCONTINUE | RUNTIME_CONTINUE) -#define RUNTIME_THROW_MASK (RUNTIME_CANTHROW | RUNTIME_THROW) - #define TINYJS_RETURN_VAR "return" #define TINYJS_PROTOTYPE_CLASS "prototype" #define TINYJS_TEMP_NAME "" @@ -210,7 +213,8 @@ class CScriptVarLink public: std::string name; CScriptVar *var; - CScriptVar *owned; // pointer to the owner CScriptVar + bool owned; + CScriptVar *owner; // pointer to the owner CScriptVar bool dontDelete; CScriptVarLink(CScriptVar *var, const std::string &name = TINYJS_TEMP_NAME); @@ -424,11 +428,10 @@ class CTinyJS { CScriptVar *stringClass; /// Built in string class CScriptVar *objectClass; /// Built in object class CScriptVar *arrayClass; /// Built in array class - CScriptVar *tempScope; /// is temporary used by the '.' and '[' operator NULL meens the root-scope CScriptVar *exeption; /// containing the exeption var by (runtimeFlags&RUNTIME_THROW) == true; void CheckRightHandVar(bool &execute, CScriptVarSmartLink &link, int pos=-1) { - if(execute && link && !tempScope && !link->owned && link->name.length()>0) + if(execute && link && !link->owned && !link->owner && link->name.length()>0) throwError(execute, link->name + " is not defined", pos); } From 2e7def591444b2805beaa340e14435c0464d3da2 Mon Sep 17 00:00:00 2001 From: armin Date: Fri, 17 Sep 2010 18:40:15 +0000 Subject: [PATCH 12/43] fixed: set the dontDelete-Flag for function-declarations, they can't be deleted fixed: in-operator works now fully fixed: In javascript, a function does not inherit the scope of the caller. E.g.: var var_b = "global"; function a() { print(var_b); } function b() { var var_b="local"; a(); } b(); // --> prints "global", but before the fix was "local" printed Function "a()" is called from function "b()", but the function "a()" has no access to 'var_b', because "var_b" was declared in the local scope of function "b()" "CTinyJS::findInScopes(...)" now searched only in the current scope (scopes.back()) and in the root-scope (scopes.front()) --- TinyJS.cpp | 21 +++++++++++---------- TinyJS.h | 7 ++++--- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/TinyJS.cpp b/TinyJS.cpp index e373d90..ef91075 100755 --- a/TinyJS.cpp +++ b/TinyJS.cpp @@ -2014,16 +2014,19 @@ CScriptVarSmartLink CTinyJS::binary_shift(bool &execute) { CScriptVarSmartLink CTinyJS::relation(bool &execute, int set, int set_n) { CScriptVarSmartLink a = set_n ? relation(execute, set_n, 0) : binary_shift(execute); if ((set==LEX_EQUAL && (l->tk==LEX_EQUAL || l->tk==LEX_NEQUAL || l->tk==LEX_TYPEEQUAL || l->tk==LEX_NTYPEEQUAL)) - || (set=='<' && (l->tk==LEX_LEQUAL || l->tk==LEX_GEQUAL || l->tk=='<' || l->tk=='>'))) { + || (set=='<' && (l->tk==LEX_LEQUAL || l->tk==LEX_GEQUAL || l->tk=='<' || l->tk=='>' || l->tk == LEX_R_IN))) { CheckRightHandVar(execute, a); while ((set==LEX_EQUAL && (l->tk==LEX_EQUAL || l->tk==LEX_NEQUAL || l->tk==LEX_TYPEEQUAL || l->tk==LEX_NTYPEEQUAL)) - || (set=='<' && (l->tk==LEX_LEQUAL || l->tk==LEX_GEQUAL || l->tk=='<' || l->tk=='>'))) { + || (set=='<' && (l->tk==LEX_LEQUAL || l->tk==LEX_GEQUAL || l->tk=='<' || l->tk=='>' || l->tk == LEX_R_IN))) { int op = l->tk; l->match(l->tk); CScriptVarSmartLink b = set_n ? relation(execute, set_n, 0) : binary_shift(execute); // L->R if (execute) { CheckRightHandVar(execute, b); - a = a->var->mathsOp(b->var, op); + if(op == LEX_R_IN) { + a = new CScriptVarLink(new CScriptVar( b->var->findChild(a->var->getString())!= 0 )); + } else + a = a->var->mathsOp(b->var, op); } } } @@ -2562,7 +2565,7 @@ void CTinyJS::statement(bool &execute) { if (funcVar->name == TINYJS_TEMP_NAME) throw new CScriptException("Functions defined at statement-level are meant to have a name."); else - scopes.back()->addChildNoDup(funcVar->name, funcVar->var); + scopes.back()->addChildNoDup(funcVar->name, funcVar->var)->dontDelete = true; } } else if (l->tk==LEX_R_TRY) { l->match(LEX_R_TRY); @@ -2701,12 +2704,10 @@ const string *CTinyJS::getVariable(const string &path) { /// Finds a child, looking recursively up the scopes CScriptVarLink *CTinyJS::findInScopes(const std::string &childName) { - for (int s=scopes.size()-1;s>=0;s--) { - CScriptVarLink *v = scopes[s]->findChild(childName); - if (v) return v; - } - return NULL; - + CScriptVarLink *v = scopes.back()->findChild(childName); + if(!v && scopes.front() != scopes.back()) + v = scopes.front()->findChild(childName); + return v; } /// Look up in any parent classes of the given object diff --git a/TinyJS.h b/TinyJS.h index a6fac5a..324f878 100755 --- a/TinyJS.h +++ b/TinyJS.h @@ -213,10 +213,11 @@ class CScriptVarLink public: std::string name; CScriptVar *var; - bool owned; CScriptVar *owner; // pointer to the owner CScriptVar - bool dontDelete; - + struct { + bool owned :1; + bool dontDelete :1; + }; CScriptVarLink(CScriptVar *var, const std::string &name = TINYJS_TEMP_NAME); CScriptVarLink(const CScriptVarLink &link); ///< Copy constructor ~CScriptVarLink(); From 7eaa228436e93e69d8daf419d4b59a0ba4aafdb5 Mon Sep 17 00:00:00 2001 From: armin Date: Fri, 17 Sep 2010 22:28:27 +0000 Subject: [PATCH 13/43] fixed: a few memoryleaks by throwing an error fixed: wrong execute state when returning from a function with throw ...; changed: evaluate and evaluateComplex interpret now also statements fixed: eval(...) eval interpret now also statements fixed: eval(...) must interpret the code with the context of the caller --- TinyJS.cpp | 154 ++++++++++++++++++++++++++--------------------------- TinyJS.h | 26 ++++----- 2 files changed, 91 insertions(+), 89 deletions(-) diff --git a/TinyJS.cpp b/TinyJS.cpp index ef91075..391e183 100755 --- a/TinyJS.cpp +++ b/TinyJS.cpp @@ -380,6 +380,7 @@ void CScriptLex::reset(int toPos) { } void CScriptLex::match(int expected_tk) { + if (expected_tk==';' && tk==LEX_EOF) return; // ignore last missing ';' if (tk!=expected_tk) { ostringstream errorString; errorString << "Got " << getTokenStr(tk) << " expected " << getTokenStr(expected_tk) @@ -1392,6 +1393,7 @@ CTinyJS::CTinyJS() { l = NULL; runtimeFlags = 0; root = (new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT))->ref(); + scopes.push_back(root); // Add built-in classes stringClass = (new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT))->ref(); arrayClass = (new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT))->ref(); @@ -1431,56 +1433,35 @@ void CTinyJS::trace() { } void CTinyJS::execute(const string &code) { - CScriptLex *oldLex = l; - vector oldScopes = scopes; - l = new CScriptLex(code); - scopes.clear(); - scopes.push_back(root); - try { - bool execute = true; - while (l->tk) statement(execute); - } catch (CScriptException *e) { - ostringstream msg; - msg << "Error " << e->text << " at " << l->getPosition(e->pos); - delete e; - delete l; - l = oldLex; - throw new CScriptException(msg.str()); - } - delete l; - l = oldLex; - scopes = oldScopes; + evaluateComplex(code); } CScriptVarLink CTinyJS::evaluateComplex(const string &code) { CScriptLex *oldLex = l; - vector oldScopes = scopes; l = new CScriptLex(code); - scopes.clear(); - scopes.push_back(root); CScriptVarSmartLink v; try { - bool execute = true; - do { - v = base(execute); - if (l->tk!=LEX_EOF) l->match(';'); - } while (l->tk!=LEX_EOF); + bool execute = true; + do { + v = statement(execute); + while (l->tk==';') l->match(';'); // skip empty statements + } while (l->tk!=LEX_EOF); } catch (CScriptException *e) { - ostringstream msg; - msg << "Error " << e->text << " at " << l->getPosition(e->pos); - delete e; - delete l; - l = oldLex; - throw new CScriptException(msg.str()); + runtimeFlags = 0; // clean up + ostringstream msg; + msg << "Error " << e->text << " at " << l->getPosition(e->pos); + delete e; + delete l; + l = oldLex; + throw new CScriptException(msg.str()); } delete l; l = oldLex; - scopes = oldScopes; if (v) { - CScriptVarLink r = *v; - return r; + CScriptVarLink r = *v; + return r; } // return undefined... return CScriptVarLink(new CScriptVar()); @@ -1491,13 +1472,13 @@ string CTinyJS::evaluate(const string &code) { } void CTinyJS::parseFunctionArguments(CScriptVar *funcVar) { - l->match('('); - while (l->tk!=')') { + l->match('('); + while (l->tk!=')') { funcVar->addChildNoDup(l->tkStr); l->match(LEX_ID); if (l->tk!=')') l->match(','); - } - l->match(')'); + } + l->match(')'); } void CTinyJS::addNative(const string &funcDesc, JSCallback ptr, void *userdata) { CScriptVar *funcVar = addNative(funcDesc); @@ -1673,39 +1654,45 @@ CScriptVarSmartLink CTinyJS::factor(bool &execute) { parameters->addChild("length", new CScriptVar(parameters_idx)); functionRoot->addChild("parameters", parameters); // setup a return variable - CScriptVarLink *returnVar = NULL; + CScriptVarLink *returnVar = 0; if(execute) { // execute function! // add the function's execute space to the symbol table so we can recurse CScriptVarLink *returnVarLink = functionRoot->addChild(TINYJS_RETURN_VAR); scopes.push_back(functionRoot); - - if (a->var->isNative()) { - ASSERT(a->var->jsCallback); - if (a->var->isNative_ClassMemberFnc()) - (*a->var->jsCallbackClass)(functionRoot, a->var->jsCallbackUserData); - else - a->var->jsCallback(functionRoot, a->var->jsCallbackUserData); - } else { - /* we just want to execute the block, but something could - * have messed up and left us with the wrong ScriptLex, so - * we want to be careful here... */ - CScriptException *exception = 0; - CScriptLex *oldLex = l; - CScriptLex *newLex = new CScriptLex(a->var->getString()); - l = newLex; - try { - block(execute); - // because return will probably have called this, and set execute to false - execute = true; - } catch (CScriptException *e) { - exception = e; + try { + if (a->var->isNative()) { + ASSERT(a->var->jsCallback); + if (a->var->isNative_ClassMemberFnc()) + (*a->var->jsCallbackClass)(functionRoot, a->var->jsCallbackUserData); + else + a->var->jsCallback(functionRoot, a->var->jsCallbackUserData); + } else { + /* we just want to execute the block, but something could + * have messed up and left us with the wrong ScriptLex, so + * we want to be careful here... */ + CScriptLex *oldLex = l; + CScriptLex *newLex = new CScriptLex(a->var->getString()); + l = newLex; + try { + block(execute); + // because return will probably have called this, and set execute to false + if(runtimeFlags & RUNTIME_RETURN) { + execute = true; + runtimeFlags &= ~RUNTIME_RETURN; + } + } catch (CScriptException *e) { + delete newLex; + l = oldLex; + throw e; + } + delete newLex; + l = oldLex; } - delete newLex; - l = oldLex; - - if (exception) - throw exception; + } catch (CScriptException *e) { + scopes.pop_back(); + delete functionRoot; + throw e; } scopes.pop_back(); @@ -2160,9 +2147,8 @@ CScriptVarSmartLink CTinyJS::assignment(bool &execute) { lhs->replaceWith(lhs->var->mathsOp(rhs->var, '^')); } } - else if(lhs->name.length()>0 && !lhs->owned && !lhs->owner) { - throwError(execute, lhs->name + " is not defined"); - } + else + CheckRightHandVar(execute, lhs); return lhs; } // L->R: Precedence 17 @@ -2194,7 +2180,8 @@ void CTinyJS::block(bool &execute) { } } -void CTinyJS::statement(bool &execute) { +CScriptVarSmartLink CTinyJS::statement(bool &execute) { + CScriptVarSmartLink ret; if (l->tk=='{') { /* A block of code */ block(execute); @@ -2595,9 +2582,13 @@ void CTinyJS::statement(bool &execute) { catchScope->addChild(exeption_var_name, exeption); exeption->unref(); exeption = 0; scopes.push_back(catchScope); - - block(execute); - + try { + block(execute); + } catch (CScriptException *e) { + scopes.pop_back(); // clean up scopes + delete catchScope; + throw e; + } scopes.pop_back(); delete catchScope; } else { @@ -2673,11 +2664,11 @@ void CTinyJS::statement(bool &execute) { block(execute); } else if(l->tk != LEX_EOF) { /* Execute a simple statement that only contains basic arithmetic... */ - base(execute); + ret = base(execute); l->match(';'); } else l->match(LEX_EOF); - + return ret; } /// Get the value of the given variable, or return 0 @@ -2736,5 +2727,14 @@ CScriptVarLink *CTinyJS::findInParentClasses(CScriptVar *object, const std::stri void CTinyJS::scEval(CScriptVar *c, void *data) { std::string str = c->getParameter("jsCode")->getString(); - c->setReturnVar(evaluateComplex(str).var); + CScriptVar *scEvalScope = scopes.back(); // save scope + scopes.pop_back(); // go back to the callers scope + try { + CScriptVarLink returnVar = evaluateComplex(str).var; + scopes.push_back(scEvalScope); // restore Scopes; + c->setReturnVar(returnVar.var); + } catch (CScriptException *e) { + scopes.push_back(scEvalScope); // restore Scopes; + throw e; + } } diff --git a/TinyJS.h b/TinyJS.h index 324f878..39d3ed9 100755 --- a/TinyJS.h +++ b/TinyJS.h @@ -140,13 +140,14 @@ enum SCRIPTVAR_FLAGS { SCRIPTVAR_NATIVE_MFNC, }; enum RUNTIME_FLAGS { - RUNTIME_CANBREAK = 1, - RUNTIME_BREAK = 2, - RUNTIME_CANCONTINUE = 4, - RUNTIME_CONTINUE = 8, - RUNTIME_NEW = 16, - RUNTIME_CANTHROW = 32, - RUNTIME_THROW = 64, + RUNTIME_RETURN = 1<<0, + RUNTIME_CANBREAK = 1<<1, + RUNTIME_BREAK = 1<<2, + RUNTIME_CANCONTINUE = 1<<3, + RUNTIME_CONTINUE = 1<<4, + RUNTIME_NEW = 1<<5, + RUNTIME_CANTHROW = 1<<6, + RUNTIME_THROW = 1<<7, RUNTIME_BREAK_MASK = RUNTIME_CANBREAK | RUNTIME_BREAK, RUNTIME_LOOP_MASK = RUNTIME_BREAK_MASK | RUNTIME_CANCONTINUE | RUNTIME_CONTINUE, RUNTIME_THROW_MASK = RUNTIME_CANTHROW | RUNTIME_THROW, @@ -415,9 +416,6 @@ class CTinyJS { /// Get the value of the given variable, or return 0 const std::string *getVariable(const std::string &path); - /// throws an Error - void throwError(bool &execute, const std::string &message, int pos=-1); - /// Send all variables to stdout void trace(); @@ -449,7 +447,7 @@ class CTinyJS { CScriptVarSmartLink assignment(bool &execute); CScriptVarSmartLink base(bool &execute); void block(bool &execute); - void statement(bool &execute); + CScriptVarSmartLink statement(bool &execute); // parsing utility functions CScriptVarLink *parseFunctionDefinition(); void parseFunctionArguments(CScriptVar *funcVar); @@ -458,7 +456,11 @@ class CTinyJS { /// Look up in any parent classes of the given object CScriptVarLink *findInParentClasses(CScriptVar *object, const std::string &name); CScriptVar *addNative(const std::string &funcDesc); -public: // native Functions + + /// throws an Error + void throwError(bool &execute, const std::string &message, int pos=-1); + + /// native Functions void scEval(CScriptVar *c, void *data); }; From daeb01dabc3a284fbcf552b3192229e060317601 Mon Sep 17 00:00:00 2001 From: armin Date: Sun, 19 Sep 2010 00:06:04 +0000 Subject: [PATCH 14/43] fixed: a few bugs, i have created in the last release fixed: mistake with 'break' & 'continue' when execute==false added: throwing a catch-able error from a native function. Simply throw a pointer to a CScripVarLink. e.g.: throw new CScriptVarLink(....); added: eval() can break and continue a loop, can return from a function and can throws an error. for(i=0; i<10; ++i) { ... eval("break;"); ... } function f(i){ if(i<12) eval("return i*12;"); else eval("throw i+12;"); } --- TinyJS.cpp | 138 ++++++++++++++++++++++++++++++++++++++++++----------- TinyJS.h | 16 +++---- 2 files changed, 118 insertions(+), 36 deletions(-) diff --git a/TinyJS.cpp b/TinyJS.cpp index 391e183..2aea387 100755 --- a/TinyJS.cpp +++ b/TinyJS.cpp @@ -801,6 +801,16 @@ CScriptVar::CScriptVar(const string &str) { data = str; } +CScriptVar::CScriptVar(const char *str) { + refs = 0; + __proto__ = NULL; +#if DEBUG_MEMORY + mark_allocated(this); +#endif + init(); + flags = SCRIPTVAR_STRING; + data = str; +} CScriptVar::CScriptVar(const string &varData, int varFlags) { refs = 0; @@ -1448,7 +1458,7 @@ CScriptVarLink CTinyJS::evaluateComplex(const string &code) { while (l->tk==';') l->match(';'); // skip empty statements } while (l->tk!=LEX_EOF); } catch (CScriptException *e) { - runtimeFlags = 0; // clean up + runtimeFlags = 0; // clean up runtimeFlags ostringstream msg; msg << "Error " << e->text << " at " << l->getPosition(e->pos); delete e; @@ -1533,6 +1543,13 @@ CScriptVarLink *CTinyJS::parseFunctionDefinition() { funcVar->var->data = l->getSubString(funcBegin); return funcVar; } + +class CScriptEvalException { +public: + int runtimeFlags; + CScriptEvalException(int RuntimeFlags) : runtimeFlags(RuntimeFlags){} +}; + // Precedence 2, Precedence 1 & literals CScriptVarSmartLink CTinyJS::factor(bool &execute) { if (l->tk==LEX_R_TRUE) { @@ -1656,6 +1673,8 @@ CScriptVarSmartLink CTinyJS::factor(bool &execute) { // setup a return variable CScriptVarLink *returnVar = 0; if(execute) { + int old_function_runtimeFlags = runtimeFlags; // save runtimFlags + runtimeFlags &= ~RUNTIME_LOOP_MASK; // clear LOOP-Flags // execute function! // add the function's execute space to the symbol table so we can recurse CScriptVarLink *returnVarLink = functionRoot->addChild(TINYJS_RETURN_VAR); @@ -1663,10 +1682,43 @@ CScriptVarSmartLink CTinyJS::factor(bool &execute) { try { if (a->var->isNative()) { ASSERT(a->var->jsCallback); - if (a->var->isNative_ClassMemberFnc()) - (*a->var->jsCallbackClass)(functionRoot, a->var->jsCallbackUserData); - else - a->var->jsCallback(functionRoot, a->var->jsCallbackUserData); + try { + if (a->var->isNative_ClassMemberFnc()) + (*a->var->jsCallbackClass)(functionRoot, a->var->jsCallbackUserData); + else + a->var->jsCallback(functionRoot, a->var->jsCallbackUserData); + // special thinks for eval() // set execute to false when runtimeFlags setted + runtimeFlags = old_function_runtimeFlags | (runtimeFlags & RUNTIME_THROW); // restore runtimeFlags + if(runtimeFlags & RUNTIME_THROW) { + execute = false; + } + } catch (CScriptEvalException *e) { + runtimeFlags = old_function_runtimeFlags; // restore runtimeFlags + if(e->runtimeFlags & RUNTIME_BREAK) { + if(runtimeFlags & RUNTIME_CANBREAK) { + runtimeFlags |= RUNTIME_BREAK; + execute = false; + } + else + throw new CScriptException("'break' must be inside loop or switch"); + } else if(e->runtimeFlags & RUNTIME_CONTINUE) { + if(runtimeFlags & RUNTIME_CANCONTINUE) { + runtimeFlags |= RUNTIME_CONTINUE; + execute = false; + } + else + throw new CScriptException("'continue' must be inside loop"); + } + } catch (CScriptVarLink *v) { + CScriptVarSmartLink e = v; + if(runtimeFlags & RUNTIME_CANTHROW) { + runtimeFlags |= RUNTIME_THROW; + execute = false; + this->exeption = e->var->ref(); + } + else + throw new CScriptException("uncaught exeption: '"+e->var->getString()+"' in: "+a->name+"()"); + } } else { /* we just want to execute the block, but something could * have messed up and left us with the wrong ScriptLex, so @@ -1677,9 +1729,9 @@ CScriptVarSmartLink CTinyJS::factor(bool &execute) { try { block(execute); // because return will probably have called this, and set execute to false - if(runtimeFlags & RUNTIME_RETURN) { + runtimeFlags = old_function_runtimeFlags | (runtimeFlags & RUNTIME_THROW); // restore runtimeFlags + if(!(runtimeFlags & RUNTIME_THROW)) { execute = true; - runtimeFlags &= ~RUNTIME_RETURN; } } catch (CScriptException *e) { delete newLex; @@ -2514,23 +2566,27 @@ CScriptVarSmartLink CTinyJS::statement(bool &execute) { } } else if (l->tk==LEX_R_BREAK) { l->match(LEX_R_BREAK); - if(execute && runtimeFlags & RUNTIME_CANBREAK) - { - runtimeFlags |= RUNTIME_BREAK; - execute = false; + if(execute) { + if(runtimeFlags & RUNTIME_CANBREAK) + { + runtimeFlags |= RUNTIME_BREAK; + execute = false; + } + else + throw new CScriptException("'break' must be inside loop or switch"); } - else - throw new CScriptException("'break' must be inside loop or switch"); l->match(';'); } else if (l->tk==LEX_R_CONTINUE) { l->match(LEX_R_CONTINUE); - if(execute && runtimeFlags & RUNTIME_CANCONTINUE) - { - runtimeFlags |= RUNTIME_CONTINUE; - execute = false; + if(execute) { + if(runtimeFlags & RUNTIME_CANCONTINUE) + { + runtimeFlags |= RUNTIME_CONTINUE; + execute = false; + } + else + throw new CScriptException("'continue' must be inside loop"); } - else - throw new CScriptException("'continue' must be inside loop"); l->match(';'); } else if (l->tk==LEX_R_RETURN) { l->match(LEX_R_RETURN); @@ -2538,10 +2594,10 @@ CScriptVarSmartLink CTinyJS::statement(bool &execute) { if (l->tk != ';') result = base(execute); if (execute) { - CScriptVarSmartLink resultVar = scopes.back()->findChild(TINYJS_RETURN_VAR); - if (resultVar) - resultVar << result; - else + CScriptVarSmartLink resultVar = scopes.back()->findChild(TINYJS_RETURN_VAR); + if (resultVar) { + if(result) resultVar << result; + } else throw new CScriptException("'return' statement, but not in a function."); execute = false; } @@ -2613,7 +2669,7 @@ CScriptVarSmartLink CTinyJS::statement(bool &execute) { exeption = a->var->ref(); } else - throw new CScriptException("uncaught exeption: "+a->var->getString(), tokenStart); + throw new CScriptException("uncaught exeption: '"+a->var->getString()+"'", tokenStart); } } else if (l->tk==LEX_R_SWITCH) { l->match(LEX_R_SWITCH); @@ -2726,15 +2782,41 @@ CScriptVarLink *CTinyJS::findInParentClasses(CScriptVar *object, const std::stri } void CTinyJS::scEval(CScriptVar *c, void *data) { - std::string str = c->getParameter("jsCode")->getString(); + std::string code = c->getParameter("jsCode")->getString(); CScriptVar *scEvalScope = scopes.back(); // save scope scopes.pop_back(); // go back to the callers scope try { - CScriptVarLink returnVar = evaluateComplex(str).var; - scopes.push_back(scEvalScope); // restore Scopes; - c->setReturnVar(returnVar.var); + CScriptLex *oldLex = l; + l = new CScriptLex(code); + CScriptVarSmartLink returnVar; + runtimeFlags |= RUNTIME_CANBREAK | RUNTIME_CANCONTINUE; + + bool execute = true; + try { + do { + returnVar = statement(execute); + while (l->tk==';') l->match(';'); // skip empty statements + } while (l->tk!=LEX_EOF); + } catch (CScriptException *e) { + delete l; + l = oldLex; + throw e; + } + delete l; + l = oldLex; + + this->scopes.push_back(scEvalScope); // restore Scopes; + if(returnVar) + c->setReturnVar(returnVar->var); + + // check of exeptions + int exeption = runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE); + runtimeFlags &= ~RUNTIME_LOOP_MASK; + if(exeption) throw new CScriptEvalException(exeption); + } catch (CScriptException *e) { scopes.push_back(scEvalScope); // restore Scopes; + e->pos = -1; throw e; } } diff --git a/TinyJS.h b/TinyJS.h index 39d3ed9..15bfe06 100755 --- a/TinyJS.h +++ b/TinyJS.h @@ -140,14 +140,13 @@ enum SCRIPTVAR_FLAGS { SCRIPTVAR_NATIVE_MFNC, }; enum RUNTIME_FLAGS { - RUNTIME_RETURN = 1<<0, - RUNTIME_CANBREAK = 1<<1, - RUNTIME_BREAK = 1<<2, - RUNTIME_CANCONTINUE = 1<<3, - RUNTIME_CONTINUE = 1<<4, - RUNTIME_NEW = 1<<5, - RUNTIME_CANTHROW = 1<<6, - RUNTIME_THROW = 1<<7, + RUNTIME_CANBREAK = 1<<0, + RUNTIME_BREAK = 1<<1, + RUNTIME_CANCONTINUE = 1<<2, + RUNTIME_CONTINUE = 1<<3, + RUNTIME_NEW = 1<<4, + RUNTIME_CANTHROW = 1<<5, + RUNTIME_THROW = 1<<6, RUNTIME_BREAK_MASK = RUNTIME_CANBREAK | RUNTIME_BREAK, RUNTIME_LOOP_MASK = RUNTIME_BREAK_MASK | RUNTIME_CANCONTINUE | RUNTIME_CONTINUE, RUNTIME_THROW_MASK = RUNTIME_CANTHROW | RUNTIME_THROW, @@ -235,6 +234,7 @@ class CScriptVar CScriptVar(); ///< Create undefined CScriptVar(const std::string &varData, int varFlags); ///< User defined CScriptVar(const std::string &str); ///< Create a string + CScriptVar(const char *str); ///< Create a string CScriptVar(double varData); CScriptVar(int val); CScriptVar(bool val); From ed42ac3b69c8a411a9c8a1fc5a46b2f78d2cb414 Mon Sep 17 00:00:00 2001 From: armin Date: Mon, 20 Sep 2010 14:46:17 +0000 Subject: [PATCH 15/43] fixed: in R26 i have added variadic args by injecting a 'paramters'-Object in the functions-scope. But the correct name of this Object is 'arguments'. --- TinyJS.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/TinyJS.cpp b/TinyJS.cpp index 2aea387..049024c 100755 --- a/TinyJS.cpp +++ b/TinyJS.cpp @@ -1617,8 +1617,8 @@ CScriptVarSmartLink CTinyJS::factor(bool &execute) { else functionRoot->addChildNoDup("this", new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT)); // always add a this-Object // grab in all parameters - CScriptVar *parameters = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT); - int parameters_idx = 0; + CScriptVar *arguments = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT); + int arguments_idx = 0; for(SCRIPTVAR_CHILDS::iterator it = a->var->Childs.begin(); it != a->var->Childs.end(); ++it) { if (l->tk!=')') { CScriptVarSmartLink value; @@ -1626,17 +1626,17 @@ CScriptVarSmartLink CTinyJS::factor(bool &execute) { value = assignment(execute); path += value->var->getString(); } catch (CScriptException *e) { - delete parameters; + delete arguments; delete functionRoot; throw e; } if (execute) { if (value->var->isBasic()) { // pass by value - functionRoot->addChild( it->first, parameters->addChild(int2string(parameters_idx++) , value->var->deepCopy())->var); + functionRoot->addChild( it->first, arguments->addChild(int2string(arguments_idx++) , value->var->deepCopy())->var); } else { // pass by reference - functionRoot->addChild(it->first, parameters->addChild(int2string(parameters_idx++), value->var)->var); + functionRoot->addChild(it->first, arguments->addChild(int2string(arguments_idx++), value->var)->var); } } } else { @@ -1650,17 +1650,17 @@ CScriptVarSmartLink CTinyJS::factor(bool &execute) { value = assignment(execute); path += value->var->getString(); } catch (CScriptException *e) { - delete parameters; + delete arguments; delete functionRoot; throw e; } if (execute) { if (value->var->isBasic()) { // pass by value - parameters->addChild(int2string(parameters_idx++) , value->var->deepCopy()); + arguments->addChild(int2string(arguments_idx++) , value->var->deepCopy()); } else { // pass by reference - parameters->addChild(int2string(parameters_idx++), value->var); + arguments->addChild(int2string(arguments_idx++), value->var); } } if (l->tk!=')') { l->match(','); path+=','; } @@ -1668,8 +1668,8 @@ CScriptVarSmartLink CTinyJS::factor(bool &execute) { l->match(')'); path+=')'; - parameters->addChild("length", new CScriptVar(parameters_idx)); - functionRoot->addChild("parameters", parameters); + arguments->addChild("length", new CScriptVar(arguments_idx)); + functionRoot->addChild("arguments", arguments); // setup a return variable CScriptVarLink *returnVar = 0; if(execute) { From 99ddf29aa6f4c93aaf492faef0d058a4bc54c4e6 Mon Sep 17 00:00:00 2001 From: armin Date: Mon, 20 Sep 2010 18:42:53 +0000 Subject: [PATCH 16/43] fixed: Bug since the change from linked list to c++ map in R25. A map is allways sorted. So also the argumentlist from a function. This could lead to an argument that associated with a wrong value. --- TinyJS.cpp | 52 ++++++++++++++++++++-------------------------------- 1 file changed, 20 insertions(+), 32 deletions(-) diff --git a/TinyJS.cpp b/TinyJS.cpp index 049024c..44feb33 100755 --- a/TinyJS.cpp +++ b/TinyJS.cpp @@ -1483,12 +1483,17 @@ string CTinyJS::evaluate(const string &code) { void CTinyJS::parseFunctionArguments(CScriptVar *funcVar) { l->match('('); + CScriptVar *arguments = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT); + funcVar->addChild("arguments", arguments); + int idx = 0; while (l->tk!=')') { - funcVar->addChildNoDup(l->tkStr); + arguments->addChild(int2string(idx++), new CScriptVar(l->tkStr)); +// funcVar->addChildNoDup(l->tkStr); l->match(LEX_ID); if (l->tk!=')') l->match(','); } l->match(')'); + funcVar->addChild("length", new CScriptVar(idx)); } void CTinyJS::addNative(const string &funcDesc, JSCallback ptr, void *userdata) { CScriptVar *funcVar = addNative(funcDesc); @@ -1617,11 +1622,13 @@ CScriptVarSmartLink CTinyJS::factor(bool &execute) { else functionRoot->addChildNoDup("this", new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT)); // always add a this-Object // grab in all parameters + CScriptVarLink *arguments_proto = a->var->findChild("arguments"); + int length_proto = arguments_proto ? arguments_proto->var->getChildren() : 0; CScriptVar *arguments = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT); int arguments_idx = 0; - for(SCRIPTVAR_CHILDS::iterator it = a->var->Childs.begin(); it != a->var->Childs.end(); ++it) { + for(; l->tk!=')' || arguments_idxtk!=')') { - CScriptVarSmartLink value; try { value = assignment(execute); path += value->var->getString(); @@ -1630,49 +1637,30 @@ CScriptVarSmartLink CTinyJS::factor(bool &execute) { delete functionRoot; throw e; } - if (execute) { - if (value->var->isBasic()) { - // pass by value - functionRoot->addChild( it->first, arguments->addChild(int2string(arguments_idx++) , value->var->deepCopy())->var); - } else { - // pass by reference - functionRoot->addChild(it->first, arguments->addChild(int2string(arguments_idx++), value->var)->var); - } - } - } else { - functionRoot->addChild(it->first, new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_UNDEFINED)); - } - if (l->tk!=')') { l->match(','); path+=','; } - } - while(l->tk!=')') { - CScriptVarSmartLink value; - try { - value = assignment(execute); - path += value->var->getString(); - } catch (CScriptException *e) { - delete arguments; - delete functionRoot; - throw e; - } + + } else + value = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_UNDEFINED); if (execute) { if (value->var->isBasic()) { // pass by value - arguments->addChild(int2string(arguments_idx++) , value->var->deepCopy()); + value = arguments->addChild(int2string(arguments_idx) , value->var->deepCopy()); } else { // pass by reference - arguments->addChild(int2string(arguments_idx++), value->var); + value = arguments->addChild(int2string(arguments_idx), value->var); } + CScriptVarLink *argument_name; + if(arguments_proto && (argument_name = arguments_proto->var->findChild(int2string(arguments_idx)))) + functionRoot->addChild(argument_name->var->getString(), value->var); } if (l->tk!=')') { l->match(','); path+=','; } } - l->match(')'); path+=')'; - arguments->addChild("length", new CScriptVar(arguments_idx)); - functionRoot->addChild("arguments", arguments); // setup a return variable CScriptVarLink *returnVar = 0; if(execute) { + arguments->addChild("length", new CScriptVar(arguments_idx)); + functionRoot->addChild("arguments", arguments); int old_function_runtimeFlags = runtimeFlags; // save runtimFlags runtimeFlags &= ~RUNTIME_LOOP_MASK; // clear LOOP-Flags // execute function! From 3db18be5816e0b88aa3ba381215aa35bdf77791f Mon Sep 17 00:00:00 2001 From: armin Date: Tue, 21 Sep 2010 19:33:08 +0000 Subject: [PATCH 17/43] fixed: again memoryleaks --- TinyJS.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/TinyJS.cpp b/TinyJS.cpp index 44feb33..cdf8ce0 100755 --- a/TinyJS.cpp +++ b/TinyJS.cpp @@ -142,7 +142,7 @@ inline CScriptVarSmartLink::CScriptVarSmartLink () : link(0){} inline CScriptVarSmartLink::CScriptVarSmartLink (CScriptVarLink *Link) : link(Link) {} // this constructor replace "CScriptVarLink *link=0 ; CREATE_LINK(link, Var);" -inline CScriptVarSmartLink::CScriptVarSmartLink (CScriptVar *Var) { +inline CScriptVarSmartLink::CScriptVarSmartLink (CScriptVar *Var) : link(0) { *this = Var; } // this operator replace "CREATE_LINK(link, Var);" @@ -163,10 +163,11 @@ inline CScriptVarSmartLink::~CScriptVarSmartLink () { } // the copy-stuff has a special thing - // when copying a SmartLink to an other then the right hand side will lost your link -inline CScriptVarSmartLink::CScriptVarSmartLink (const CScriptVarSmartLink &Link) { +inline CScriptVarSmartLink::CScriptVarSmartLink (const CScriptVarSmartLink &Link) : link(0) { *this = Link; } inline CScriptVarSmartLink &CScriptVarSmartLink::operator = (const CScriptVarSmartLink &Link) { + if(link && !link->owned) delete link; link = Link.link; ((CScriptVarSmartLink &)Link).link = 0; // explicit cast to a non const ref return *this; From 5c8be35dbd4b9707057acaff7690a3fe7132a575 Mon Sep 17 00:00:00 2001 From: armin Date: Fri, 24 Sep 2010 14:24:50 +0000 Subject: [PATCH 18/43] added: Added a two-pass-mode. An unusual thing in JavaScript is that you can refer to a variable declared later, without getting an exception. But this makes necessary do interprete the code in two passes. You can control the pass-mode over the class CTinyJS-constructor. --- TinyJS.cpp | 604 +++++++++++++++++++++++++++++++---------------------- TinyJS.h | 55 +++-- 2 files changed, 402 insertions(+), 257 deletions(-) diff --git a/TinyJS.cpp b/TinyJS.cpp index cdf8ce0..d78e773 100755 --- a/TinyJS.cpp +++ b/TinyJS.cpp @@ -713,6 +713,9 @@ CScriptLex *CScriptLex::getSubLex(int lastPosition) { else return new CScriptLex( this, lastPosition, dataEnd ); } +int CScriptLex::getDataPos() { + return dataPos; +} string CScriptLex::getPosition(int pos) { if (pos<0) pos=tokenLastEnd; @@ -767,6 +770,7 @@ CScriptVarLink::~CScriptVarLink() { } void CScriptVarLink::replaceWith(CScriptVar *newVar) { + if(!newVar) newVar = new CScriptVar(); CScriptVar *oldVar = var; var = newVar->ref(); oldVar->unref(); @@ -1400,7 +1404,9 @@ int CScriptVar::getRefs() { // ----------------------------------------------------------------------------------- CSCRIPT -CTinyJS::CTinyJS() { +CTinyJS::CTinyJS(bool TwoPass, bool TwoPassEval) { + twoPass=TwoPass; + twoPassEval=TwoPassEval; l = NULL; runtimeFlags = 0; root = (new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT))->ref(); @@ -1454,6 +1460,20 @@ CScriptVarLink CTinyJS::evaluateComplex(const string &code) { CScriptVarSmartLink v; try { bool execute = true; + bool noexecute = false; + SET_RUNTIME_PASS_SINGLE; + if(twoPass) + { + printf("starte Pass 1\n"); + SET_RUNTIME_PASS_TWO_1; + do { + statement(noexecute); + while (l->tk==';') l->match(';'); // skip empty statements + } while (l->tk!=LEX_EOF); + l->reset(); + SET_RUNTIME_PASS_TWO_2; + printf("starte Pass 2\n"); + } do { v = statement(execute); while (l->tk==';') l->match(';'); // skip empty statements @@ -1489,7 +1509,6 @@ void CTinyJS::parseFunctionArguments(CScriptVar *funcVar) { int idx = 0; while (l->tk!=')') { arguments->addChild(int2string(idx++), new CScriptVar(l->tkStr)); -// funcVar->addChildNoDup(l->tkStr); l->match(LEX_ID); if (l->tk!=')') l->match(','); } @@ -1532,22 +1551,52 @@ CScriptVar *CTinyJS::addNative(const string &funcDesc) { return funcVar; } -CScriptVarLink *CTinyJS::parseFunctionDefinition() { - // actually parse a function... - l->match(LEX_R_FUNCTION); - string funcName = TINYJS_TEMP_NAME; - /* we can have functions without names */ - if (l->tk==LEX_ID) { - funcName = l->tkStr; - l->match(LEX_ID); - } - CScriptVarLink *funcVar = new CScriptVarLink(new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_FUNCTION), funcName); - parseFunctionArguments(funcVar->var); - int funcBegin = l->tokenStart; - bool noexecute = false; - block(noexecute); - funcVar->var->data = l->getSubString(funcBegin); - return funcVar; +CScriptVarSmartLink CTinyJS::parseFunctionDefinition() { + // actually parse a function... + int funcOffset = l->getDataPos(); + l->match(LEX_R_FUNCTION); + string funcName = TINYJS_TEMP_NAME; + /* we can have functions without names */ + if (l->tk==LEX_ID) { + funcName = l->tkStr; + l->match(LEX_ID); + } + funcOffset -= l->getDataPos(); + CScriptVarSmartLink funcVar = new CScriptVarLink(new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_FUNCTION), funcName); + parseFunctionArguments(funcVar->var); + funcOffset += l->getDataPos(); + bool noexecute = false; + if(IS_RUNTIME_PASS_TWO_2) { + SAVE_RUNTIME_PASS; + SET_RUNTIME_PASS_SINGLE; + block(noexecute); + RESTORE_RUNTIME_PASS; + } else if(IS_RUNTIME_PASS_TWO_1) { + int funcBegin = l->tokenStart; + CScriptVar *locale = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT); + CScriptVarLink *anonymous_link = locale->addChild(TINYJS_ANONYMOUS_VAR, new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT)); + CScriptVarLink *funcOffset_link = anonymous_link->var->addChild("funcOffset", new CScriptVar(funcOffset)); + CScriptVarLink *locale_link = funcVar->var->addChild(TINYJS_LOKALE_VAR, locale); + locale_link->hidden = locale_link->dontEnumerable = true; + scopes.push_back(locale); + try { + block(noexecute); + } catch(CScriptException *e) { + scopes.pop_back(); + throw e; + } + scopes.pop_back(); + funcVar->var->data = l->getSubString(funcBegin); + if(anonymous_link->var->Childs.size() == 1) + locale->removeLink(anonymous_link); + else + anonymous_link->var->removeLink(funcOffset_link); + } else { + int funcBegin = l->tokenStart; + block(noexecute); + funcVar->var->data = l->getSubString(funcBegin); + } + return funcVar; } class CScriptEvalException { @@ -1599,7 +1648,11 @@ CScriptVarSmartLink CTinyJS::factor(bool &execute) { if (execute && !a) { /* Variable doesn't exist! JavaScript says we should create it * (we won't add it here. This is done in the assignment operator)*/ - a = new CScriptVarLink(new CScriptVar(), l->tkStr); + if(l->tkStr == "this") { + a = new CScriptVarLink(this->root, l->tkStr); + parent = this->root; + } else + a = new CScriptVarLink(new CScriptVar(), l->tkStr); } string path = a->name; if(op==LEX_ID) @@ -1617,11 +1670,19 @@ CScriptVarSmartLink CTinyJS::factor(bool &execute) { } l->match('('); path += '('; // create a new symbol table entry for execution of this function - CScriptVar *functionRoot = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_FUNCTION); + CScriptVarSmartLink functionRoot(new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_FUNCTION)); if (parent) - functionRoot->addChildNoDup("this", parent); - else - functionRoot->addChildNoDup("this", new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT)); // always add a this-Object + functionRoot->var->addChildNoDup("this", parent); +// else +// functionRoot->var->addChildNoDup("this", this->root);//new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT)); // always add a this-Object +// functionRoot->var->addChildNoDup("this", new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT)); // always add a this-Object + // insert locale + CScriptVarLink *__locale__ = a->var->findChild(TINYJS_LOKALE_VAR); + if(__locale__) { + for(SCRIPTVAR_CHILDS::iterator it = __locale__->var->Childs.begin(); it != __locale__->var->Childs.end(); ++it) { + functionRoot->var->addChild(it->first , it->second->var->deepCopy()); + } + } // grab in all parameters CScriptVarLink *arguments_proto = a->var->findChild("arguments"); int length_proto = arguments_proto ? arguments_proto->var->getChildren() : 0; @@ -1635,47 +1696,47 @@ CScriptVarSmartLink CTinyJS::factor(bool &execute) { path += value->var->getString(); } catch (CScriptException *e) { delete arguments; - delete functionRoot; throw e; } } else value = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_UNDEFINED); if (execute) { + string arguments_idx_str; int2string(arguments_idx, arguments_idx_str); if (value->var->isBasic()) { // pass by value - value = arguments->addChild(int2string(arguments_idx) , value->var->deepCopy()); + value = arguments->addChild(arguments_idx_str , value->var->deepCopy()); } else { // pass by reference - value = arguments->addChild(int2string(arguments_idx), value->var); + value = arguments->addChild(arguments_idx_str, value->var); } CScriptVarLink *argument_name; - if(arguments_proto && (argument_name = arguments_proto->var->findChild(int2string(arguments_idx)))) - functionRoot->addChild(argument_name->var->getString(), value->var); + if(arguments_proto && (argument_name = arguments_proto->var->findChild(arguments_idx_str))) + functionRoot->var->addChildNoDup(argument_name->var->getString(), value->var); } if (l->tk!=')') { l->match(','); path+=','; } } l->match(')'); path+=')'; + arguments->addChild("length", new CScriptVar(arguments_idx)); + functionRoot->var->addChild("arguments", arguments); // setup a return variable - CScriptVarLink *returnVar = 0; + CScriptVarSmartLink returnVar; if(execute) { - arguments->addChild("length", new CScriptVar(arguments_idx)); - functionRoot->addChild("arguments", arguments); int old_function_runtimeFlags = runtimeFlags; // save runtimFlags runtimeFlags &= ~RUNTIME_LOOP_MASK; // clear LOOP-Flags // execute function! // add the function's execute space to the symbol table so we can recurse - CScriptVarLink *returnVarLink = functionRoot->addChild(TINYJS_RETURN_VAR); - scopes.push_back(functionRoot); +// CScriptVarLink *returnVarLink = functionRoot->addChild(TINYJS_RETURN_VAR); + scopes.push_back(functionRoot->var); try { if (a->var->isNative()) { ASSERT(a->var->jsCallback); try { if (a->var->isNative_ClassMemberFnc()) - (*a->var->jsCallbackClass)(functionRoot, a->var->jsCallbackUserData); + (*a->var->jsCallbackClass)(functionRoot->var, a->var->jsCallbackUserData); else - a->var->jsCallback(functionRoot, a->var->jsCallbackUserData); + a->var->jsCallback(functionRoot->var, a->var->jsCallbackUserData); // special thinks for eval() // set execute to false when runtimeFlags setted runtimeFlags = old_function_runtimeFlags | (runtimeFlags & RUNTIME_THROW); // restore runtimeFlags if(runtimeFlags & RUNTIME_THROW) { @@ -1716,7 +1777,12 @@ CScriptVarSmartLink CTinyJS::factor(bool &execute) { CScriptLex *newLex = new CScriptLex(a->var->getString()); l = newLex; try { + SET_RUNTIME_CANRETURN; + SAVE_RUNTIME_PASS; + if(__locale__) + SET_RUNTIME_PASS_TWO_2; block(execute); + RESTORE_RUNTIME_PASS; // because return will probably have called this, and set execute to false runtimeFlags = old_function_runtimeFlags | (runtimeFlags & RUNTIME_THROW); // restore runtimeFlags if(!(runtimeFlags & RUNTIME_THROW)) { @@ -1732,20 +1798,21 @@ CScriptVarSmartLink CTinyJS::factor(bool &execute) { } } catch (CScriptException *e) { scopes.pop_back(); - delete functionRoot; throw e; } scopes.pop_back(); /* get the real return var before we remove it from our function */ - returnVar = new CScriptVarLink(returnVarLink->var); - functionRoot->removeLink(returnVarLink); + if(returnVar = functionRoot->var->findChild(TINYJS_RETURN_VAR)) + returnVar = new CScriptVarLink(returnVar->var); +// returnVar = functionRoot->getReturnVar(); +// functionRoot->removeLink(returnVarLink); } - delete functionRoot; if (returnVar) a = returnVar; else - a = new CScriptVarLink(new CScriptVar()); + a = new CScriptVarLink( + new CScriptVar()); } else { // function, but not executing - just parse args and be done l->match('('); @@ -1754,9 +1821,9 @@ CScriptVarSmartLink CTinyJS::factor(bool &execute) { if (l->tk!=')') l->match(','); } l->match(')'); - if (l->tk == '{') { - block(execute); - } +// if (l->tk == '{') { +// block(execute, SINGLE_PASS); +// } } } else if (l->tk == '.' || l->tk == '[') { // ------------------------------------- Record Access string name; @@ -1852,10 +1919,27 @@ CScriptVarSmartLink CTinyJS::factor(bool &execute) { return new CScriptVarLink(contents); } if (l->tk==LEX_R_FUNCTION) { - CScriptVarLink *funcVar = parseFunctionDefinition(); - if (funcVar->name != TINYJS_TEMP_NAME) - TRACE("Functions not defined at statement-level are not meant to have a name"); - return funcVar; + CScriptVarLink *funcOffset = 0; + CScriptVarLink *anonymous = scopes.back()->findChild(TINYJS_ANONYMOUS_VAR); + if(anonymous) funcOffset = anonymous->var->findChild("funcOffset"); + string anonymous_name = int2string(l->getDataPos()-(funcOffset?funcOffset->var->getInt():0)); + CScriptVarSmartLink funcVar = parseFunctionDefinition(); + this->runtimeFlags; +// funcName = anonymous ? int2string(l->tokenStart) : l->tkStr; + + funcVar->name = TINYJS_TEMP_NAME; // allways anonymous + if(IS_RUNTIME_PASS_TWO_1) + { + CScriptVarLink *anonymous = scopes.back()->findChildOrCreate(TINYJS_ANONYMOUS_VAR, SCRIPTVAR_OBJECT); + anonymous->var->addChild(anonymous_name, funcVar->var); + } + if(IS_RUNTIME_PASS_TWO_2) + { + CScriptVarLink *anonymous = scopes.back()->findChild(TINYJS_ANONYMOUS_VAR); + if(anonymous) + funcVar->replaceWith(anonymous->var->findChild(anonymous_name)); + } + return funcVar; } if (l->tk==LEX_R_NEW) { // new -> create a new object @@ -2207,7 +2291,7 @@ CScriptVarSmartLink CTinyJS::base(bool &execute) { } void CTinyJS::block(bool &execute) { l->match('{'); - if (execute) { + if (execute || IS_RUNTIME_PASS_TWO_1) { while (l->tk && l->tk!='}') statement(execute); l->match('}'); @@ -2239,7 +2323,7 @@ CScriptVarSmartLink CTinyJS::statement(bool &execute) { CScriptVarSmartLink a; string var = l->tkStr; l->match(LEX_ID); - if (execute) { + if (execute || IS_RUNTIME_PASS_TWO_1) { a = scopes.back()->findChildOrCreate(var); a->dontDelete = true; }// sort out initialiser @@ -2271,134 +2355,150 @@ CScriptVarSmartLink CTinyJS::statement(bool &execute) { // We do repetition by pulling out the string representing our statement // there's definitely some opportunity for optimisation here l->match(LEX_R_DO); - bool loopCond = true; - bool old_execute = execute; - int old_loop_runtimeFlags = runtimeFlags & RUNTIME_LOOP_MASK; - runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | RUNTIME_CANBREAK | RUNTIME_CANCONTINUE; - int whileBodyStart = l->tokenStart; - int whileCondStart = 0; - CScriptLex *oldLex = NULL; - CScriptLex *whileBody = NULL; - CScriptLex *whileCond = NULL; - int loopCount = TINYJS_LOOP_MAX_ITERATIONS; - while (loopCond && loopCount-->0) + if(IS_RUNTIME_PASS_TWO_1) { - if(whileBody) - { - whileBody->reset(); - l = whileBody; - } statement(execute); - if(!whileBody) whileBody = l->getSubLex(whileBodyStart); - if(old_execute && !execute) + l->match(LEX_R_WHILE); + l->match('('); + base(execute); + l->match(')'); + l->match(';'); + } else { + bool loopCond = true; + bool old_execute = execute; + int old_loop_runtimeFlags = runtimeFlags & RUNTIME_LOOP_MASK; + runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | RUNTIME_CANBREAK | RUNTIME_CANCONTINUE; + int whileBodyStart = l->tokenStart; + int whileCondStart = 0; + CScriptLex *oldLex = NULL; + CScriptLex *whileBody = NULL; + CScriptLex *whileCond = NULL; + int loopCount = TINYJS_LOOP_MAX_ITERATIONS; + while (loopCond && loopCount-->0) { - // break or continue - if(runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE)) + if(whileBody) { - execute = old_execute; - if(runtimeFlags & RUNTIME_BREAK) - loopCond = false; - runtimeFlags &= ~(RUNTIME_BREAK | RUNTIME_CONTINUE); + whileBody->reset(); + l = whileBody; + } + statement(execute); + if(!whileBody) whileBody = l->getSubLex(whileBodyStart); + if(old_execute && !execute) + { + // break or continue + if(runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE)) + { + execute = old_execute; + if(runtimeFlags & RUNTIME_BREAK) + loopCond = false; + runtimeFlags &= ~(RUNTIME_BREAK | RUNTIME_CONTINUE); + } + // other stuff e.g return, throw } - // other stuff e.g return, throw - } - if(!whileCond) - { - l->match(LEX_R_WHILE); - l->match('('); - whileCondStart = l->tokenStart; - } - else - { - whileCond->reset(); - l = whileCond; - } - CScriptVarSmartLink cond = base(execute); - loopCond = execute && cond->var->getBool(); - if(!whileCond) - { - whileCond = l->getSubLex(whileCondStart); - l->match(')'); - l->match(';'); - oldLex = l; + if(!whileCond) + { + l->match(LEX_R_WHILE); + l->match('('); + whileCondStart = l->tokenStart; + } + else + { + whileCond->reset(); + l = whileCond; + } + CScriptVarSmartLink cond = base(execute); + loopCond = execute && cond->var->getBool(); + if(!whileCond) + { + whileCond = l->getSubLex(whileCondStart); + l->match(')'); + l->match(';'); + oldLex = l; + } } - } - runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | old_loop_runtimeFlags; - l = oldLex; - delete whileCond; - delete whileBody; + runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | old_loop_runtimeFlags; + l = oldLex; + delete whileCond; + delete whileBody; - if (loopCount<=0) { - root->trace(); - TRACE("WHILE Loop exceeded %d iterations at %s\n", TINYJS_LOOP_MAX_ITERATIONS, l->getPosition(l->tokenLastEnd).c_str()); - throw new CScriptException("LOOP_ERROR"); + if (loopCount<=0) { + root->trace(); + TRACE("WHILE Loop exceeded %d iterations at %s\n", TINYJS_LOOP_MAX_ITERATIONS, l->getPosition(l->tokenLastEnd).c_str()); + throw new CScriptException("LOOP_ERROR"); + } } } else if (l->tk==LEX_R_WHILE) { // We do repetition by pulling out the string representing our statement // there's definitely some opportunity for optimisation here l->match(LEX_R_WHILE); l->match('('); - int whileCondStart = l->tokenStart; - bool noexecute = false; - CScriptVarSmartLink cond = base(execute); - bool loopCond = execute && cond->var->getBool(); - CScriptLex *whileCond = l->getSubLex(whileCondStart); - l->match(')'); - int whileBodyStart = l->tokenStart; + if(IS_RUNTIME_PASS_TWO_1) { + base(execute); + l->match(')'); + statement(execute); + } else { + int whileCondStart = l->tokenStart; + bool noexecute = false; + CScriptVarSmartLink cond = base(execute); + bool loopCond = execute && cond->var->getBool(); + CScriptLex *whileCond = l->getSubLex(whileCondStart); + l->match(')'); + int whileBodyStart = l->tokenStart; - bool old_execute = execute; - int old_loop_runtimeFlags = runtimeFlags & RUNTIME_LOOP_MASK; - runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | RUNTIME_CANBREAK | RUNTIME_CANCONTINUE; - statement(loopCond ? execute : noexecute); - CScriptLex *whileBody = l->getSubLex(whileBodyStart); - CScriptLex *oldLex = l; - if(loopCond && old_execute != execute) - { - // break or continue - if(runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE)) + bool old_execute = execute; + int old_loop_runtimeFlags = runtimeFlags & RUNTIME_LOOP_MASK; + runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | RUNTIME_CANBREAK | RUNTIME_CANCONTINUE; + statement(loopCond ? execute : noexecute); + CScriptLex *whileBody = l->getSubLex(whileBodyStart); + CScriptLex *oldLex = l; + if(loopCond && old_execute != execute) { - execute = old_execute; - if(runtimeFlags & RUNTIME_BREAK) - loopCond = false; - runtimeFlags &= ~(RUNTIME_BREAK | RUNTIME_CONTINUE); + // break or continue + if(runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE)) + { + execute = old_execute; + if(runtimeFlags & RUNTIME_BREAK) + loopCond = false; + runtimeFlags &= ~(RUNTIME_BREAK | RUNTIME_CONTINUE); + } + // other stuff e.g return, throw } - // other stuff e.g return, throw - } - int loopCount = TINYJS_LOOP_MAX_ITERATIONS; - while (loopCond && loopCount-->0) { - whileCond->reset(); - l = whileCond; - cond = base(execute); - loopCond = execute && cond->var->getBool(); - if (loopCond) { - whileBody->reset(); - l = whileBody; - statement(execute); - if(!execute) - { - // break or continue - if(runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE)) + int loopCount = TINYJS_LOOP_MAX_ITERATIONS; + while (loopCond && loopCount-->0) { + whileCond->reset(); + l = whileCond; + cond = base(execute); + loopCond = execute && cond->var->getBool(); + if (loopCond) { + whileBody->reset(); + l = whileBody; + statement(execute); + if(!execute) { - execute = old_execute; - if(runtimeFlags & RUNTIME_BREAK) - loopCond = false; - runtimeFlags &= ~(RUNTIME_BREAK | RUNTIME_CONTINUE); + // break or continue + if(runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE)) + { + execute = old_execute; + if(runtimeFlags & RUNTIME_BREAK) + loopCond = false; + runtimeFlags &= ~(RUNTIME_BREAK | RUNTIME_CONTINUE); + } + // other stuff e.g return, throw } - // other stuff e.g return, throw } } - } - runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | old_loop_runtimeFlags; - l = oldLex; - delete whileCond; - delete whileBody; + runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | old_loop_runtimeFlags; + l = oldLex; + delete whileCond; + delete whileBody; - if (loopCount<=0) { - root->trace(); - TRACE("WHILE Loop exceeded %d iterations at %s\n", TINYJS_LOOP_MAX_ITERATIONS, l->getPosition(l->tokenLastEnd).c_str()); - throw new CScriptException("LOOP_ERROR"); + if (loopCount<=0) { + root->trace(); + TRACE("WHILE Loop exceeded %d iterations at %s\n", TINYJS_LOOP_MAX_ITERATIONS, l->getPosition(l->tokenLastEnd).c_str()); + throw new CScriptException("LOOP_ERROR"); + } } } else if (l->tk==LEX_R_FOR) { l->match(LEX_R_FOR); @@ -2434,31 +2534,35 @@ CScriptVarSmartLink CTinyJS::statement(bool &execute) { real_for_var = root->addChildNoDup(for_var->name, for_var->var); for_var = real_for_var; } - int forBodyStart = l->tokenStart; - bool old_execute = execute; - int old_loop_runtimeFlags = runtimeFlags & RUNTIME_LOOP_MASK; - runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | RUNTIME_CANBREAK | RUNTIME_CANCONTINUE; - for(SCRIPTVAR_CHILDS::iterator it = for_in_var->var->Childs.begin(); execute && it != for_in_var->var->Childs.end(); ++it) { - if (for_each_in) - for_var->replaceWith(it->second->var); - else - for_var->replaceWith(new CScriptVar(it->first)); - l->reset(forBodyStart); + if(IS_RUNTIME_PASS_TWO_1) statement(execute); - if(!execute) - { - // break or continue - if(runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE)) + else { + int forBodyStart = l->tokenStart; + bool old_execute = execute; + int old_loop_runtimeFlags = runtimeFlags & RUNTIME_LOOP_MASK; + runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | RUNTIME_CANBREAK | RUNTIME_CANCONTINUE; + for(SCRIPTVAR_CHILDS::iterator it = for_in_var->var->Childs.begin(); execute && it != for_in_var->var->Childs.end(); ++it) { + if (for_each_in) + for_var->replaceWith(it->second->var); + else + for_var->replaceWith(new CScriptVar(it->first)); + l->reset(forBodyStart); + statement(execute); + if(!execute) { - execute = old_execute; - if(runtimeFlags & RUNTIME_BREAK) - break; - runtimeFlags &= ~(RUNTIME_BREAK | RUNTIME_CONTINUE); + // break or continue + if(runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE)) + { + execute = old_execute; + if(runtimeFlags & RUNTIME_BREAK) + break; + runtimeFlags &= ~(RUNTIME_BREAK | RUNTIME_CONTINUE); + } + // other stuff e.g return, throw } - // other stuff e.g return, throw } + runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | old_loop_runtimeFlags; } - runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | old_loop_runtimeFlags; } else { statement(execute); // initialisation //l->match(';'); @@ -2467,8 +2571,7 @@ CScriptVarSmartLink CTinyJS::statement(bool &execute) { bool cond_empty = true; bool loopCond = true; // Empty Condition -->always true CScriptVarSmartLink cond; - if(l->tk != ';') - { + if(l->tk != ';') { cond_empty = false; cond = base(execute); // condition loopCond = execute && cond->var->getBool(); @@ -2485,74 +2588,75 @@ CScriptVarSmartLink CTinyJS::statement(bool &execute) { forIter = l->getSubLex(forIterStart); } l->match(')'); - int forBodyStart = l->tokenStart; - bool old_execute = execute; - int old_loop_runtimeFlags = runtimeFlags & RUNTIME_LOOP_MASK; - runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | RUNTIME_CANBREAK | RUNTIME_CANCONTINUE; - statement(loopCond ? execute : noexecute); - CScriptLex *forBody = l->getSubLex(forBodyStart); - CScriptLex *oldLex = l; - if(loopCond && old_execute != execute) - { - // break or continue - if(runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE)) - { - execute = old_execute; - if(runtimeFlags & RUNTIME_BREAK) - loopCond = false; - runtimeFlags &= ~(RUNTIME_BREAK | RUNTIME_CONTINUE); + if(IS_RUNTIME_PASS_TWO_1) { + delete forCond; + delete forIter; + statement(execute); + } else { + int forBodyStart = l->tokenStart; + bool old_execute = execute; + int old_loop_runtimeFlags = runtimeFlags & RUNTIME_LOOP_MASK; + runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | RUNTIME_CANBREAK | RUNTIME_CANCONTINUE; + statement(loopCond ? execute : noexecute); + CScriptLex *forBody = l->getSubLex(forBodyStart); + CScriptLex *oldLex = l; + if(loopCond && old_execute != execute) { + // break or continue + if(runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE)) { + execute = old_execute; + if(runtimeFlags & RUNTIME_BREAK) + loopCond = false; + runtimeFlags &= ~(RUNTIME_BREAK | RUNTIME_CONTINUE); + } + // other stuff e.g return, throw } - // other stuff e.g return, throw - } - if (loopCond && !iter_empty) { - forIter->reset(); - l = forIter; - base(execute); - } - int loopCount = TINYJS_LOOP_MAX_ITERATIONS; - while (execute && loopCond && loopCount-->0) { - if(!cond_empty) - { - forCond->reset(); - l = forCond; - cond = base(execute); - loopCond = cond->var->getBool(); + if (loopCond && !iter_empty) { + forIter->reset(); + l = forIter; + base(execute); } - if (execute && loopCond) { - forBody->reset(); - l = forBody; - statement(execute); - if(!execute) - { - // break or continue; - if(runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE)) - { - execute = old_execute; - if(runtimeFlags & RUNTIME_BREAK) - loopCond = false; - runtimeFlags &= ~(RUNTIME_BREAK | RUNTIME_CONTINUE); + int loopCount = TINYJS_LOOP_MAX_ITERATIONS; + while (execute && loopCond && loopCount-->0) { + if(!cond_empty) { + forCond->reset(); + l = forCond; + cond = base(execute); + loopCond = cond->var->getBool(); + } + if (execute && loopCond) { + forBody->reset(); + l = forBody; + statement(execute); + if(!execute) { + // break or continue; + if(runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE)) { + execute = old_execute; + if(runtimeFlags & RUNTIME_BREAK) + loopCond = false; + runtimeFlags &= ~(RUNTIME_BREAK | RUNTIME_CONTINUE); + } + // return nothing to do } - // return nothing to do + } + if (execute && loopCond && !iter_empty) { + forIter->reset(); + l = forIter; + base(execute); } } - if (execute && loopCond && !iter_empty) { - forIter->reset(); - l = forIter; - base(execute); + runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | old_loop_runtimeFlags; + l = oldLex; + delete forCond; + delete forIter; + delete forBody; + if (loopCount<=0) { + root->trace(); + TRACE("FOR Loop exceeded %d iterations at %s\n", TINYJS_LOOP_MAX_ITERATIONS, l->getPosition(l->tokenLastEnd).c_str()); + throw new CScriptException("LOOP_ERROR"); } } - runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | old_loop_runtimeFlags; - l = oldLex; - delete forCond; - delete forIter; - delete forBody; - if (loopCount<=0) { - root->trace(); - TRACE("FOR Loop exceeded %d iterations at %s\n", TINYJS_LOOP_MAX_ITERATIONS, l->getPosition(l->tokenLastEnd).c_str()); - throw new CScriptException("LOOP_ERROR"); - } - } + } } else if (l->tk==LEX_R_BREAK) { l->match(LEX_R_BREAK); if(execute) { @@ -2583,9 +2687,8 @@ CScriptVarSmartLink CTinyJS::statement(bool &execute) { if (l->tk != ';') result = base(execute); if (execute) { - CScriptVarSmartLink resultVar = scopes.back()->findChild(TINYJS_RETURN_VAR); - if (resultVar) { - if(result) resultVar << result; + if (IS_RUNTIME_CANRETURN) { + if(result->var) scopes.back()->setReturnVar(result->var); } else throw new CScriptException("'return' statement, but not in a function."); execute = false; @@ -2593,7 +2696,7 @@ CScriptVarSmartLink CTinyJS::statement(bool &execute) { l->match(';'); } else if (l->tk==LEX_R_FUNCTION) { CScriptVarSmartLink funcVar = parseFunctionDefinition(); - if (execute) { + if ((execute && IS_RUNTIME_PASS_SINGLE) || IS_RUNTIME_PASS_TWO_1) { if (funcVar->name == TINYJS_TEMP_NAME) throw new CScriptException("Functions defined at statement-level are meant to have a name."); else @@ -2638,14 +2741,13 @@ CScriptVarSmartLink CTinyJS::statement(bool &execute) { delete catchScope; } else { bool noexecute = false; - this->block(noexecute); + block(noexecute); } } if(l->tk == LEX_R_FINALLY) { l->match(LEX_R_FINALLY); bool finally_execute = true; block(isThrow ? finally_execute : execute); - ; } } else if (l->tk==LEX_R_THROW) { int tokenStart = l->tokenStart; @@ -2780,8 +2882,21 @@ void CTinyJS::scEval(CScriptVar *c, void *data) { CScriptVarSmartLink returnVar; runtimeFlags |= RUNTIME_CANBREAK | RUNTIME_CANCONTINUE; - bool execute = true; + SAVE_RUNTIME_PASS; + SET_RUNTIME_PASS_SINGLE; try { + bool execute = true; + bool noexecute = false; + if(twoPassEval) + { + SET_RUNTIME_PASS_TWO_1; + do { + statement(noexecute); + while (l->tk==';') l->match(';'); // skip empty statements + } while (l->tk!=LEX_EOF); + l->reset(); + SET_RUNTIME_PASS_TWO_2; + } do { returnVar = statement(execute); while (l->tk==';') l->match(';'); // skip empty statements @@ -2793,7 +2908,8 @@ void CTinyJS::scEval(CScriptVar *c, void *data) { } delete l; l = oldLex; - + RESTORE_RUNTIME_PASS; + this->scopes.push_back(scEvalScope); // restore Scopes; if(returnVar) c->setReturnVar(returnVar->var); diff --git a/TinyJS.h b/TinyJS.h index 15bfe06..5042f6d 100755 --- a/TinyJS.h +++ b/TinyJS.h @@ -140,19 +140,44 @@ enum SCRIPTVAR_FLAGS { SCRIPTVAR_NATIVE_MFNC, }; enum RUNTIME_FLAGS { - RUNTIME_CANBREAK = 1<<0, - RUNTIME_BREAK = 1<<1, - RUNTIME_CANCONTINUE = 1<<2, - RUNTIME_CONTINUE = 1<<3, - RUNTIME_NEW = 1<<4, - RUNTIME_CANTHROW = 1<<5, - RUNTIME_THROW = 1<<6, + RUNTIME_CANRETURN = 1<<0, + + RUNTIME_CANBREAK = 1<<1, + RUNTIME_BREAK = 1<<2, RUNTIME_BREAK_MASK = RUNTIME_CANBREAK | RUNTIME_BREAK, + + RUNTIME_CANCONTINUE = 1<<3, + RUNTIME_CONTINUE = 1<<4, RUNTIME_LOOP_MASK = RUNTIME_BREAK_MASK | RUNTIME_CANCONTINUE | RUNTIME_CONTINUE, + + RUNTIME_NEW = 1<<5, + + RUNTIME_CANTHROW = 1<<6, + RUNTIME_THROW = 1<<7, RUNTIME_THROW_MASK = RUNTIME_CANTHROW | RUNTIME_THROW, + + RUNTIME_PASS_1 = 1<<8, + RUNTIME_PASS_2 = 1<<9, + RUNTIME_PASS_MASK = RUNTIME_PASS_1 | RUNTIME_PASS_2, }; +#define SAVE_RUNTIME_RETURN int old_return_runtimeFlags = runtimeFlags & RUNTIME_CANRETURN +#define RESTORE_RUNTIME_RETURN runtimeFlags = (runtimeFlags & ~RUNTIME_CANRETURN) | old_pass_runtimeFlags +#define SET_RUNTIME_CANRETURN runtimeFlags |= RUNTIME_CANRETURN +#define IS_RUNTIME_CANRETURN ((runtimeFlags & RUNTIME_CANRETURN) == RUNTIME_CANRETURN) + +#define SAVE_RUNTIME_PASS int old_pass_runtimeFlags = runtimeFlags & RUNTIME_PASS_MASK +#define RESTORE_RUNTIME_PASS runtimeFlags = (runtimeFlags & ~RUNTIME_PASS_MASK) | old_pass_runtimeFlags +#define SET_RUNTIME_PASS_SINGLE runtimeFlags = (runtimeFlags & ~RUNTIME_PASS_MASK) +#define IS_RUNTIME_PASS_SINGLE ((runtimeFlags & RUNTIME_PASS_MASK) == 0) +#define SET_RUNTIME_PASS_TWO_1 runtimeFlags = (runtimeFlags & ~RUNTIME_PASS_MASK) | RUNTIME_PASS_1 +#define IS_RUNTIME_PASS_TWO_1 ((runtimeFlags & RUNTIME_PASS_MASK) == RUNTIME_PASS_1) +#define SET_RUNTIME_PASS_TWO_2 runtimeFlags = (runtimeFlags & ~RUNTIME_PASS_MASK) | RUNTIME_PASS_2 +#define IS_RUNTIME_PASS_TWO_2 ((runtimeFlags & RUNTIME_PASS_MASK) == RUNTIME_PASS_2) + #define TINYJS_RETURN_VAR "return" +#define TINYJS_LOKALE_VAR "__locale__" +#define TINYJS_ANONYMOUS_VAR "__anonymous__" #define TINYJS_PROTOTYPE_CLASS "prototype" #define TINYJS_TEMP_NAME "" #define TINYJS_BLANK_DATA "" @@ -187,7 +212,7 @@ class CScriptLex std::string getSubString(int pos); ///< Return a sub-string from the given position up until right now CScriptLex *getSubLex(int lastPosition); ///< Return a sub-lexer from the given position up until right now - + int CScriptLex::getDataPos(); std::string getPosition(int pos=-1); ///< Return a string representing the position in lines and columns of the character pos given protected: @@ -215,8 +240,10 @@ class CScriptVarLink CScriptVar *var; CScriptVar *owner; // pointer to the owner CScriptVar struct { - bool owned :1; - bool dontDelete :1; + bool owned :1; + bool dontDelete :1; + bool dontEnumerable :1; + bool hidden :1; }; CScriptVarLink(CScriptVar *var, const std::string &name = TINYJS_TEMP_NAME); CScriptVarLink(const CScriptVarLink &link); ///< Copy constructor @@ -358,7 +385,7 @@ class CScriptVarSmartLink CScriptVarLink &operator *() { return *link; } CScriptVarSmartLink &operator << (CScriptVarSmartLink &Link); operator bool() { return link != 0; } - operator CScriptVarLink *() { return link; } +// operator CScriptVarLink *() { return link; } CScriptVarLink *&getLink() { return link; }; private: CScriptVarLink *link; @@ -366,7 +393,7 @@ class CScriptVarSmartLink class CTinyJS { public: - CTinyJS(); + CTinyJS(bool TwoPass=true, bool TwoPassEval=true); ~CTinyJS(); void execute(const std::string &code); @@ -421,6 +448,8 @@ class CTinyJS { CScriptVar *root; /// root of symbol table private: + bool twoPass; + bool twoPassEval; CScriptLex *l; /// current lexer int runtimeFlags; std::vector scopes; /// stack of scopes when parsing @@ -449,7 +478,7 @@ class CTinyJS { void block(bool &execute); CScriptVarSmartLink statement(bool &execute); // parsing utility functions - CScriptVarLink *parseFunctionDefinition(); + CScriptVarSmartLink parseFunctionDefinition(); void parseFunctionArguments(CScriptVar *funcVar); CScriptVarLink *findInScopes(const std::string &childName); ///< Finds a child, looking recursively up the scopes From 27027f8a913de2f0b05e79951fd2cbb0718ec709 Mon Sep 17 00:00:00 2001 From: armin Date: Fri, 24 Sep 2010 14:27:58 +0000 Subject: [PATCH 19/43] change: remove debug-message --- TinyJS.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/TinyJS.cpp b/TinyJS.cpp index d78e773..80c28dd 100755 --- a/TinyJS.cpp +++ b/TinyJS.cpp @@ -1464,7 +1464,6 @@ CScriptVarLink CTinyJS::evaluateComplex(const string &code) { SET_RUNTIME_PASS_SINGLE; if(twoPass) { - printf("starte Pass 1\n"); SET_RUNTIME_PASS_TWO_1; do { statement(noexecute); @@ -1472,7 +1471,6 @@ CScriptVarLink CTinyJS::evaluateComplex(const string &code) { } while (l->tk!=LEX_EOF); l->reset(); SET_RUNTIME_PASS_TWO_2; - printf("starte Pass 2\n"); } do { v = statement(execute); From 05e11230d0e4df2ef57af0f59938c4f4c0a31ae5 Mon Sep 17 00:00:00 2001 From: armin Date: Sat, 25 Sep 2010 15:43:46 +0000 Subject: [PATCH 20/43] changed: used now a CTinyJS-member for funcOffset instead of a Javascript var --- TinyJS.cpp | 15 +++++++-------- TinyJS.h | 1 + 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/TinyJS.cpp b/TinyJS.cpp index 80c28dd..3238005 100755 --- a/TinyJS.cpp +++ b/TinyJS.cpp @@ -1407,6 +1407,7 @@ int CScriptVar::getRefs() { CTinyJS::CTinyJS(bool TwoPass, bool TwoPassEval) { twoPass=TwoPass; twoPassEval=TwoPassEval; + funcOffset = 0; l = NULL; runtimeFlags = 0; root = (new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT))->ref(); @@ -1551,7 +1552,8 @@ CScriptVar *CTinyJS::addNative(const string &funcDesc) { CScriptVarSmartLink CTinyJS::parseFunctionDefinition() { // actually parse a function... - int funcOffset = l->getDataPos(); + int oldFuncOffset = funcOffset; + funcOffset = l->getDataPos(); l->match(LEX_R_FUNCTION); string funcName = TINYJS_TEMP_NAME; /* we can have functions without names */ @@ -1573,7 +1575,6 @@ CScriptVarSmartLink CTinyJS::parseFunctionDefinition() { int funcBegin = l->tokenStart; CScriptVar *locale = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT); CScriptVarLink *anonymous_link = locale->addChild(TINYJS_ANONYMOUS_VAR, new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT)); - CScriptVarLink *funcOffset_link = anonymous_link->var->addChild("funcOffset", new CScriptVar(funcOffset)); CScriptVarLink *locale_link = funcVar->var->addChild(TINYJS_LOKALE_VAR, locale); locale_link->hidden = locale_link->dontEnumerable = true; scopes.push_back(locale); @@ -1585,15 +1586,15 @@ CScriptVarSmartLink CTinyJS::parseFunctionDefinition() { } scopes.pop_back(); funcVar->var->data = l->getSubString(funcBegin); - if(anonymous_link->var->Childs.size() == 1) + if(anonymous_link->var->Childs.size() == 0) locale->removeLink(anonymous_link); - else - anonymous_link->var->removeLink(funcOffset_link); } else { int funcBegin = l->tokenStart; block(noexecute); funcVar->var->data = l->getSubString(funcBegin); } + funcOffset = oldFuncOffset; + return funcVar; } @@ -1917,10 +1918,8 @@ CScriptVarSmartLink CTinyJS::factor(bool &execute) { return new CScriptVarLink(contents); } if (l->tk==LEX_R_FUNCTION) { - CScriptVarLink *funcOffset = 0; CScriptVarLink *anonymous = scopes.back()->findChild(TINYJS_ANONYMOUS_VAR); - if(anonymous) funcOffset = anonymous->var->findChild("funcOffset"); - string anonymous_name = int2string(l->getDataPos()-(funcOffset?funcOffset->var->getInt():0)); + string anonymous_name = int2string(l->getDataPos()-this->funcOffset); CScriptVarSmartLink funcVar = parseFunctionDefinition(); this->runtimeFlags; // funcName = anonymous ? int2string(l->tokenStart) : l->tkStr; diff --git a/TinyJS.h b/TinyJS.h index 5042f6d..06dd2db 100755 --- a/TinyJS.h +++ b/TinyJS.h @@ -450,6 +450,7 @@ class CTinyJS { private: bool twoPass; bool twoPassEval; + int funcOffset; CScriptLex *l; /// current lexer int runtimeFlags; std::vector scopes; /// stack of scopes when parsing From 3e103bb7444246703c313152390814bded6b337a Mon Sep 17 00:00:00 2001 From: armin Date: Sun, 26 Sep 2010 09:40:26 +0000 Subject: [PATCH 21/43] changed: remove extra 2-pass-mode-setting for eval(...) --- TinyJS.cpp | 5 ++--- TinyJS.h | 3 +-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/TinyJS.cpp b/TinyJS.cpp index 3238005..0c678f3 100755 --- a/TinyJS.cpp +++ b/TinyJS.cpp @@ -1404,9 +1404,8 @@ int CScriptVar::getRefs() { // ----------------------------------------------------------------------------------- CSCRIPT -CTinyJS::CTinyJS(bool TwoPass, bool TwoPassEval) { +CTinyJS::CTinyJS(bool TwoPass) { twoPass=TwoPass; - twoPassEval=TwoPassEval; funcOffset = 0; l = NULL; runtimeFlags = 0; @@ -2884,7 +2883,7 @@ void CTinyJS::scEval(CScriptVar *c, void *data) { try { bool execute = true; bool noexecute = false; - if(twoPassEval) + if(twoPass) { SET_RUNTIME_PASS_TWO_1; do { diff --git a/TinyJS.h b/TinyJS.h index 06dd2db..00b25e4 100755 --- a/TinyJS.h +++ b/TinyJS.h @@ -393,7 +393,7 @@ class CScriptVarSmartLink class CTinyJS { public: - CTinyJS(bool TwoPass=true, bool TwoPassEval=true); + CTinyJS(bool TwoPass=true); ~CTinyJS(); void execute(const std::string &code); @@ -449,7 +449,6 @@ class CTinyJS { CScriptVar *root; /// root of symbol table private: bool twoPass; - bool twoPassEval; int funcOffset; CScriptLex *l; /// current lexer int runtimeFlags; From f910a3dcc2aa8f9b71406c954929d63ab659e52f Mon Sep 17 00:00:00 2001 From: armin Date: Sun, 26 Sep 2010 15:44:47 +0000 Subject: [PATCH 22/43] fixed: no error by object- or array-literals when "execute"==false e.g. if(...) a=[1,2,9]; else a={a:9,b:12}; --- TinyJS.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/TinyJS.cpp b/TinyJS.cpp index 0c678f3..d9936cd 100755 --- a/TinyJS.cpp +++ b/TinyJS.cpp @@ -1888,8 +1888,8 @@ CScriptVarSmartLink CTinyJS::factor(bool &execute) { if (l->tk==LEX_STR) l->match(LEX_STR); else l->match(LEX_ID); l->match(':'); + CScriptVarSmartLink a = assignment(execute); if (execute) { - CScriptVarSmartLink a = assignment(execute); contents->addChild(id, a->var); } // no need to clean here, as it will definitely be used @@ -1905,9 +1905,9 @@ CScriptVarSmartLink CTinyJS::factor(bool &execute) { l->match('['); int idx = 0; while (l->tk != ']') { - if (execute) { CScriptVarSmartLink a = assignment(execute); - contents->addChild(int2string(idx), a->var); + if (execute) { + contents->addChild(int2string(idx), a->var); } // no need to clean here, as it will definitely be used if (l->tk != ']') l->match(','); From a7edf9474de5813062d7645a436f4732983a3278 Mon Sep 17 00:00:00 2001 From: armin Date: Tue, 5 Oct 2010 17:33:35 +0000 Subject: [PATCH 23/43] added: a recursion-detector. Been brought recursion (such as: a = {}; a.a = a;) problems with trace() and Dump() (its runs endless) also was the Var never released (results in memory leaks or out of memory when this stuff is in a loop). A Var is destroyed when its reference counter is 0. But by recursions the reference counter never goes to 0, because the Var references to its self. The NEW reference-detector detects, when a recursion has no external references and destroys the recursed Vars. --- TinyJS.cpp | 301 +++++++++++++++++++++++++++++++++++++------ TinyJS.h | 33 ++++- TinyJS_Functions.cpp | 8 +- 3 files changed, 297 insertions(+), 45 deletions(-) diff --git a/TinyJS.cpp b/TinyJS.cpp index d9936cd..9ec4a92 100755 --- a/TinyJS.cpp +++ b/TinyJS.cpp @@ -182,9 +182,7 @@ inline CScriptVarSmartLink &CScriptVarSmartLink::operator = (CScriptVarLink *Lin // this operator corresponds "link->replaceWith(Link->var)" inline CScriptVarSmartLink &CScriptVarSmartLink::operator <<(CScriptVarSmartLink &Link) { -#if _DEBUG ASSERT(link && Link.link); -#endif link->replaceWith(Link->var); return *this; } @@ -290,19 +288,13 @@ void replace(string &str, char textFrom, const char *textTo) { p = str.find(textFrom, p+sLen); } } -#ifdef __GNUC__ -string int2string(int intData) { - char buffer[32]; - sprintf_s(buffer, sizeof(buffer), "%d", intData); - return buffer; -} -#else -string &int2string(int intData, string &inString=string()); -#endif -string &int2string(int intData, string &inString) { + +string &int2string(int intData, const string &inString=string()); +string &int2string(int intData, const string &inString) { + string &_inString = (string &) inString; char buffer[32]; sprintf_s(buffer, sizeof(buffer), "%d", intData); - return inString=buffer; + return _inString=buffer; } /// convert the given string into a quoted string suitable for javascript @@ -766,14 +758,15 @@ CScriptVarLink::~CScriptVarLink() { #if DEBUG_MEMORY mark_deallocated(this); #endif - var->unref(); + var->unref(this->owner); } void CScriptVarLink::replaceWith(CScriptVar *newVar) { if(!newVar) newVar = new CScriptVar(); CScriptVar *oldVar = var; + oldVar->unref(owner); var = newVar->ref(); - oldVar->unref(); + if(owner) var->recoursionCheck(owner); } void CScriptVarLink::replaceWith(CScriptVarLink *newVar) { @@ -787,6 +780,9 @@ void CScriptVarLink::replaceWith(CScriptVarLink *newVar) { CScriptVar::CScriptVar() { refs = 0; + internalRefs = 0; + recursionFlag = 0; + recursionSet = 0; __proto__ = NULL; #if DEBUG_MEMORY mark_allocated(this); @@ -797,6 +793,9 @@ CScriptVar::CScriptVar() { CScriptVar::CScriptVar(const string &str) { refs = 0; + internalRefs = 0; + recursionFlag = 0; + recursionSet = 0; __proto__ = NULL; #if DEBUG_MEMORY mark_allocated(this); @@ -808,6 +807,9 @@ CScriptVar::CScriptVar(const string &str) { CScriptVar::CScriptVar(const char *str) { refs = 0; + internalRefs = 0; + recursionFlag = 0; + recursionSet = 0; __proto__ = NULL; #if DEBUG_MEMORY mark_allocated(this); @@ -819,6 +821,9 @@ CScriptVar::CScriptVar(const char *str) { CScriptVar::CScriptVar(const string &varData, int varFlags) { refs = 0; + internalRefs = 0; + recursionFlag = 0; + recursionSet = 0; __proto__ = NULL; #if DEBUG_MEMORY mark_allocated(this); @@ -835,6 +840,9 @@ CScriptVar::CScriptVar(const string &varData, int varFlags) { CScriptVar::CScriptVar(double val) { refs = 0; + internalRefs = 0; + recursionFlag = 0; + recursionSet = 0; __proto__ = NULL; #if DEBUG_MEMORY mark_allocated(this); @@ -845,6 +853,9 @@ CScriptVar::CScriptVar(double val) { CScriptVar::CScriptVar(int val) { refs = 0; + internalRefs = 0; + recursionFlag = 0; + recursionSet = 0; __proto__ = NULL; #if DEBUG_MEMORY mark_allocated(this); @@ -855,6 +866,9 @@ CScriptVar::CScriptVar(int val) { CScriptVar::CScriptVar(bool val) { refs = 0; + internalRefs = 0; + recursionFlag = 0; + recursionSet = 0; __proto__ = NULL; #if DEBUG_MEMORY mark_allocated(this); @@ -894,6 +908,11 @@ CScriptVar *CScriptVar::getParameter(const std::string &name) { return findChildOrCreate(name)->var; } +CScriptVar *CScriptVar::getParameter(int Idx) { + CScriptVar *arguments = findChildOrCreate(TINYJS_ARGUMENTS_VAR)->var; + return arguments->findChildOrCreate(int2string(Idx))->var; +} + CScriptVarLink *CScriptVar::findChild(const string &childName) { SCRIPTVAR_CHILDS::iterator it = Childs.find(childName); @@ -931,24 +950,27 @@ CScriptVarLink *CScriptVar::addChild(const std::string &childName, CScriptVar *c CScriptVarLink *link = new CScriptVarLink(child, childName); link->owned = true; link->owner = this; - return Childs[childName] = link; + Childs[childName] = link; + link->var->recoursionCheck(this); + return link; } CScriptVarLink *CScriptVar::addChildNoDup(const std::string &childName, CScriptVar *child) { // if no child supplied, create one if (!child) child = new CScriptVar(); - CScriptVarLink *v = findChild(childName); - if (v) { - v->replaceWith(child); + CScriptVarLink *link = findChild(childName); + if (link) { + link->replaceWith(child); } else { - CScriptVarLink *link = new CScriptVarLink(child, childName); + link = new CScriptVarLink(child, childName); link->owned = true; link->owner = this; - v = Childs[childName] = link; + Childs[childName] = link; + link->var->recoursionCheck(this); } - return v; + return link; } bool CScriptVar::removeLink(CScriptVarLink *&link) { @@ -962,13 +984,12 @@ bool CScriptVar::removeLink(CScriptVarLink *&link) { link = 0; return true; } - void CScriptVar::removeAllChildren() { SCRIPTVAR_CHILDS::iterator it; - while((it = Childs.begin()) != Childs.end()) { + for(it = Childs.begin(); it!= Childs.end(); ++it) { delete it->second; - Childs.erase(it); } + Childs.clear(); } CScriptVar *CScriptVar::getArrayIndex(int idx) { @@ -1275,16 +1296,48 @@ CScriptVar *CScriptVar::deepCopy() { return newVar; } -void CScriptVar::trace(string indentStr, const string &name) { +void CScriptVar::trace(const string &name) { + trace(string(), name); +} +void CScriptVar::trace(string &indentStr, const string &name) { + string indent = " "; + if(recursionSet) { + if(recursionFlag) { + int pos = recursionFlag>>12; + string indent_r = indentStr.substr(0, pos+1) + string(indentStr.length()-(pos+1), '-'); + TRACE("%s'%s' = '%s' %s\n", + indent_r.c_str(), + name.c_str(), + getString().c_str(), + getFlagsAsString().c_str()); + + if((recursionFlag&0xfff) == 1) { + indentStr.replace(pos,1," "); + recursionFlag = 0; + } else + recursionFlag--; + return; + } else if(!recursionSet->recursionPathBase || internalRefs>1) { + indent = "| "; + recursionSet->recursionPathBase = this; + } + recursionFlag = indentStr.length()<<12 | (internalRefs&0xfff) ; + } TRACE("%s'%s' = '%s' %s\n", indentStr.c_str(), name.c_str(), getString().c_str(), getFlagsAsString().c_str()); - string indent = indentStr+" "; + + indentStr+=indent; for(SCRIPTVAR_CHILDS::iterator it = Childs.begin(); it != Childs.end(); ++it) { - it->second->var->trace(indent, it->first); + it->second->var->trace(indentStr, it->first); } + if(recursionSet) { + recursionSet->recursionPathBase = 0; + recursionFlag = 0; + } + indentStr = indentStr.substr(0, indentStr.length()-2); } string CScriptVar::getFlagsAsString() { @@ -1387,12 +1440,84 @@ void CScriptVar::setCallback(NativeFncBase *callback, void *userdata) { CScriptVar *CScriptVar::ref() { refs++; + if(recursionSet) recursionSet->sumRefs++; return this; } +RECURSION_SET_VAR *CScriptVar::unrefInternal(){ + RECURSION_SET_VAR *ret=0; + if(internalRefs) { + ASSERT(recursionSet); + internalRefs--; + recursionSet->sumInternalRefs--; + if(internalRefs==0) { + recursionSet->sumRefs -= refs; + recursionSet->recursionSet.erase(this); + if(recursionSet->recursionSet.size()) { + for(SCRIPTVAR_CHILDS::iterator it = Childs.begin(); it!= Childs.end(); ++it) { + if(it->second->var->recursionSet == recursionSet) + it->second->var->unrefInternal(); + } + ret = recursionSet; + } else + delete recursionSet; + recursionSet = 0; + } + } + return ret; +} +void CScriptVar::unref(CScriptVar* Owner) { + refs--; + ASSERT(refs>=0); // printf("OMFG, we have unreffed too far!\n"); +#if 1 + if(recursionSet) { // this Var is in a Set + recursionSet->sumRefs--; + if(Owner && Owner->recursionSet == recursionSet) { // internal Ref + if(internalRefs==1) { + if(refs) { + RECURSION_SET_VAR *old_set = unrefInternal(); + + if(old_set) { // we have breaked a recursion bu the recursion Set is not destroyed + // we needs a rededection of recursions for all Vars in the Set; + // first we destroy the Set + for(RECURSION_SET::iterator it= old_set->recursionSet.begin(); it!=old_set->recursionSet.end(); ++it) { + (*it)->internalRefs = 0; + (*it)->recursionSet = 0; + } + // now we can rededect recursions + for(RECURSION_SET::iterator it= old_set->recursionSet.begin(); it!=old_set->recursionSet.end(); ++it) { + if((*it)->recursionSet == 0) + (*it)->recoursionCheck(); + } + delete old_set; + } -void CScriptVar::unref() { - if (refs<=0) printf("OMFG, we have unreffed too far!\n"); - if ((--refs)==0) { + } else { + removeAllChildren(); + internalRefs--; + recursionSet->sumInternalRefs--; + recursionSet->recursionSet.erase(this); + if(!recursionSet->recursionSet.size()) + delete recursionSet; + delete this; + } + }// otherwise nothing to do + } + else { //external Ref + ASSERT(refs); // by a Set it is not possible to remove an external Ref as the last ref + if(recursionSet->sumRefs==recursionSet->sumInternalRefs) { + SCRIPTVAR_CHILDS copyOfChilds(Childs); + Childs.clear(); + SCRIPTVAR_CHILDS::iterator it; + for(it = copyOfChilds.begin(); it!= copyOfChilds.end(); ++it) { + delete it->second; + } + copyOfChilds.clear(); + } // otherwise nothing to do + } + } + else +#endif + if (refs==0) { delete this; } } @@ -1401,6 +1526,106 @@ int CScriptVar::getRefs() { return refs; } +RECURSION_VECT::iterator find_last(RECURSION_VECT &Vector, const RECURSION_VECT::value_type &Key) { + for(RECURSION_VECT::reverse_iterator rit=Vector.rbegin(); rit!=Vector.rend(); ++rit) { + if(*rit==Key) return --rit.base(); + } + return Vector.end(); +} + +void CScriptVar::recoursionCheck(CScriptVar *Owner) { + if(recursionSet && Owner &&recursionSet == Owner->recursionSet) { // special thing for self-linking + Owner->internalRefs++; + Owner->recursionSet->sumInternalRefs++; + } else { + RECURSION_VECT recursionPath; + recoursionCheck(recursionPath); + } +} +void CScriptVar::recoursionCheck(RECURSION_VECT &recursionPath) +{ + if(recursionFlag || (recursionSet && recursionSet->recursionPathBase)) { // recursion found - create a new set + RECURSION_SET_VAR *new_set; + RECURSION_VECT::iterator it; + + if(recursionSet) { // recursion starts by a Set + new_set = recursionSet; // we use the old Set as new Set + new_set->sumInternalRefs++; // one internal + this->internalRefs++; // new internal reference found + // find the Var of the Set tat is in the Path + // +1, because the Set is allready in the new Set + it = find_last(recursionPath, recursionSet->recursionPathBase)+1; + } else { // recursion starts by a Var + new_set = new RECURSION_SET_VAR(this); // create a new Set(marks it as "in the Path", with "this as start Var) + // find the Var in the Path + it = find_last(recursionPath, this); + } + // insert the Path begining by this Var or the next Var after dis Set + // in the new_set and removes it from the Path + for(; it!= recursionPath.end(); ++it) { + if((*it)->recursionSet) { // insert an existing Set + RECURSION_SET_VAR *old_set = (*it)->recursionSet; + new_set->sumInternalRefs += old_set->sumInternalRefs; // for speed up; we adds the sum of internal refs and the sum of + new_set->sumRefs += old_set->sumRefs; // refs to the new Set here instead rather than separately for each Var + new_set->sumInternalRefs++; // a new Set in a Set is linked + (*it)->internalRefs++; // over an internale reference + // insert all Vars of the old Set in the new Set + for(RECURSION_SET::iterator set_it = old_set->recursionSet.begin(); set_it!= old_set->recursionSet.end(); ++set_it) { + new_set->recursionSet.insert(*set_it); // insert this Var to the Set + (*set_it)->recursionSet = new_set; // the Var is now in a Set + } + delete old_set; + } else { // insert this Var in the new set + new_set->sumInternalRefs++; // a new Var in a Set is linked + (*it)->internalRefs++; // over an internale reference + new_set->sumRefs += (*it)->refs; // adds the Var refs to the Set refs + new_set->recursionSet.insert(*it); // insert this Var to the Set + (*it)->recursionSet = new_set; // the Var is now in a Set + (*it)->recursionFlag = 0; // clear the Var-recursionFlag, because the Var in now in a Set + } + } + // all Vars inserted in the new Set + // remove from Path all Vars from the end up to the start of Set (without the start of Set) + while(recursionPath.back() != new_set->recursionPathBase) + recursionPath.pop_back(); + } else if(recursionSet) { // found a recursionSet + recursionPath.push_back(this); // push "this" in the Path + recursionSet->recursionPathBase = this; // of all Vars in the Set is "this" in the Path + RECURSION_SET_VAR *old_set = recursionSet; + // a recursionSet is like an one&only Var + // goes to all Cilds of the Set + RECURSION_SET Set(recursionSet->recursionSet); + for(RECURSION_SET::iterator set_it = Set.begin(); set_it != Set.end(); ++set_it) { + for(SCRIPTVAR_CHILDS::iterator it = (*set_it)->Childs.begin(); it != (*set_it)->Childs.end(); ++it) { + if(it->second->var->recursionSet != recursionSet) + it->second->var->recoursionCheck(recursionPath); + } + } + if(old_set == recursionSet) { // old_set == recursionSet meens this Set is *not* included in an other Set + recursionSet->recursionPathBase = 0; + recursionPath.pop_back(); + } // otherwise this Set is included in an other Set and is allready removed from Path + } else { + recursionPath.push_back(this); // push "this" in the Path + recursionFlag = 1; // marked this Var as "the Var is in the Path" + // goes to all Cilds of the Var + for(SCRIPTVAR_CHILDS::iterator it = Childs.begin(); it != Childs.end(); ++it) { + it->second->var->recoursionCheck(recursionPath); + } + if(recursionFlag) { // recursionFlag!=0 meens this Var is not included in a Set + recursionFlag = 0; + ASSERT(recursionPath.back() == this); + recursionPath.pop_back(); + } else { // otherwise this Var is included in a Set and is allready removed from Path + ASSERT(recursionSet && recursionSet->recursionPathBase); + if(recursionSet->recursionPathBase == this) { // the Var is the Base of the Set + recursionSet->recursionPathBase = 0; + ASSERT(recursionPath.back() == this); + recursionPath.pop_back(); + } + } + } +} // ----------------------------------------------------------------------------------- CSCRIPT @@ -1425,10 +1650,10 @@ CTinyJS::CTinyJS(bool TwoPass) { CTinyJS::~CTinyJS() { ASSERT(!l); scopes.clear(); - stringClass->unref(); - arrayClass->unref(); - objectClass->unref(); - root->unref(); + stringClass->unref(0); + arrayClass->unref(0); + objectClass->unref(0); + root->unref(0); #if DEBUG_MEMORY show_allocated(); @@ -1503,7 +1728,7 @@ string CTinyJS::evaluate(const string &code) { void CTinyJS::parseFunctionArguments(CScriptVar *funcVar) { l->match('('); CScriptVar *arguments = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT); - funcVar->addChild("arguments", arguments); + funcVar->addChild(TINYJS_ARGUMENTS_VAR, arguments); int idx = 0; while (l->tk!=')') { arguments->addChild(int2string(idx++), new CScriptVar(l->tkStr)); @@ -2055,7 +2280,7 @@ CScriptVarSmartLink CTinyJS::unary(bool &execute) { CScriptVar *new_a = a->var->mathsOp(&one, op==LEX_PLUSPLUS ? '+' : '-'); a->replaceWith(new_a); a = res; - res->unref(); + res->unref(0); } } } @@ -2724,7 +2949,7 @@ CScriptVarSmartLink CTinyJS::statement(bool &execute) { if(isThrow) { CScriptVar *catchScope = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT); catchScope->addChild(exeption_var_name, exeption); - exeption->unref(); exeption = 0; + exeption->unref(0); exeption = 0; scopes.push_back(catchScope); try { block(execute); diff --git a/TinyJS.h b/TinyJS.h index 00b25e4..b00e440 100755 --- a/TinyJS.h +++ b/TinyJS.h @@ -29,6 +29,7 @@ #include #include #include +#include #ifndef __GNUC__ # define __attribute__(x) @@ -178,6 +179,7 @@ enum RUNTIME_FLAGS { #define TINYJS_RETURN_VAR "return" #define TINYJS_LOKALE_VAR "__locale__" #define TINYJS_ANONYMOUS_VAR "__anonymous__" +#define TINYJS_ARGUMENTS_VAR "arguments" #define TINYJS_PROTOTYPE_CLASS "prototype" #define TINYJS_TEMP_NAME "" #define TINYJS_BLANK_DATA "" @@ -252,6 +254,19 @@ class CScriptVarLink void replaceWith(CScriptVarLink *newVar); ///< Replace the Variable pointed to (just dereferences) }; +typedef std::vector RECURSION_VECT; +typedef std::set RECURSION_SET; +class RECURSION_SET_VAR +{ +public: + RECURSION_SET_VAR(CScriptVar *PathBase) : sumRefs(0), sumInternalRefs(0), recursionPathBase(PathBase) {} + RECURSION_SET recursionSet; ///< contains all Vars in the Set + int sumRefs; ///< Sum of Refs + int sumInternalRefs; ///< Sum of Internal Refs --> sumRefs - sumInternalRefs = sumExternalRefs + CScriptVar *recursionPathBase; ///< temporary used in recoursionCheck() and trace() +}; + + class NativeFncBase; typedef std::map SCRIPTVAR_CHILDS; /// Variable class (containing a doubly-linked list of children) @@ -270,6 +285,7 @@ class CScriptVar CScriptVar *getReturnVar(); ///< If this is a function, get the result value (for use by native functions) void setReturnVar(CScriptVar *var); ///< Set the result value. Use this when setting complex return data as it avoids a deepCopy() CScriptVar *getParameter(const std::string &name); ///< If this is a function, get the parameter with the given name (for use by native functions) + CScriptVar *getParameter(int Idx); ///< If this is a function, get the parameter with the given index (for use by native functions) CScriptVarLink *findChild(const std::string &childName); ///< Tries to find a child with the given name, may return 0 CScriptVarLink *findChildOrCreate(const std::string &childName, int varFlags=SCRIPTVAR_UNDEFINED); ///< Tries to find a child with the given name, or will create it with the given flags @@ -315,7 +331,8 @@ class CScriptVar void copyValue(CScriptVar *val); ///< copy the value from the value given CScriptVar *deepCopy(); ///< deep copy this node and return the result - void trace(std::string indentStr = "", const std::string &name = ""); ///< Dump out the contents of this using trace + void trace(const std::string &name = ""); ///< Dump out the contents of this using trace + void trace(std::string &indentStr, const std::string &name = ""); ///< Dump out the contents of this using trace std::string getFlagsAsString(); ///< For debugging - just dump a string version of the flags void getJSON(std::ostringstream &destination, const std::string linePrefix=""); ///< Write out all the JS code needed to recreate this script variable to the stream (as JSON) void setCallback(JSCallback callback, void *userdata); @@ -324,11 +341,15 @@ class CScriptVar SCRIPTVAR_CHILDS Childs; /// For memory management/garbage collection + void recoursionCheck(CScriptVar *Owner=0); CScriptVar *ref(); ///< Add reference to this variable - void unref(); ///< Remove a reference, and delete this variable if required + void unref(CScriptVar* Owner); ///< Remove a reference, and delete this variable if required int getRefs(); ///< Get the number of references to this script variable +private: + RECURSION_SET_VAR *unrefInternal(); protected: int refs; ///< The number of references held to this - used for garbage collection + int internalRefs; CScriptVarLink *__proto__; std::string data; ///< The contents of this variable if it is a string long intData; ///< The contents of this variable if it is an int @@ -336,8 +357,8 @@ class CScriptVar int flags; ///< the flags determine the type of the variable - int/double/string/etc union { - JSCallback jsCallback; ///< Callback for native functions - NativeFncBase *jsCallbackClass; ///< Wrapper for Class-Member-Functions as Callback for native functions + JSCallback jsCallback; ///< Callback for native functions + NativeFncBase *jsCallbackClass; ///< Wrapper for Class-Member-Functions as Callback for native functions }; void *jsCallbackUserData; ///< user data passed as second argument to native functions @@ -346,7 +367,9 @@ class CScriptVar /** Copy the basic data and flags from the variable given, with no * children. Should be used internally only - by copyValue and deepCopy */ void copySimpleData(CScriptVar *val); - + void recoursionCheck(RECURSION_VECT &recursionPath); + int recursionFlag; + RECURSION_SET_VAR *recursionSet; friend class CTinyJS; }; diff --git a/TinyJS_Functions.cpp b/TinyJS_Functions.cpp index 7712d98..64fbde4 100755 --- a/TinyJS_Functions.cpp +++ b/TinyJS_Functions.cpp @@ -53,9 +53,13 @@ using namespace std; // ----------------------------------------------- Actual Functions -void scTrace(CScriptVar *UNUSED(c), void * userdata) { +void scTrace(CScriptVar *c, void * userdata) { CTinyJS *js = (CTinyJS*)userdata; - js->root->trace(); + c=c->getParameter(0); + if(c->isUndefined()) + js->root->trace("root"); + else + c->trace(); } void scObjectDump(CScriptVar *c, void *) { From bca0611cf8fd1d77dc0be1b33403ca98500b9322 Mon Sep 17 00:00:00 2001 From: armin Date: Tue, 5 Oct 2010 18:37:45 +0000 Subject: [PATCH 24/43] misc: some clean ups added: getParameterLength() for native functions to get the count of function-parameters added: getParameter(int Idx) for native functions to get the parameter with the given index. (already added in R45, but forgotten to write in the change-log) --- TinyJS.cpp | 69 ++++++++++++++++++++++---------------------- TinyJS.h | 4 +-- TinyJS_Functions.cpp | 7 ++--- 3 files changed, 40 insertions(+), 40 deletions(-) diff --git a/TinyJS.cpp b/TinyJS.cpp index 9ec4a92..b440361 100755 --- a/TinyJS.cpp +++ b/TinyJS.cpp @@ -25,7 +25,7 @@ /* Version 0.1 : (gw) First published on Google Code Version 0.11 : Making sure the 'root' variable never changes - 'symbol_base' added for the current base of the sybmbol table + 'symbol_base' added for the current base of the symbol table Version 0.12 : Added findChildOrCreate, changed string passing to use references Fixed broken string encoding in getJSString() Removed getInitCode and added getJSON instead @@ -96,7 +96,7 @@ Added break and continue statements for loops R4 Changed "owned"-member of CScriptVarLink from bool to a pointer of CScriptVarowned Added some Visual Studio Preprocessor stuff - Added now alowed stuff like this (function (v) {print(v);})(12); + Added now allowed stuff like this (function (v) {print(v);})(12); Remove unneeded and wrong deepCopy by assignment operator '=' @@ -912,6 +912,10 @@ CScriptVar *CScriptVar::getParameter(int Idx) { CScriptVar *arguments = findChildOrCreate(TINYJS_ARGUMENTS_VAR)->var; return arguments->findChildOrCreate(int2string(Idx))->var; } +int CScriptVar::getParameterLength() { + CScriptVar *arguments = findChildOrCreate(TINYJS_ARGUMENTS_VAR)->var; + return arguments->findChildOrCreate("length")->var->getInt(); +} CScriptVarLink *CScriptVar::findChild(const string &childName) { @@ -1388,7 +1392,7 @@ string CScriptVar::getVarType() { return "string"; else if(this->isUndefined()) return "undefined"; - return "object"; // Objcect / Array / null + return "object"; // Object / Array / null } void CScriptVar::getJSON(ostringstream &destination, const string linePrefix) { @@ -1468,7 +1472,6 @@ RECURSION_SET_VAR *CScriptVar::unrefInternal(){ void CScriptVar::unref(CScriptVar* Owner) { refs--; ASSERT(refs>=0); // printf("OMFG, we have unreffed too far!\n"); -#if 1 if(recursionSet) { // this Var is in a Set recursionSet->sumRefs--; if(Owner && Owner->recursionSet == recursionSet) { // internal Ref @@ -1477,13 +1480,13 @@ void CScriptVar::unref(CScriptVar* Owner) { RECURSION_SET_VAR *old_set = unrefInternal(); if(old_set) { // we have breaked a recursion bu the recursion Set is not destroyed - // we needs a rededection of recursions for all Vars in the Set; + // we needs a redetection of recursions for all Vars in the Set; // first we destroy the Set for(RECURSION_SET::iterator it= old_set->recursionSet.begin(); it!=old_set->recursionSet.end(); ++it) { (*it)->internalRefs = 0; (*it)->recursionSet = 0; } - // now we can rededect recursions + // now we can redetect recursions for(RECURSION_SET::iterator it= old_set->recursionSet.begin(); it!=old_set->recursionSet.end(); ++it) { if((*it)->recursionSet == 0) (*it)->recoursionCheck(); @@ -1515,9 +1518,7 @@ void CScriptVar::unref(CScriptVar* Owner) { } // otherwise nothing to do } } - else -#endif - if (refs==0) { + else if (refs==0) { delete this; } } @@ -1553,14 +1554,14 @@ void CScriptVar::recoursionCheck(RECURSION_VECT &recursionPath) new_set->sumInternalRefs++; // one internal this->internalRefs++; // new internal reference found // find the Var of the Set tat is in the Path - // +1, because the Set is allready in the new Set + // +1, because the Set is already in the new Set it = find_last(recursionPath, recursionSet->recursionPathBase)+1; } else { // recursion starts by a Var new_set = new RECURSION_SET_VAR(this); // create a new Set(marks it as "in the Path", with "this as start Var) // find the Var in the Path it = find_last(recursionPath, this); } - // insert the Path begining by this Var or the next Var after dis Set + // insert the Path beginning by this Var or the next Var after dis Set // in the new_set and removes it from the Path for(; it!= recursionPath.end(); ++it) { if((*it)->recursionSet) { // insert an existing Set @@ -1568,7 +1569,7 @@ void CScriptVar::recoursionCheck(RECURSION_VECT &recursionPath) new_set->sumInternalRefs += old_set->sumInternalRefs; // for speed up; we adds the sum of internal refs and the sum of new_set->sumRefs += old_set->sumRefs; // refs to the new Set here instead rather than separately for each Var new_set->sumInternalRefs++; // a new Set in a Set is linked - (*it)->internalRefs++; // over an internale reference + (*it)->internalRefs++; // over an internal reference // insert all Vars of the old Set in the new Set for(RECURSION_SET::iterator set_it = old_set->recursionSet.begin(); set_it!= old_set->recursionSet.end(); ++set_it) { new_set->recursionSet.insert(*set_it); // insert this Var to the Set @@ -1577,7 +1578,7 @@ void CScriptVar::recoursionCheck(RECURSION_VECT &recursionPath) delete old_set; } else { // insert this Var in the new set new_set->sumInternalRefs++; // a new Var in a Set is linked - (*it)->internalRefs++; // over an internale reference + (*it)->internalRefs++; // over an internal reference new_set->sumRefs += (*it)->refs; // adds the Var refs to the Set refs new_set->recursionSet.insert(*it); // insert this Var to the Set (*it)->recursionSet = new_set; // the Var is now in a Set @@ -1593,7 +1594,7 @@ void CScriptVar::recoursionCheck(RECURSION_VECT &recursionPath) recursionSet->recursionPathBase = this; // of all Vars in the Set is "this" in the Path RECURSION_SET_VAR *old_set = recursionSet; // a recursionSet is like an one&only Var - // goes to all Cilds of the Set + // goes to all Childs of the Set RECURSION_SET Set(recursionSet->recursionSet); for(RECURSION_SET::iterator set_it = Set.begin(); set_it != Set.end(); ++set_it) { for(SCRIPTVAR_CHILDS::iterator it = (*set_it)->Childs.begin(); it != (*set_it)->Childs.end(); ++it) { @@ -1601,22 +1602,22 @@ void CScriptVar::recoursionCheck(RECURSION_VECT &recursionPath) it->second->var->recoursionCheck(recursionPath); } } - if(old_set == recursionSet) { // old_set == recursionSet meens this Set is *not* included in an other Set + if(old_set == recursionSet) { // old_set == recursionSet means this Set is *not* included in an other Set recursionSet->recursionPathBase = 0; recursionPath.pop_back(); - } // otherwise this Set is included in an other Set and is allready removed from Path + } // otherwise this Set is included in an other Set and is already removed from Path } else { recursionPath.push_back(this); // push "this" in the Path recursionFlag = 1; // marked this Var as "the Var is in the Path" - // goes to all Cilds of the Var + // goes to all Childs of the Var for(SCRIPTVAR_CHILDS::iterator it = Childs.begin(); it != Childs.end(); ++it) { it->second->var->recoursionCheck(recursionPath); } - if(recursionFlag) { // recursionFlag!=0 meens this Var is not included in a Set + if(recursionFlag) { // recursionFlag!=0 means this Var is not included in a Set recursionFlag = 0; ASSERT(recursionPath.back() == this); recursionPath.pop_back(); - } else { // otherwise this Var is included in a Set and is allready removed from Path + } else { // otherwise this Var is included in a Set and is already removed from Path ASSERT(recursionSet && recursionSet->recursionPathBase); if(recursionSet->recursionPathBase == this) { // the Var is the Base of the Set recursionSet->recursionPathBase = 0; @@ -1643,8 +1644,8 @@ CTinyJS::CTinyJS(bool TwoPass) { root->addChild("String", stringClass); root->addChild("Array", arrayClass); root->addChild("Object", objectClass); - exeption = NULL; - addNative("function eval(jsCode)", this, &CTinyJS::scEval); // execute the given string and return the resul + exceptionVar = NULL; + addNative("function eval(jsCode)", this, &CTinyJS::scEval); // execute the given string and return the result } CTinyJS::~CTinyJS() { @@ -1662,7 +1663,7 @@ CTinyJS::~CTinyJS() { void CTinyJS::throwError(bool &execute, const string &message, int pos /*=-1*/) { if(execute && (runtimeFlags & RUNTIME_CANTHROW)) { - exeption = (new CScriptVar(message))->ref(); + exceptionVar = (new CScriptVar(message))->ref(); runtimeFlags |= RUNTIME_THROW; execute = false; return; @@ -1946,7 +1947,7 @@ CScriptVarSmartLink CTinyJS::factor(bool &execute) { // setup a return variable CScriptVarSmartLink returnVar; if(execute) { - int old_function_runtimeFlags = runtimeFlags; // save runtimFlags + int old_function_runtimeFlags = runtimeFlags; // save runtimeFlags runtimeFlags &= ~RUNTIME_LOOP_MASK; // clear LOOP-Flags // execute function! // add the function's execute space to the symbol table so we can recurse @@ -1987,10 +1988,10 @@ CScriptVarSmartLink CTinyJS::factor(bool &execute) { if(runtimeFlags & RUNTIME_CANTHROW) { runtimeFlags |= RUNTIME_THROW; execute = false; - this->exeption = e->var->ref(); + this->exceptionVar = e->var->ref(); } else - throw new CScriptException("uncaught exeption: '"+e->var->getString()+"' in: "+a->name+"()"); + throw new CScriptException("uncaught exception: '"+e->var->getString()+"' in: "+a->name+"()"); } } else { /* we just want to execute the block, but something could @@ -2148,7 +2149,7 @@ CScriptVarSmartLink CTinyJS::factor(bool &execute) { this->runtimeFlags; // funcName = anonymous ? int2string(l->tokenStart) : l->tkStr; - funcVar->name = TINYJS_TEMP_NAME; // allways anonymous + funcVar->name = TINYJS_TEMP_NAME; // always anonymous if(IS_RUNTIME_PASS_TWO_1) { CScriptVarLink *anonymous = scopes.back()->findChildOrCreate(TINYJS_ANONYMOUS_VAR, SCRIPTVAR_OBJECT); @@ -2943,13 +2944,13 @@ CScriptVarSmartLink CTinyJS::statement(bool &execute) { { l->match(LEX_R_CATCH); l->match('('); - string exeption_var_name = l->tkStr; + string exception_var_name = l->tkStr; l->match(LEX_ID); l->match(')'); if(isThrow) { CScriptVar *catchScope = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT); - catchScope->addChild(exeption_var_name, exeption); - exeption->unref(0); exeption = 0; + catchScope->addChild(exception_var_name, exceptionVar); + exceptionVar->unref(0); exceptionVar = 0; scopes.push_back(catchScope); try { block(execute); @@ -2978,10 +2979,10 @@ CScriptVarSmartLink CTinyJS::statement(bool &execute) { if(runtimeFlags & RUNTIME_CANTHROW) { runtimeFlags |= RUNTIME_THROW; execute = false; - exeption = a->var->ref(); + exceptionVar = a->var->ref(); } else - throw new CScriptException("uncaught exeption: '"+a->var->getString()+"'", tokenStart); + throw new CScriptException("uncaught exception: '"+a->var->getString()+"'", tokenStart); } } else if (l->tk==LEX_R_SWITCH) { l->match(LEX_R_SWITCH); @@ -3135,10 +3136,10 @@ void CTinyJS::scEval(CScriptVar *c, void *data) { if(returnVar) c->setReturnVar(returnVar->var); - // check of exeptions - int exeption = runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE); + // check of exceptions + int exceptionState = runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE); runtimeFlags &= ~RUNTIME_LOOP_MASK; - if(exeption) throw new CScriptEvalException(exeption); + if(exceptionState) throw new CScriptEvalException(exceptionState); } catch (CScriptException *e) { scopes.push_back(scEvalScope); // restore Scopes; diff --git a/TinyJS.h b/TinyJS.h index b00e440..4c6bc38 100755 --- a/TinyJS.h +++ b/TinyJS.h @@ -286,7 +286,7 @@ class CScriptVar void setReturnVar(CScriptVar *var); ///< Set the result value. Use this when setting complex return data as it avoids a deepCopy() CScriptVar *getParameter(const std::string &name); ///< If this is a function, get the parameter with the given name (for use by native functions) CScriptVar *getParameter(int Idx); ///< If this is a function, get the parameter with the given index (for use by native functions) - + int getParameterLength(); ///< If this is a function, get the count of parameters CScriptVarLink *findChild(const std::string &childName); ///< Tries to find a child with the given name, may return 0 CScriptVarLink *findChildOrCreate(const std::string &childName, int varFlags=SCRIPTVAR_UNDEFINED); ///< Tries to find a child with the given name, or will create it with the given flags CScriptVarLink *findChildOrCreateByPath(const std::string &path); ///< Tries to find a child with the given path (separated by dots) @@ -479,7 +479,7 @@ class CTinyJS { CScriptVar *stringClass; /// Built in string class CScriptVar *objectClass; /// Built in object class CScriptVar *arrayClass; /// Built in array class - CScriptVar *exeption; /// containing the exeption var by (runtimeFlags&RUNTIME_THROW) == true; + CScriptVar *exceptionVar; /// containing the exception var by (runtimeFlags&RUNTIME_THROW) == true; void CheckRightHandVar(bool &execute, CScriptVarSmartLink &link, int pos=-1) { if(execute && link && !link->owned && !link->owner && link->name.length()>0) diff --git a/TinyJS_Functions.cpp b/TinyJS_Functions.cpp index 64fbde4..ad9eeba 100755 --- a/TinyJS_Functions.cpp +++ b/TinyJS_Functions.cpp @@ -55,11 +55,10 @@ using namespace std; void scTrace(CScriptVar *c, void * userdata) { CTinyJS *js = (CTinyJS*)userdata; - c=c->getParameter(0); - if(c->isUndefined()) - js->root->trace("root"); + if(c->getParameterLength()) + c->getParameter(0)->trace(); else - c->trace(); + js->root->trace("root"); } void scObjectDump(CScriptVar *c, void *) { From 7ab84f24d43a795a65a17c64c9ce1dc1d7576d84 Mon Sep 17 00:00:00 2001 From: armin Date: Tue, 5 Oct 2010 18:46:05 +0000 Subject: [PATCH 25/43] added: Visual Studio 2010 Project Files --- Script.vcxproj | 110 ++++++++++++++++++++++++++++++++++++++++++++ lib-tiny-js.vcxproj | 82 +++++++++++++++++++++++++++++++++ tiny-js.2010.sln | 25 ++++++++++ 3 files changed, 217 insertions(+) create mode 100644 Script.vcxproj create mode 100644 lib-tiny-js.vcxproj create mode 100644 tiny-js.2010.sln diff --git a/Script.vcxproj b/Script.vcxproj new file mode 100644 index 0000000..7de64ff --- /dev/null +++ b/Script.vcxproj @@ -0,0 +1,110 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {70D538BA-867B-4564-8DEE-F2C78C5AD4C8} + Script + Win32Proj + + + + Application + false + false + MultiByte + true + + + Application + false + false + MultiByte + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + $(SolutionDir)$(Configuration)\ + $(ProjectName)\$(Configuration)\ + true + $(SolutionDir)$(Configuration)\ + $(ProjectName)\$(Configuration)\ + false + + + + Disabled + %(AdditionalIncludeDirectories) + WIN32;_DEBUG;_CONSOLE;_AFXDLL;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + + Level4 + EditAndContinue + + + true + Console + MachineX86 + + + + + MaxSpeed + true + %(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CONSOLE;_AFXDLL;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + + Level3 + ProgramDatabase + + + true + Console + true + true + MachineX86 + + + + + + + + + + + + + + + {9751c465-0294-43cd-a5d9-bd038aba3961} + false + + + + + + \ No newline at end of file diff --git a/lib-tiny-js.vcxproj b/lib-tiny-js.vcxproj new file mode 100644 index 0000000..bbdeb43 --- /dev/null +++ b/lib-tiny-js.vcxproj @@ -0,0 +1,82 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {9751C465-0294-43CD-A5D9-BD038ABA3961} + lib-tiny-js + + + + StaticLibrary + false + MultiByte + true + false + + + StaticLibrary + false + false + MultiByte + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + $(SolutionDir)$(Configuration)\ + $(ProjectName)\$(Configuration)\ + $(SolutionDir)$(Configuration)\ + $(ProjectName)\$(Configuration)\ + + + + Disabled + WIN32;_DEBUG;_AFXDLL;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + Level3 + EditAndContinue + + + + + Full + true + WIN32;NDEBUG;_AFXDLL;%(PreprocessorDefinitions) + MultiThreadedDLL + true + Level3 + ProgramDatabase + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tiny-js.2010.sln b/tiny-js.2010.sln new file mode 100644 index 0000000..299fdd0 --- /dev/null +++ b/tiny-js.2010.sln @@ -0,0 +1,25 @@ +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Script", "Script.vcxproj", "{70D538BA-867B-4564-8DEE-F2C78C5AD4C8}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lib-tiny-js", "lib-tiny-js.vcxproj", "{9751C465-0294-43CD-A5D9-BD038ABA3961}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {70D538BA-867B-4564-8DEE-F2C78C5AD4C8}.Debug|Win32.ActiveCfg = Debug|Win32 + {70D538BA-867B-4564-8DEE-F2C78C5AD4C8}.Debug|Win32.Build.0 = Debug|Win32 + {70D538BA-867B-4564-8DEE-F2C78C5AD4C8}.Release|Win32.ActiveCfg = Release|Win32 + {70D538BA-867B-4564-8DEE-F2C78C5AD4C8}.Release|Win32.Build.0 = Release|Win32 + {9751C465-0294-43CD-A5D9-BD038ABA3961}.Debug|Win32.ActiveCfg = Debug|Win32 + {9751C465-0294-43CD-A5D9-BD038ABA3961}.Debug|Win32.Build.0 = Debug|Win32 + {9751C465-0294-43CD-A5D9-BD038ABA3961}.Release|Win32.ActiveCfg = Release|Win32 + {9751C465-0294-43CD-A5D9-BD038ABA3961}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal From d8c86e05907d3bb5802459565761b4f0635f7e35 Mon Sep 17 00:00:00 2001 From: armin Date: Fri, 25 Nov 2011 16:52:40 +0000 Subject: [PATCH 26/43] 42tiny-js: added: run_tests project to the project-map (only VisualStudio 10) NOTE: VisualStudio 9 is no more supported. The VisualStudio 9 Projectfiles will be removed in the future added: math-functions --- Script.cpp | 2 + Script.vcxproj.filters | 33 +++++++++++++ lib-tiny-js.vcxproj | 2 + lib-tiny-js.vcxproj.filters | 42 ++++++++++++++++ run_tests.cpp | 3 ++ run_tests.vcxproj | 96 +++++++++++++++++++++++++++++++++++++ run_tests.vcxproj.filters | 33 +++++++++++++ tiny-js.2010.sln | 9 ++++ 8 files changed, 220 insertions(+) create mode 100644 Script.vcxproj.filters create mode 100644 lib-tiny-js.vcxproj.filters create mode 100644 run_tests.vcxproj create mode 100644 run_tests.vcxproj.filters diff --git a/Script.cpp b/Script.cpp index 44c99b6..9712318 100755 --- a/Script.cpp +++ b/Script.cpp @@ -33,6 +33,7 @@ #endif #include "TinyJS.h" #include "TinyJS_Functions.h" +#include "TinyJS_MathFunctions.h" #include #include @@ -71,6 +72,7 @@ int main(int UNUSED(argc), char **UNUSED(argv)) CTinyJS *js = new CTinyJS(); /* add the functions from TinyJS_Functions.cpp */ registerFunctions(js); + registerMathFunctions(js); /* Add a native function */ js->addNative("function print(text)", &js_print, 0); js->addNative("function dump()", &js_dump, js); diff --git a/Script.vcxproj.filters b/Script.vcxproj.filters new file mode 100644 index 0000000..1f6996d --- /dev/null +++ b/Script.vcxproj.filters @@ -0,0 +1,33 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav + + + + + Quelldateien + + + + + Headerdateien + + + Headerdateien + + + + + + \ No newline at end of file diff --git a/lib-tiny-js.vcxproj b/lib-tiny-js.vcxproj index bbdeb43..0a811a2 100644 --- a/lib-tiny-js.vcxproj +++ b/lib-tiny-js.vcxproj @@ -70,11 +70,13 @@ + + diff --git a/lib-tiny-js.vcxproj.filters b/lib-tiny-js.vcxproj.filters new file mode 100644 index 0000000..189584d --- /dev/null +++ b/lib-tiny-js.vcxproj.filters @@ -0,0 +1,42 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav + + + + + Quelldateien + + + Quelldateien + + + Quelldateien + + + + + Headerdateien + + + Headerdateien + + + Headerdateien + + + Headerdateien + + + \ No newline at end of file diff --git a/run_tests.cpp b/run_tests.cpp index e79af69..f29849a 100644 --- a/run_tests.cpp +++ b/run_tests.cpp @@ -29,6 +29,7 @@ #include "TinyJS.h" #include "TinyJS_Functions.h" +#include "TinyJS_MathFunctions.h" #include #include #include @@ -206,11 +207,13 @@ bool run_test(const char *filename) { CTinyJS s; registerFunctions(&s); + registerMathFunctions(&s); s.root->addChild("result", new CScriptVar("0",SCRIPTVAR_INTEGER)); try { s.execute(buffer); } catch (CScriptException *e) { printf("ERROR: %s\n", e->text.c_str()); + delete e; } bool pass = s.root->getParameter("result")->getBool(); diff --git a/run_tests.vcxproj b/run_tests.vcxproj new file mode 100644 index 0000000..fb53257 --- /dev/null +++ b/run_tests.vcxproj @@ -0,0 +1,96 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {C6C6CD82-1400-464C-9B80-E15450CFDD18} + Win32Proj + run_tests + + + + Application + true + Unicode + + + Application + false + true + Unicode + + + + + + + + + + + + + true + Build\$(ProjectName)\$(Configuration)\ + + + false + Build\$(ProjectName)\$(Configuration)\ + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + true + + + + + + + + + + + + + + + {9751c465-0294-43cd-a5d9-bd038aba3961} + + + + + + \ No newline at end of file diff --git a/run_tests.vcxproj.filters b/run_tests.vcxproj.filters new file mode 100644 index 0000000..87407e0 --- /dev/null +++ b/run_tests.vcxproj.filters @@ -0,0 +1,33 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + Headerdateien + + + Headerdateien + + + + + Quelldateien + + + \ No newline at end of file diff --git a/tiny-js.2010.sln b/tiny-js.2010.sln index 299fdd0..5c40b0e 100644 --- a/tiny-js.2010.sln +++ b/tiny-js.2010.sln @@ -4,6 +4,11 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Script", "Script.vcxproj", EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lib-tiny-js", "lib-tiny-js.vcxproj", "{9751C465-0294-43CD-A5D9-BD038ABA3961}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "run_tests", "run_tests.vcxproj", "{C6C6CD82-1400-464C-9B80-E15450CFDD18}" + ProjectSection(ProjectDependencies) = postProject + {9751C465-0294-43CD-A5D9-BD038ABA3961} = {9751C465-0294-43CD-A5D9-BD038ABA3961} + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -18,6 +23,10 @@ Global {9751C465-0294-43CD-A5D9-BD038ABA3961}.Debug|Win32.Build.0 = Debug|Win32 {9751C465-0294-43CD-A5D9-BD038ABA3961}.Release|Win32.ActiveCfg = Release|Win32 {9751C465-0294-43CD-A5D9-BD038ABA3961}.Release|Win32.Build.0 = Release|Win32 + {C6C6CD82-1400-464C-9B80-E15450CFDD18}.Debug|Win32.ActiveCfg = Debug|Win32 + {C6C6CD82-1400-464C-9B80-E15450CFDD18}.Debug|Win32.Build.0 = Debug|Win32 + {C6C6CD82-1400-464C-9B80-E15450CFDD18}.Release|Win32.ActiveCfg = Release|Win32 + {C6C6CD82-1400-464C-9B80-E15450CFDD18}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 54f47eb499994759c0d84b9d5d1d33665bb11e25 Mon Sep 17 00:00:00 2001 From: armin Date: Fri, 25 Nov 2011 16:58:13 +0000 Subject: [PATCH 27/43] 42tiny-js: oops --- TinyJS_MathFunctions.cpp | 264 +++++++++++++++++++++++++++++++++++++++ TinyJS_MathFunctions.h | 9 ++ 2 files changed, 273 insertions(+) create mode 100644 TinyJS_MathFunctions.cpp create mode 100644 TinyJS_MathFunctions.h diff --git a/TinyJS_MathFunctions.cpp b/TinyJS_MathFunctions.cpp new file mode 100644 index 0000000..ea29cd8 --- /dev/null +++ b/TinyJS_MathFunctions.cpp @@ -0,0 +1,264 @@ +/* + * TinyJS + * + * A single-file Javascript-alike engine + * + * - Math and Trigonometry functions - + * + */ + +#include +#include +#include +#include "TinyJS_MathFunctions.h" + +using namespace std; + +#define k_E exp(1.0) +#define k_PI 3.1415926535897932384626433832795 + +#define F_ABS(a) ((a)>=0 ? (a) : (-(a))) +#define F_MIN(a,b) ((a)>(b) ? (b) : (a)) +#define F_MAX(a,b) ((a)>(b) ? (a) : (b)) +#define F_SGN(a) ((a)>0 ? 1 : ((a)<0 ? -1 : 0 )) +#define F_RNG(a,min,max) ((a)<(min) ? min : ((a)>(max) ? max : a )) +#define F_ROUND(a) ((a)>0 ? (int) ((a)+0.5) : (int) ((a)-0.5) ) + +//CScriptVar shortcut macro +#define scIsInt(a) ( c->getParameter(a)->isInt() ) +#define scIsDouble(a) ( c->getParameter(a)->isDouble() ) +#define scGetInt(a) ( c->getParameter(a)->getInt() ) +#define scGetDouble(a) ( c->getParameter(a)->getDouble() ) +#define scReturnInt(a) ( c->getReturnVar()->setInt(a) ) +#define scReturnDouble(a) ( c->getReturnVar()->setDouble(a) ) + +#ifdef _MSC_VER +namespace +{ + double asinh( const double &value ) + { + double returned; + + if(value>0) + returned = log(value + sqrt(value * value + 1)); + else + returned = -log(-value + sqrt(value * value + 1)); + + return(returned); + } + + double acosh( const double &value ) + { + double returned; + + if(value>0) + returned = log(value + sqrt(value * value - 1)); + else + returned = -log(-value + sqrt(value * value - 1)); + + return(returned); + } +} +#endif + +//Math.abs(x) - returns absolute of given value +void scMathAbs(CScriptVar *c, void *userdata) { + if ( scIsInt("a") ) { + scReturnInt( F_ABS( scGetInt("a") ) ); + } else if ( scIsDouble("a") ) { + scReturnDouble( F_ABS( scGetDouble("a") ) ); + } +} + +//Math.round(a) - returns nearest round of given value +void scMathRound(CScriptVar *c, void *userdata) { + if ( scIsInt("a") ) { + scReturnInt( F_ROUND( scGetInt("a") ) ); + } else if ( scIsDouble("a") ) { + scReturnDouble( F_ROUND( scGetDouble("a") ) ); + } +} + +//Math.min(a,b) - returns minimum of two given values +void scMathMin(CScriptVar *c, void *userdata) { + if ( (scIsInt("a")) && (scIsInt("b")) ) { + scReturnInt( F_MIN( scGetInt("a"), scGetInt("b") ) ); + } else { + scReturnDouble( F_MIN( scGetDouble("a"), scGetDouble("b") ) ); + } +} + +//Math.max(a,b) - returns maximum of two given values +void scMathMax(CScriptVar *c, void *userdata) { + if ( (scIsInt("a")) && (scIsInt("b")) ) { + scReturnInt( F_MAX( scGetInt("a"), scGetInt("b") ) ); + } else { + scReturnDouble( F_MAX( scGetDouble("a"), scGetDouble("b") ) ); + } +} + +//Math.range(x,a,b) - returns value limited between two given values +void scMathRange(CScriptVar *c, void *userdata) { + if ( (scIsInt("x")) ) { + scReturnInt( F_RNG( scGetInt("x"), scGetInt("a"), scGetInt("b") ) ); + } else { + scReturnDouble( F_RNG( scGetDouble("x"), scGetDouble("a"), scGetDouble("b") ) ); + } +} + +//Math.sign(a) - returns sign of given value (-1==negative,0=zero,1=positive) +void scMathSign(CScriptVar *c, void *userdata) { + if ( scIsInt("a") ) { + scReturnInt( F_SGN( scGetInt("a") ) ); + } else if ( scIsDouble("a") ) { + scReturnDouble( F_SGN( scGetDouble("a") ) ); + } +} + +//Math.PI() - returns PI value +void scMathPI(CScriptVar *c, void *userdata) { + scReturnDouble(k_PI); +} + +//Math.toDegrees(a) - returns degree value of a given angle in radians +void scMathToDegrees(CScriptVar *c, void *userdata) { + scReturnDouble( (180.0/k_PI)*( scGetDouble("a") ) ); +} + +//Math.toRadians(a) - returns radians value of a given angle in degrees +void scMathToRadians(CScriptVar *c, void *userdata) { + scReturnDouble( (k_PI/180.0)*( scGetDouble("a") ) ); +} + +//Math.sin(a) - returns trig. sine of given angle in radians +void scMathSin(CScriptVar *c, void *userdata) { + scReturnDouble( sin( scGetDouble("a") ) ); +} + +//Math.asin(a) - returns trig. arcsine of given angle in radians +void scMathASin(CScriptVar *c, void *userdata) { + scReturnDouble( asin( scGetDouble("a") ) ); +} + +//Math.cos(a) - returns trig. cosine of given angle in radians +void scMathCos(CScriptVar *c, void *userdata) { + scReturnDouble( cos( scGetDouble("a") ) ); +} + +//Math.acos(a) - returns trig. arccosine of given angle in radians +void scMathACos(CScriptVar *c, void *userdata) { + scReturnDouble( acos( scGetDouble("a") ) ); +} + +//Math.tan(a) - returns trig. tangent of given angle in radians +void scMathTan(CScriptVar *c, void *userdata) { + scReturnDouble( tan( scGetDouble("a") ) ); +} + +//Math.atan(a) - returns trig. arctangent of given angle in radians +void scMathATan(CScriptVar *c, void *userdata) { + scReturnDouble( atan( scGetDouble("a") ) ); +} + +//Math.sinh(a) - returns trig. hyperbolic sine of given angle in radians +void scMathSinh(CScriptVar *c, void *userdata) { + scReturnDouble( sinh( scGetDouble("a") ) ); +} + +//Math.asinh(a) - returns trig. hyperbolic arcsine of given angle in radians +void scMathASinh(CScriptVar *c, void *userdata) { + scReturnDouble( asinh( scGetDouble("a") ) ); +} + +//Math.cosh(a) - returns trig. hyperbolic cosine of given angle in radians +void scMathCosh(CScriptVar *c, void *userdata) { + scReturnDouble( cosh( scGetDouble("a") ) ); +} + +//Math.acosh(a) - returns trig. hyperbolic arccosine of given angle in radians +void scMathACosh(CScriptVar *c, void *userdata) { + scReturnDouble( acosh( scGetDouble("a") ) ); +} + +//Math.tanh(a) - returns trig. hyperbolic tangent of given angle in radians +void scMathTanh(CScriptVar *c, void *userdata) { + scReturnDouble( tanh( scGetDouble("a") ) ); +} + +//Math.atan(a) - returns trig. hyperbolic arctangent of given angle in radians +void scMathATanh(CScriptVar *c, void *userdata) { + scReturnDouble( atan( scGetDouble("a") ) ); +} + +//Math.E() - returns E Neplero value +void scMathE(CScriptVar *c, void *userdata) { + scReturnDouble(k_E); +} + +//Math.log(a) - returns natural logaritm (base E) of given value +void scMathLog(CScriptVar *c, void *userdata) { + scReturnDouble( log( scGetDouble("a") ) ); +} + +//Math.log10(a) - returns logaritm(base 10) of given value +void scMathLog10(CScriptVar *c, void *userdata) { + scReturnDouble( log10( scGetDouble("a") ) ); +} + +//Math.exp(a) - returns e raised to the power of a given number +void scMathExp(CScriptVar *c, void *userdata) { + scReturnDouble( exp( scGetDouble("a") ) ); +} + +//Math.pow(a,b) - returns the result of a number raised to a power (a)^(b) +void scMathPow(CScriptVar *c, void *userdata) { + scReturnDouble( pow( scGetDouble("a"), scGetDouble("b") ) ); +} + +//Math.sqr(a) - returns square of given value +void scMathSqr(CScriptVar *c, void *userdata) { + scReturnDouble( ( scGetDouble("a") * scGetDouble("a") ) ); +} + +//Math.sqrt(a) - returns square root of given value +void scMathSqrt(CScriptVar *c, void *userdata) { + scReturnDouble( sqrt( scGetDouble("a") ) ); +} + +// ----------------------------------------------- Register Functions +void registerMathFunctions(CTinyJS *tinyJS) { + + // --- Math and Trigonometry functions --- + tinyJS->addNative("function Math.abs(a)", scMathAbs, 0); + tinyJS->addNative("function Math.round(a)", scMathRound, 0); + tinyJS->addNative("function Math.min(a,b)", scMathMin, 0); + tinyJS->addNative("function Math.max(a,b)", scMathMax, 0); + tinyJS->addNative("function Math.range(x,a,b)", scMathRange, 0); + tinyJS->addNative("function Math.sign(a)", scMathSign, 0); + + tinyJS->addNative("function Math.PI()", scMathPI, 0); + tinyJS->addNative("function Math.toDegrees(a)", scMathToDegrees, 0); + tinyJS->addNative("function Math.toRadians(a)", scMathToRadians, 0); + tinyJS->addNative("function Math.sin(a)", scMathSin, 0); + tinyJS->addNative("function Math.asin(a)", scMathASin, 0); + tinyJS->addNative("function Math.cos(a)", scMathCos, 0); + tinyJS->addNative("function Math.acos(a)", scMathACos, 0); + tinyJS->addNative("function Math.tan(a)", scMathTan, 0); + tinyJS->addNative("function Math.atan(a)", scMathATan, 0); + tinyJS->addNative("function Math.sinh(a)", scMathSinh, 0); + tinyJS->addNative("function Math.asinh(a)", scMathASinh, 0); + tinyJS->addNative("function Math.cosh(a)", scMathCosh, 0); + tinyJS->addNative("function Math.acosh(a)", scMathACosh, 0); + tinyJS->addNative("function Math.tanh(a)", scMathTanh, 0); + tinyJS->addNative("function Math.atanh(a)", scMathATanh, 0); + + tinyJS->addNative("function Math.E()", scMathE, 0); + tinyJS->addNative("function Math.log(a)", scMathLog, 0); + tinyJS->addNative("function Math.log10(a)", scMathLog10, 0); + tinyJS->addNative("function Math.exp(a)", scMathExp, 0); + tinyJS->addNative("function Math.pow(a,b)", scMathPow, 0); + + tinyJS->addNative("function Math.sqr(a)", scMathSqr, 0); + tinyJS->addNative("function Math.sqrt(a)", scMathSqrt, 0); + +} diff --git a/TinyJS_MathFunctions.h b/TinyJS_MathFunctions.h new file mode 100644 index 0000000..935fdd9 --- /dev/null +++ b/TinyJS_MathFunctions.h @@ -0,0 +1,9 @@ +#ifndef TINYJS_MATHFUNCTIONS_H +#define TINYJS_MATHFUNCTIONS_H + +#include "TinyJS.h" + +/// Register useful math. functions with the TinyJS interpreter +extern void registerMathFunctions(CTinyJS *tinyJS); + +#endif From 0860b72a941f350ee89a860b999ae05f13583ae5 Mon Sep 17 00:00:00 2001 From: armin Date: Fri, 25 Nov 2011 18:15:54 +0000 Subject: [PATCH 28/43] 42tiny-js: fixed: tests 007, 010, 015, 025, 026, 028 fixed: tests 008, 014, 017, 018, 021, 023 (by change testXXX.js to correct JavaScript syntax) todo: tests 016, 019, 022, 024 --- TinyJS.cpp | 2 +- tests/test008.js | 5 ++++- tests/test014.js | 4 +++- tests/test017.js | 6 +++++- tests/test018.js | 5 ++++- tests/test021.js | 6 +++++- tests/test023.js | 3 +++ 7 files changed, 25 insertions(+), 6 deletions(-) diff --git a/TinyJS.cpp b/TinyJS.cpp index b440361..1dd98e3 100755 --- a/TinyJS.cpp +++ b/TinyJS.cpp @@ -1032,7 +1032,7 @@ int CScriptVar::getArrayLength() { int CScriptVar::getInt() { /* strtol understands about hex and octal */ - if (isInt()) return intData; + if (isInt() || isBool()) return intData; // if (isNull()) return 0; // if (isUndefined()) return 0; if (isDouble()) return (int)doubleData; diff --git a/tests/test008.js b/tests/test008.js index 8596ee2..29f0e28 100644 --- a/tests/test008.js +++ b/tests/test008.js @@ -1,5 +1,8 @@ // functions in variables -var bob; +// 42-tiny-js change begin ---> +//var bob; // in JavaScript it's not allowed to add a child to a var with type "undefined" +var bob = {}; +//<--- 42-tiny-js change end bob.add = function(x,y) { return x+y; }; result = bob.add(3,6)==9; diff --git a/tests/test014.js b/tests/test014.js index 9c2bb30..7f2fec3 100644 --- a/tests/test014.js +++ b/tests/test014.js @@ -1,7 +1,9 @@ // Variable creation and scope from http://en.wikipedia.org/wiki/JavaScript_syntax x = 0; // A global variable var y = 'Hello!'; // Another global variable - +// 42-tiny-js change begin ---> +z = 0; // a var is only created automated by assignment +//<--- 42-tiny-js change end function f(){ var z = 'foxes'; // A local variable twenty = 20; // Global because keyword var is not used diff --git a/tests/test017.js b/tests/test017.js index 0f6be1b..a778e29 100644 --- a/tests/test017.js +++ b/tests/test017.js @@ -1,6 +1,10 @@ // references for arrays -var a; +// 42-tiny-js change begin ---> +//var a; // in JavaScript it's not allowed to add array elements to a var with type "undefined" +var a = []; +//<--- 42-tiny-js change end + a[0] = 10; a[1] = 22; diff --git a/tests/test018.js b/tests/test018.js index 52da3d1..fba02bb 100644 --- a/tests/test018.js +++ b/tests/test018.js @@ -1,7 +1,10 @@ // references with functions var a = 42; -var b; +// 42-tiny-js change begin ---> +//var b; // in JavaScript it's not allowed to add array elements to a var with type "undefined" +var b = []; +//<--- 42-tiny-js change end b[0] = 43; function foo(myarray) { diff --git a/tests/test021.js b/tests/test021.js index d94e231..a11cc73 100644 --- a/tests/test021.js +++ b/tests/test021.js @@ -1,5 +1,9 @@ /* Javascript eval */ -myfoo = eval("{ foo: 42 }"); +// 42-tiny-js change begin ---> +// in JavaScript eval is not a JSON-parser +//myfoo = eval("{ foo: 42 }"); +myfoo = eval("tmp = { foo: 42 }"); +//<--- 42-tiny-js change end result = eval("4*10+2")==42 && myfoo.foo==42; diff --git a/tests/test023.js b/tests/test023.js index 619c486..b0ecb14 100644 --- a/tests/test023.js +++ b/tests/test023.js @@ -1,4 +1,7 @@ // mikael.kindborg@mobilesorcery.com - Function symbol is evaluated in bracket-less body of false if-statement +// 42-tiny-js change begin ---> +var foo; // a var is only created automated by assignment +//<--- 42-tiny-js change end if (foo !== undefined) foo(); From 0230327d23386cb9c372d507445fc22ac72d023c Mon Sep 17 00:00:00 2001 From: armin Date: Sat, 26 Nov 2011 00:10:49 +0000 Subject: [PATCH 29/43] 42tiny-js: fixed: test016 --- TinyJS.cpp | 41 ++++++++++++++++++++++++++++++----------- tests/test016.js | 14 +++++++++----- 2 files changed, 39 insertions(+), 16 deletions(-) diff --git a/TinyJS.cpp b/TinyJS.cpp index 1dd98e3..9ba71fa 100755 --- a/TinyJS.cpp +++ b/TinyJS.cpp @@ -1171,20 +1171,25 @@ CScriptVar *CScriptVar::mathsOp(CScriptVar *b, int op) { // Type equality check if (op == LEX_TYPEEQUAL || op == LEX_NTYPEEQUAL) { // check type first, then call again to check data - bool eql = ((a->flags & SCRIPTVAR_VARTYPEMASK) == - (b->flags & SCRIPTVAR_VARTYPEMASK)) && - a->mathsOp(b, LEX_EQUAL); + bool eql = false; + if(!a->isNaN() && !b->isNaN() && + ((a->flags & SCRIPTVAR_VARTYPEMASK) == + (b->flags & SCRIPTVAR_VARTYPEMASK))) { + CScriptVar * e = a->mathsOp(b, LEX_EQUAL); + eql = e->getBool(); + delete e; + } if (op == LEX_TYPEEQUAL) return new CScriptVar(eql); else return new CScriptVar(!eql); } // do maths... - if((a->isNaN() || a->isUndefined() || b->isNaN() || b->isUndefined()) && !a->isString() && !b->isString()) { + if( a->isNaN() || b->isNaN() || ((a->isUndefined() || b->isUndefined()) && !a->isString() && !b->isString())) { switch (op) { - case LEX_NEQUAL: return new CScriptVar(!(a->isUndefined() && b->isUndefined())); - case LEX_EQUAL: return new CScriptVar((a->isUndefined() && b->isUndefined())); - case LEX_GEQUAL: + case LEX_NEQUAL: return new CScriptVar( !( ( a->isUndefined() || a->isNull() ) && ( b->isUndefined() || b->isNull() ) ) ); + case LEX_EQUAL: return new CScriptVar( ( a->isUndefined() || a->isNull() ) && ( b->isUndefined() || b->isNull() ) ); + case LEX_GEQUAL: case LEX_LEQUAL: case '<': case '>': return new CScriptVar(false); @@ -1201,7 +1206,7 @@ CScriptVar *CScriptVar::mathsOp(CScriptVar *b, int op) { } else if((a->isInfinity() || b->isInfinity()) && !a->isString() && !b->isString()) { switch (op) { case LEX_EQUAL: - case LEX_GEQUAL: + case LEX_GEQUAL: case LEX_LEQUAL: return new CScriptVar(a->isInfinity() == b->isInfinity()); case LEX_NEQUAL: return new CScriptVar(a->isInfinity() != b->isInfinity()); case '<': @@ -1227,18 +1232,20 @@ CScriptVar *CScriptVar::mathsOp(CScriptVar *b, int op) { } else { //Strings string da = a->getString(); string db = b->getString(); +/* if((a->isNumeric() || b->isNumeric()) && op != '+') { - CScriptVar *ret = NULL; + CScriptVar *ret = 0; CScriptVar *a_num = String2NumericVar(da); CScriptVar *b_num = String2NumericVar(db); if(a_num && b_num) ret = a_num->mathsOp(b_num, op); if(a_num) delete a_num; if(b_num) delete b_num; - if(ret) + if(ret) return ret; } +*/ // use strings switch (op) { case '+': return new CScriptVar(da+db, SCRIPTVAR_STRING); @@ -1248,7 +1255,19 @@ CScriptVar *CScriptVar::mathsOp(CScriptVar *b, int op) { case LEX_LEQUAL: return new CScriptVar(da<=db); case '>': return new CScriptVar(da>db); case LEX_GEQUAL: return new CScriptVar(da>=db); - default: return new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_NAN); + default: + { + CScriptVar *ret = 0; + CScriptVar *a_num = String2NumericVar(da); + CScriptVar *b_num = String2NumericVar(db); + if(a_num && b_num) + ret = a_num->mathsOp(b_num, op); + if(a_num) delete a_num; + if(b_num) delete b_num; + if(ret) + return ret; + } + return new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_NAN); } } ASSERT(0); diff --git a/tests/test016.js b/tests/test016.js index 4dcc4f6..33ada4c 100644 --- a/tests/test016.js +++ b/tests/test016.js @@ -1,15 +1,19 @@ // Undefined/null from http://en.wikipedia.org/wiki/JavaScript_syntax var testUndefined; // variable declared but not defined, set to value of undefined -var testObj = {}; +var testObj = {}; result = 1; -if ((""+test) != "undefined") result = 0; // test variable exists but value not defined, displays undefined +// 42-tiny-js change begin ---> +// test variable exists NOT but testUndefined +//if ((""+test) != "undefined") result = 0; // test variable exists but value not defined, displays undefined +if ((""+testUndefined) != "undefined") result = 0; // test variable exists but value not defined, displays undefined +//<--- 42-tiny-js change end if ((""+testObj.myProp) != "undefined") result = 0; // testObj exists, property does not, displays undefined if (!(undefined == null)) result = 0; // unenforced type during check, displays true -if (undefined === null) result = 0;// enforce type during check, displays false +//if (undefined === null) result = 0;// enforce type during check, displays false -if (null != undefined) result = 0; // unenforced type during check, displays true -if (null === undefined) result = 0; // enforce type during check, displays false +//if (null != undefined) result = 0; // unenforced type during check, displays true +//if (null === undefined) result = 0; // enforce type during check, displays false From bc15388f828d1e6e96b034a6afc2ed63a6b9dbc8 Mon Sep 17 00:00:00 2001 From: armin Date: Sat, 26 Nov 2011 00:14:01 +0000 Subject: [PATCH 30/43] 42tiny-js: Oops ... again --- tests/test016.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test016.js b/tests/test016.js index 33ada4c..92bb024 100644 --- a/tests/test016.js +++ b/tests/test016.js @@ -10,10 +10,10 @@ if ((""+testUndefined) != "undefined") result = 0; // test variable exists but v //<--- 42-tiny-js change end if ((""+testObj.myProp) != "undefined") result = 0; // testObj exists, property does not, displays undefined if (!(undefined == null)) result = 0; // unenforced type during check, displays true -//if (undefined === null) result = 0;// enforce type during check, displays false +if (undefined === null) result = 0;// enforce type during check, displays false -//if (null != undefined) result = 0; // unenforced type during check, displays true -//if (null === undefined) result = 0; // enforce type during check, displays false +if (null != undefined) result = 0; // unenforced type during check, displays true +if (null === undefined) result = 0; // enforce type during check, displays false From 575d14fe1357094e72b1e4f12c526f9f3150a7d9 Mon Sep 17 00:00:00 2001 From: armin Date: Fri, 2 Dec 2011 14:00:16 +0000 Subject: [PATCH 31/43] 42tiny-js: remove tests-folder --- tests/test001.js | 2 -- tests/test002.js | 3 --- tests/test003.js | 5 ---- tests/test004.js | 4 --- tests/test005.js | 4 --- tests/test006.js | 3 --- tests/test007.js | 4 --- tests/test008.js | 8 ------ tests/test009.js | 4 --- tests/test010.js | 4 --- tests/test011.js | 7 ----- tests/test012.js | 6 ----- tests/test013.js | 7 ----- tests/test014.js | 17 ------------- tests/test015.js | 8 ------ tests/test016.js | 19 -------------- tests/test017.js | 15 ----------- tests/test018.js | 21 --------------- tests/test019.js | 19 -------------- tests/test020.js | 27 -------------------- tests/test021.js | 9 ------- tests/test022.js | 9 ------- tests/test023.js | 8 ------ tests/test024.js | 66 ------------------------------------------------ tests/test025.js | 7 ----- tests/test026.js | 4 --- tests/test027.js | 3 --- tests/test028.js | 12 --------- 28 files changed, 305 deletions(-) delete mode 100644 tests/test001.js delete mode 100644 tests/test002.js delete mode 100644 tests/test003.js delete mode 100644 tests/test004.js delete mode 100644 tests/test005.js delete mode 100644 tests/test006.js delete mode 100644 tests/test007.js delete mode 100644 tests/test008.js delete mode 100644 tests/test009.js delete mode 100644 tests/test010.js delete mode 100644 tests/test011.js delete mode 100644 tests/test012.js delete mode 100644 tests/test013.js delete mode 100644 tests/test014.js delete mode 100644 tests/test015.js delete mode 100644 tests/test016.js delete mode 100644 tests/test017.js delete mode 100644 tests/test018.js delete mode 100644 tests/test019.js delete mode 100644 tests/test020.js delete mode 100644 tests/test021.js delete mode 100644 tests/test022.js delete mode 100644 tests/test023.js delete mode 100644 tests/test024.js delete mode 100644 tests/test025.js delete mode 100644 tests/test026.js delete mode 100644 tests/test027.js delete mode 100644 tests/test028.js diff --git a/tests/test001.js b/tests/test001.js deleted file mode 100644 index e0d95dc..0000000 --- a/tests/test001.js +++ /dev/null @@ -1,2 +0,0 @@ -// simply testing we can return the correct value -result = 1; diff --git a/tests/test002.js b/tests/test002.js deleted file mode 100644 index cebaa85..0000000 --- a/tests/test002.js +++ /dev/null @@ -1,3 +0,0 @@ -// comparison -var a = 42; -result = a==42; diff --git a/tests/test003.js b/tests/test003.js deleted file mode 100644 index 55126fa..0000000 --- a/tests/test003.js +++ /dev/null @@ -1,5 +0,0 @@ -// simple for loop -var a = 0; -var i; -for (i=1;i<10;i++) a = a + i; -result = a==45; diff --git a/tests/test004.js b/tests/test004.js deleted file mode 100644 index 2f07c8b..0000000 --- a/tests/test004.js +++ /dev/null @@ -1,4 +0,0 @@ -// simple if -var a = 42; -if (a < 43) - result = 1; diff --git a/tests/test005.js b/tests/test005.js deleted file mode 100644 index f944064..0000000 --- a/tests/test005.js +++ /dev/null @@ -1,4 +0,0 @@ -// simple for loop containing initialisation, using += -var a = 0; -for (var i=1;i<10;i++) a += i; -result = a==45; diff --git a/tests/test006.js b/tests/test006.js deleted file mode 100644 index 292ad5e..0000000 --- a/tests/test006.js +++ /dev/null @@ -1,3 +0,0 @@ -// simple function -function add(x,y) { return x+y; } -result = add(3,6)==9; diff --git a/tests/test007.js b/tests/test007.js deleted file mode 100644 index 9a43bc6..0000000 --- a/tests/test007.js +++ /dev/null @@ -1,4 +0,0 @@ -// simple function scoping test -var a = 7; -function add(x,y) { var a=x+y; return a; } -result = add(3,6)==9 && a==7; diff --git a/tests/test008.js b/tests/test008.js deleted file mode 100644 index 29f0e28..0000000 --- a/tests/test008.js +++ /dev/null @@ -1,8 +0,0 @@ -// functions in variables -// 42-tiny-js change begin ---> -//var bob; // in JavaScript it's not allowed to add a child to a var with type "undefined" -var bob = {}; -//<--- 42-tiny-js change end -bob.add = function(x,y) { return x+y; }; - -result = bob.add(3,6)==9; diff --git a/tests/test009.js b/tests/test009.js deleted file mode 100644 index 08bf42e..0000000 --- a/tests/test009.js +++ /dev/null @@ -1,4 +0,0 @@ -// functions in variables using JSON-style initialisation -var bob = { add : function(x,y) { return x+y; } }; - -result = bob.add(3,6)==9; diff --git a/tests/test010.js b/tests/test010.js deleted file mode 100644 index 82e89da..0000000 --- a/tests/test010.js +++ /dev/null @@ -1,4 +0,0 @@ -// double function calls -function a(x) { return x+2; } -function b(x) { return a(x)+1; } -result = a(3)==5 && b(3)==6; diff --git a/tests/test011.js b/tests/test011.js deleted file mode 100644 index 4eaf641..0000000 --- a/tests/test011.js +++ /dev/null @@ -1,7 +0,0 @@ -// recursion -function a(x) { - if (x>1) - return x*a(x-1); - return 1; -} -result = a(5)==1*2*3*4*5; diff --git a/tests/test012.js b/tests/test012.js deleted file mode 100644 index 2c50344..0000000 --- a/tests/test012.js +++ /dev/null @@ -1,6 +0,0 @@ -// if .. else -var a = 42; -if (a != 42) - result = 0; -else - result = 1; diff --git a/tests/test013.js b/tests/test013.js deleted file mode 100644 index a2fb08f..0000000 --- a/tests/test013.js +++ /dev/null @@ -1,7 +0,0 @@ -// if .. else with blocks -var a = 42; -if (a != 42) { - result = 0; -} else { - result = 1; -} diff --git a/tests/test014.js b/tests/test014.js deleted file mode 100644 index 7f2fec3..0000000 --- a/tests/test014.js +++ /dev/null @@ -1,17 +0,0 @@ -// Variable creation and scope from http://en.wikipedia.org/wiki/JavaScript_syntax -x = 0; // A global variable -var y = 'Hello!'; // Another global variable -// 42-tiny-js change begin ---> -z = 0; // a var is only created automated by assignment -//<--- 42-tiny-js change end -function f(){ - var z = 'foxes'; // A local variable - twenty = 20; // Global because keyword var is not used - return x; // We can use x here because it is global -} -// The value of z is no longer available - - -// testing -blah = f(); -result = blah==0 && z!='foxes' && twenty==20; diff --git a/tests/test015.js b/tests/test015.js deleted file mode 100644 index 4a70a37..0000000 --- a/tests/test015.js +++ /dev/null @@ -1,8 +0,0 @@ -// Number definition from http://en.wikipedia.org/wiki/JavaScript_syntax -a = 345; // an "integer", although there is only one numeric type in JavaScript -b = 34.5; // a floating-point number -c = 3.45e2; // another floating-point, equivalent to 345 -d = 0377; // an octal integer equal to 255 -e = 0xFF; // a hexadecimal integer equal to 255, digits represented by the letters A-F may be upper or lowercase - -result = a==345 && b*10==345 && c==345 && d==255 && e==255; diff --git a/tests/test016.js b/tests/test016.js deleted file mode 100644 index 92bb024..0000000 --- a/tests/test016.js +++ /dev/null @@ -1,19 +0,0 @@ -// Undefined/null from http://en.wikipedia.org/wiki/JavaScript_syntax -var testUndefined; // variable declared but not defined, set to value of undefined -var testObj = {}; - -result = 1; -// 42-tiny-js change begin ---> -// test variable exists NOT but testUndefined -//if ((""+test) != "undefined") result = 0; // test variable exists but value not defined, displays undefined -if ((""+testUndefined) != "undefined") result = 0; // test variable exists but value not defined, displays undefined -//<--- 42-tiny-js change end -if ((""+testObj.myProp) != "undefined") result = 0; // testObj exists, property does not, displays undefined -if (!(undefined == null)) result = 0; // unenforced type during check, displays true -if (undefined === null) result = 0;// enforce type during check, displays false - - -if (null != undefined) result = 0; // unenforced type during check, displays true -if (null === undefined) result = 0; // enforce type during check, displays false - - diff --git a/tests/test017.js b/tests/test017.js deleted file mode 100644 index a778e29..0000000 --- a/tests/test017.js +++ /dev/null @@ -1,15 +0,0 @@ -// references for arrays - -// 42-tiny-js change begin ---> -//var a; // in JavaScript it's not allowed to add array elements to a var with type "undefined" -var a = []; -//<--- 42-tiny-js change end - -a[0] = 10; -a[1] = 22; - -b = a; - -b[0] = 5; - -result = a[0]==5 && a[1]==22 && b[1]==22; diff --git a/tests/test018.js b/tests/test018.js deleted file mode 100644 index fba02bb..0000000 --- a/tests/test018.js +++ /dev/null @@ -1,21 +0,0 @@ -// references with functions - -var a = 42; -// 42-tiny-js change begin ---> -//var b; // in JavaScript it's not allowed to add array elements to a var with type "undefined" -var b = []; -//<--- 42-tiny-js change end -b[0] = 43; - -function foo(myarray) { - myarray[0]++; -} - -function bar(myvalue) { - myvalue++; -} - -foo(b); -bar(a); - -result = a==42 && b[0]==44; diff --git a/tests/test019.js b/tests/test019.js deleted file mode 100644 index 366f1d4..0000000 --- a/tests/test019.js +++ /dev/null @@ -1,19 +0,0 @@ -// built-in functions - -foo = "foo bar stuff"; -r = Math.rand(); - -parsed = Integer.parseInt("42"); - -aStr = "ABCD"; -aChar = aStr.charAt(0); - -obj1 = new Object(); -obj1.food = "cake"; -obj1.desert = "pie"; - -obj2 = obj1.clone(); -obj2.food = "kittens"; - -result = foo.length==13 && foo.indexOf("bar")==4 && foo.substring(8,13)=="stuff" && parsed==42 && - Integer.valueOf(aChar)==65 && obj1.food=="cake" && obj2.desert=="pie"; diff --git a/tests/test020.js b/tests/test020.js deleted file mode 100644 index 076b5c8..0000000 --- a/tests/test020.js +++ /dev/null @@ -1,27 +0,0 @@ -// Test reported by sterowang, Variable attribute defines conflict with function. -/* -What steps will reproduce the problem? -1. function a (){}; -2. b = {}; -3. b.a = {}; -4. a(); - -What is the expected output? What do you see instead? -Function "a" should be called. But the error message "Error Expecting 'a' -to be a function at (line: 1, col: 1)" received. - -What version of the product are you using? On what operating system? -Version 1.6 is used on Cent OS 5.4 - - -Please provide any additional information below. -When using dump() to show symbols, found the function "a" is reassigned to -"{}" by "b.a = {};" call. -*/ - -function a (){}; -b = {}; -b.a = {}; -a(); - -result = 1; diff --git a/tests/test021.js b/tests/test021.js deleted file mode 100644 index a11cc73..0000000 --- a/tests/test021.js +++ /dev/null @@ -1,9 +0,0 @@ -/* Javascript eval */ - -// 42-tiny-js change begin ---> -// in JavaScript eval is not a JSON-parser -//myfoo = eval("{ foo: 42 }"); -myfoo = eval("tmp = { foo: 42 }"); -//<--- 42-tiny-js change end - -result = eval("4*10+2")==42 && myfoo.foo==42; diff --git a/tests/test022.js b/tests/test022.js deleted file mode 100644 index fc3f427..0000000 --- a/tests/test022.js +++ /dev/null @@ -1,9 +0,0 @@ -/* Javascript eval */ - -mystructure = { a:39, b:3, addStuff : function(c,d) { return c+d; } }; - -mystring = JSON.stringify(mystructure, undefined); - -mynewstructure = eval(mystring); - -result = mynewstructure.addStuff(mynewstructure.a, mynewstructure.b); diff --git a/tests/test023.js b/tests/test023.js deleted file mode 100644 index b0ecb14..0000000 --- a/tests/test023.js +++ /dev/null @@ -1,8 +0,0 @@ -// mikael.kindborg@mobilesorcery.com - Function symbol is evaluated in bracket-less body of false if-statement -// 42-tiny-js change begin ---> -var foo; // a var is only created automated by assignment -//<--- 42-tiny-js change end - -if (foo !== undefined) foo(); - -result = 1; diff --git a/tests/test024.js b/tests/test024.js deleted file mode 100644 index 66ecae6..0000000 --- a/tests/test024.js +++ /dev/null @@ -1,66 +0,0 @@ -/* Mandelbrot! */ - -X1 = -2.0; -Y1 = -2.0; -X2 = 2.0; -Y2 = 2.0; -PX = 32; -PY = 32; - - -lines = []; -for (y=0;y Date: Thu, 8 Dec 2011 16:26:03 +0000 Subject: [PATCH 32/43] 42tiny-js: 42tiny-js is partially more JavaScript compliant as the trunk-tiny-js e.g.: trunk-tiny-js creates a var always when its needed but 42tiny-js only if the var the left-hand-side of an assignment-operator. Test-scripts: Some test-scripts in the "tests"-folder are not JavaScript compliant. I have add new scripts according to the schematic: name.js -> name.42.js run_tests: run_tests is changed to prefer the ???.42.js files. Also run_tests is changed to run scripts in the "tests/42tests" folder after finished the tests from the "tests"-folder. --- run_tests.cpp | 54 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/run_tests.cpp b/run_tests.cpp index f29849a..6703a9e 100644 --- a/run_tests.cpp +++ b/run_tests.cpp @@ -26,6 +26,18 @@ /* * This is a program to run all the tests in the tests folder... */ +//#define _AFXDLL +#ifdef _DEBUG +# ifdef _MSC_VER +# ifdef USE_DEBUG_NEW +# include "targetver.h" +# include +# define new DEBUG_NEW +# endif +# else +# define DEBUG_MEMORY 1 +# endif +#endif #include "TinyJS.h" #include "TinyJS_Functions.h" @@ -250,31 +262,43 @@ int main(int argc, char **argv) return !run_test(argv[1]); } - int test_num = 1; int count = 0; int passed = 0; + const char *mask = "tests/test%03d.42.js"; + for(int js42 = 0; js42<2; js42++) { + int test_num = 1; + while (test_num<1000) { + char fn[32]; + sprintf(fn, mask, test_num); + // check if the file exists - if not, try 42TinyJS-spezial or assume we're at the end of our tests + FILE *f = fopen(fn,"r"); + if (!f) { + if(js42) break; + sprintf(fn, "tests/test%03d.js", test_num); + f = fopen(fn,"r"); + if(!f) break; + } + fclose(f); - while (test_num<1000) { - char fn[32]; - sprintf(fn, "tests/test%03d.js", test_num); - // check if the file exists - if not, assume we're at the end of our tests - FILE *f = fopen(fn,"r"); - if (!f) break; - fclose(f); - - if (run_test(fn)) - passed++; - count++; - test_num++; + if (run_test(fn)) + passed++; + count++; + test_num++; + } + mask = "tests/42tests/test%03d.js"; } - printf("Done. %d tests, %d pass, %d fail\n", count, passed, count-passed); #ifdef INSANE_MEMORY_DEBUG memtracing_kill(); #endif #ifdef _WIN32 #ifdef _DEBUG - _CrtDumpMemoryLeaks(); +// _CrtDumpMemoryLeaks(); +/* + no dump momoryleaks here + _CrtSetDbgFlag(..) force dump memoryleake after call of all global deconstructors +*/ + _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); #endif #endif return 0; From 2ce961c964316d2213cd3ff9df3fc81bda05881f Mon Sep 17 00:00:00 2001 From: pur3mail Date: Thu, 22 Dec 2011 14:39:24 +0000 Subject: [PATCH 33/43] (gw) Small tweaks to allow compilation with GCC --- TinyJS.cpp | 4 +++- TinyJS.h | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/TinyJS.cpp b/TinyJS.cpp index 9ba71fa..f22a311 100755 --- a/TinyJS.cpp +++ b/TinyJS.cpp @@ -98,6 +98,7 @@ Added some Visual Studio Preprocessor stuff Added now allowed stuff like this (function (v) {print(v);})(12); Remove unneeded and wrong deepCopy by assignment operator '=' + (gw) Small tweaks to allow compilation with GCC NOTE: This doesn't support constructors for objects @@ -1320,7 +1321,8 @@ CScriptVar *CScriptVar::deepCopy() { } void CScriptVar::trace(const string &name) { - trace(string(), name); + string tmp; + trace(tmp, name); } void CScriptVar::trace(string &indentStr, const string &name) { string indent = " "; diff --git a/TinyJS.h b/TinyJS.h index 4c6bc38..01b2f43 100755 --- a/TinyJS.h +++ b/TinyJS.h @@ -214,7 +214,7 @@ class CScriptLex std::string getSubString(int pos); ///< Return a sub-string from the given position up until right now CScriptLex *getSubLex(int lastPosition); ///< Return a sub-lexer from the given position up until right now - int CScriptLex::getDataPos(); + int getDataPos(); std::string getPosition(int pos=-1); ///< Return a string representing the position in lines and columns of the character pos given protected: From f82619dc31e06c0dba54d68960632ce836b8597a Mon Sep 17 00:00:00 2001 From: armin Date: Sat, 31 Dec 2011 14:45:24 +0000 Subject: [PATCH 34/43] 42tiny-js: big 2012 new-year-update many many changes. Too many to enumerate them here. New main-features: - injected a tokenizer between Lexer and Executer. this makes the two-pass-mode obsolete. the first pass is done by the tokenizer. - add 'with' and 'let' statements. - fixed and optimized the prototype- and scope-chain - added accessors like: { get test(){ return this.test_var; } , test_var : 5 } - and many more i wish you a happy new year 2012 ardi --- Script.cpp | 102 +- TinyJS.cpp | 5045 ++++++++++++++++++++++------------- TinyJS.h | 1302 +++++++-- TinyJS_Functions.cpp | 189 +- TinyJS_MathFunctions.cpp | 386 ++- TinyJS_StringFunctions.cpp | 236 ++ TinyJS_StringFunctions.h | 34 + lib-tiny-js.vcxproj | 14 +- lib-tiny-js.vcxproj.filters | 18 + pool_allocator.cpp | 134 + pool_allocator.h | 107 + run_tests.cpp | 81 +- run_tests.vcxproj | 4 +- time_logger.h | 97 + 14 files changed, 5276 insertions(+), 2473 deletions(-) create mode 100644 TinyJS_StringFunctions.cpp create mode 100644 TinyJS_StringFunctions.h create mode 100644 pool_allocator.cpp create mode 100644 pool_allocator.h create mode 100644 time_logger.h diff --git a/Script.cpp b/Script.cpp index 9712318..2c53234 100755 --- a/Script.cpp +++ b/Script.cpp @@ -27,29 +27,17 @@ * This is a simple program showing how to use TinyJS */ -#if defined(_MSC_VER) -# include "targetver.h" -# include -#endif #include "TinyJS.h" #include "TinyJS_Functions.h" +#include "TinyJS_StringFunctions.h" #include "TinyJS_MathFunctions.h" #include #include - -#if defined(_MSC_VER) && defined(_DEBUG) -# define new DEBUG_NEW -#endif - -#ifdef __GNUC__ -# define UNUSED(x) __attribute__((__unused__)) -#elif defined(_MSC_VER) -# ifndef UNUSED -# define UNUSED(x) x -# pragma warning( disable : 4100 ) /* unreferenced formal parameter */ + +#ifdef _DEBUG +# ifndef _MSC_VER +# define DEBUG_MEMORY 1 # endif -#else -# define UNUSED(x) x #endif //const char *code = "var a = 5; if (a==5) a=4; else a=3;"; @@ -57,49 +45,55 @@ //const char *code = "{ var b = 1; for (var i=0;i<4;i=i+1) b = b * 2; }"; const char *code = "function myfunc(x, y) { return x + y; } var a = myfunc(1,2); print(a);"; -void js_print(CScriptVar *v, void *UNUSED(userdata)) { - printf("> %s\n", v->getParameter("text")->getString().c_str()); +void js_print(const CFunctionsScopePtr &v, void *) { + printf("> %s\n", v->getParameter("text")->getString().c_str()); } -void js_dump(CScriptVar *UNUSED(v), void *userdata) { - CTinyJS *js = (CTinyJS*)userdata; - js->root->trace("> "); +void js_dump(const CFunctionsScopePtr &v, void *) { + v->getContext()->getRoot()->trace("> "); } -int main(int UNUSED(argc), char **UNUSED(argv)) +int main(int , char **) { - CTinyJS *js = new CTinyJS(); - /* add the functions from TinyJS_Functions.cpp */ - registerFunctions(js); - registerMathFunctions(js); - /* Add a native function */ - js->addNative("function print(text)", &js_print, 0); - js->addNative("function dump()", &js_dump, js); - /* Execute out bit of code - we could call 'evaluate' here if - we wanted something returned */ - try { - js->execute("var lets_quit = 0; function quit() { lets_quit = 1; }"); - js->execute("print(\"Interactive mode... Type quit(); to exit, or print(...); to print something, or dump() to dump the symbol table!\");"); - } catch (CScriptException *e) { - printf("ERROR: %s\n", e->text.c_str()); - delete e; - } - - while (js->evaluate("lets_quit") == "0") { - char buffer[2048]; - fgets ( buffer, sizeof(buffer), stdin ); - try { - js->execute(buffer); - } catch (CScriptException *e) { - printf("ERROR: %s\n", e->text.c_str()); + CTinyJS *js = new CTinyJS(); + /* add the functions from TinyJS_Functions.cpp */ + registerFunctions(js); + registerStringFunctions(js); + registerMathFunctions(js); + /* Add a native function */ + js->addNative("function print(text)", &js_print, 0); +// js->addNative("function dump()", &js_dump, js); + /* Execute out bit of code - we could call 'evaluate' here if + we wanted something returned */ + try { + js->execute("var lets_quit = 0; function quit() { lets_quit = 1; }"); + js->execute("print(\"Interactive mode... Type quit(); to exit, or print(...); to print something, or dump() to dump the symbol table!\");"); + } catch (CScriptException *e) { + printf("ERROR: %s\n", e->text.c_str()); delete e; - } - } - delete js; -#if defined(_WIN32) && defined(_DEBUG) && !defined(_MSC_VER) - // by Visual Studio we use the DEBUG_NEW stuff - _CrtDumpMemoryLeaks(); + } + + while (js->evaluate("lets_quit") == "0") { + char buffer[2048]; + fgets ( buffer, sizeof(buffer), stdin ); + try { + js->execute(buffer); + } catch (CScriptException *e) { + printf("ERROR: %s\n", e->text.c_str()); + delete e; + } + } + delete js; +#ifdef _WIN32 +#ifdef _DEBUG +// _CrtDumpMemoryLeaks(); +/* + no dump momoryleaks here + _CrtSetDbgFlag(..) force dump memoryleake after call of all global deconstructors +*/ + _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); +#endif #endif - return 0; + return 0; } diff --git a/TinyJS.cpp b/TinyJS.cpp index f22a311..7a1c155 100755 --- a/TinyJS.cpp +++ b/TinyJS.cpp @@ -77,7 +77,7 @@ Added '~' operator Added bit-shift operators '<<' '>>' Added assignment operators '<<=' '>>=' '|=' '&=' '^=' - Addet comma operator like this 'var i=1,j,k=9;' or 'for(i=0,j=12; i<10; i++, j++)' works + Added comma operator like this 'var i=1,j,k=9;' or 'for(i=0,j=12; i<10; i++, j++)' works Added Conditional operator ( ? : ) Added automatic cast from doubles to integer e.G. by logic and binary operators Added pre-increment/-decrement like '++i'/'--i' operator @@ -85,7 +85,7 @@ Fixed throws an Error when invalid using of post/pre-increment/decrement (like this 5++ works no more) Fixed memoryleak (unref arrayClass at deconstructor of JS CTinyJS) Fixed unary operator handling (like this '-~-5' works now) - Fixed operator prority order -> ',' + Fixed operator priority order -> ',' -> '=' '+=' '-=' '<<=' '>>=' '&=' '^=' '|=' -> '? :' -> '||' -> '&&' -> '|' -> '^' -> '&' -> ['==' '===' '!=' '!=='] @@ -98,7 +98,6 @@ Added some Visual Studio Preprocessor stuff Added now allowed stuff like this (function (v) {print(v);})(12); Remove unneeded and wrong deepCopy by assignment operator '=' - (gw) Small tweaks to allow compilation with GCC NOTE: This doesn't support constructors for objects @@ -107,68 +106,41 @@ There is no ternary operator implemented yet */ - -#ifdef _MSC_VER -# include "targetver.h" -# include +#ifdef _DEBUG +# ifndef _MSC_VER +# define DEBUG_MEMORY 1 +# endif #endif #include -#include #include #include #include -#include -#include "TinyJS.h" - -#ifdef _DEBUG -# ifdef _MSC_VER -# define new DEBUG_NEW -# else -# define DEBUG_MEMORY 1 -# endif -#endif +#include +#include +#include // for auto_ptr +#include +#include "TinyJS.h" #ifndef ASSERT # define ASSERT(X) assert(X) #endif -/* Frees the given link IF it isn't owned by anything else */ -//#define CLEAN(x) do{ CScriptVarLink *__v = x; if (__v && !__v->owned) { delete __v;} }while(0) -/* Create a LINK to point to VAR and free the old link. - * BUT this is more clever - it tries to keep the old link if it's not owned to save allocations */ -//#define CREATE_LINK(LINK, VAR) do{ if (!LINK || LINK->owned) LINK = new CScriptVarLink(VAR); else {LINK->replaceWith(VAR);LINK->name=TINYJS_TEMP_NAME;} }while(0) - -// corresponds "CScriptVarLink *link=0;" and "CScriptVarLink *link=Link" -inline CScriptVarSmartLink::CScriptVarSmartLink () : link(0){} -inline CScriptVarSmartLink::CScriptVarSmartLink (CScriptVarLink *Link) : link(Link) {} - -// this constructor replace "CScriptVarLink *link=0 ; CREATE_LINK(link, Var);" -inline CScriptVarSmartLink::CScriptVarSmartLink (CScriptVar *Var) : link(0) { - *this = Var; -} -// this operator replace "CREATE_LINK(link, Var);" -inline CScriptVarSmartLink &CScriptVarSmartLink::operator = (CScriptVar *Var) { - if (!link || link->owned) + +inline CScriptVarSmartLink &CScriptVarSmartLink::operator = (CScriptVarPtr Var) { + if(link && !link->IsOwned()) delete link; + link = new CScriptVarLink(Var); +/* + if (!link || link->IsOwned()) link = new CScriptVarLink(Var); else { link->replaceWith(Var); link->name=TINYJS_TEMP_NAME; } +*/ return *this; } - -// the deconstructor replace the macro CLEAN(x) -inline CScriptVarSmartLink::~CScriptVarSmartLink () { - if(link && !link->owned) - delete link; -} -// the copy-stuff has a special thing - -// when copying a SmartLink to an other then the right hand side will lost your link -inline CScriptVarSmartLink::CScriptVarSmartLink (const CScriptVarSmartLink &Link) : link(0) { - *this = Link; -} inline CScriptVarSmartLink &CScriptVarSmartLink::operator = (const CScriptVarSmartLink &Link) { - if(link && !link->owned) delete link; + if(link && !link->IsOwned()) delete link; link = Link.link; ((CScriptVarSmartLink &)Link).link = 0; // explicit cast to a non const ref return *this; @@ -176,18 +148,16 @@ inline CScriptVarSmartLink &CScriptVarSmartLink::operator = (const CScriptVarSma // this operator corresponds "CLEAN(link); link = Link;" inline CScriptVarSmartLink &CScriptVarSmartLink::operator = (CScriptVarLink *Link) { - if(link && !link->owned) delete link; + if(link && !link->IsOwned()) delete link; link = Link; return *this; } - -// this operator corresponds "link->replaceWith(Link->var)" +// this operator corresponds "link->replaceWith(Link->get())" inline CScriptVarSmartLink &CScriptVarSmartLink::operator <<(CScriptVarSmartLink &Link) { ASSERT(link && Link.link); - link->replaceWith(Link->var); + link->replaceWith(Link.link); return *this; } - using namespace std; #ifdef __GNUC__ @@ -240,8 +210,8 @@ void show_allocated() { allocatedVars[i]->trace(" "); } for (size_t i=0;iname.c_str(), allocatedLinks[i]->var->getRefs()); - allocatedLinks[i]->var->trace(" "); + printf("ALLOCATED LINK %s, allocated[%d] to \n", allocatedLinks[i]->name.c_str(), (*allocatedLinks[i])->getRefs()); + (*allocatedLinks[i])->trace(" "); } allocatedVars.clear(); allocatedLinks.clear(); @@ -262,9 +232,10 @@ bool isNumber(const string &str) { return true; } bool isHexadecimal(char ch) { - return ((ch>='0') && (ch<='9')) || - ((ch>='a') && (ch<='f')) || - ((ch>='A') && (ch<='F')); + return ((ch>='0') && (ch<='9')) || ((ch>='a') && (ch<='f')) || ((ch>='A') && (ch<='F')); +} +bool isOctal(char ch) { + return ((ch>='0') && (ch<='7')); } bool isAlpha(char ch) { return ((ch>='a') && (ch<='z')) || ((ch>='A') && (ch<='Z')) || ch=='_'; @@ -289,18 +260,19 @@ void replace(string &str, char textFrom, const char *textTo) { p = str.find(textFrom, p+sLen); } } - -string &int2string(int intData, const string &inString=string()); -string &int2string(int intData, const string &inString) { - string &_inString = (string &) inString; +string int2string(int intData) { char buffer[32]; sprintf_s(buffer, sizeof(buffer), "%d", intData); - return _inString=buffer; + return buffer; +} +string float2string(const double &floatData) { + char buffer[32]; + sprintf_s(buffer, sizeof(buffer), "%f", floatData); + return buffer; } - /// convert the given string into a quoted string suitable for javascript -std::string getJSString(const std::string &str) { - std::string nStr = str; +string getJSString(const string &str) { + string nStr = str; for (size_t i=0;i127) { + char buffer[5]; + sprintf_s(buffer, sizeof(buffer), "\\x%02X", nCh); + replaceWith = buffer; + } else replace=false; + } } if (replace) { @@ -321,7 +307,7 @@ std::string getJSString(const std::string &str) { } /** Is the string alphanumeric */ -bool isAlphaNum(const std::string &str) { +bool isAlphaNum(const string &str) { if (str.size()==0) return true; if (!isAlpha(str[0])) return false; for (size_t i=0;idata; - dataOwned = false; - dataStart = startChar; - dataEnd = endChar; - reset(); +CScriptLex::CScriptLex(const char *Code, const string &File, int Line, int Column) : data(Code) { + currentFile = File; + currentLineStart = tokenStart = data; + reset(data, Line, data); } -CScriptLex::~CScriptLex(void) -{ - if (dataOwned) - free((void*)data); -} -void CScriptLex::reset(int toPos) { - dataPos = dataStart+toPos; - tokenStart = 0; - tokenEnd = 0; - tokenLastEnd = 0; +void CScriptLex::reset(const char *toPos, int Line, const char *LineStart) { + dataPos = toPos; + tokenStart = data; tk = 0; tkStr = ""; - getNextCh(); - getNextCh(); + currentLine = Line; + currentLineStart = LineStart; + currCh = nextCh = 0; + getNextCh(); // currCh + getNextCh(); // nextCh getNextToken(); } -void CScriptLex::match(int expected_tk) { +void CScriptLex::check(int expected_tk) { if (expected_tk==';' && tk==LEX_EOF) return; // ignore last missing ';' if (tk!=expected_tk) { - ostringstream errorString; - errorString << "Got " << getTokenStr(tk) << " expected " << getTokenStr(expected_tk) - << " at " << getPosition(tokenStart) << " in '" << data << "'"; - throw new CScriptException(errorString.str()); + ostringstream errorString; + errorString << "Got " << getTokenStr(tk) << " expected " << getTokenStr(expected_tk); + throw new CScriptException(errorString.str(), currentFile, currentLine, currentColumn()); } +} +void CScriptLex::match(int expected_tk) { + check(expected_tk); getNextToken(); } string CScriptLex::getTokenStr(int token) { if (token>32 && token<128) { - char buf[4] = "' '"; - buf[1] = (char)token; + // char buf[4] = "' '"; + // buf[1] = (char)token; + char buf[2] = " "; + buf[0] = (char)token; return buf; } switch (token) { @@ -406,6 +382,8 @@ string CScriptLex::getTokenStr(int token) { case LEX_GEQUAL : return ">="; case LEX_RSHIFT : return ">>"; case LEX_RSHIFTEQUAL : return ">>="; + case LEX_RSHIFTU : return ">>>"; + case LEX_RSHIFTUEQUAL : return ">>>="; case LEX_PLUSEQUAL : return "+="; case LEX_MINUSEQUAL : return "-="; case LEX_PLUSPLUS : return "++"; @@ -425,12 +403,20 @@ string CScriptLex::getTokenStr(int token) { case LEX_R_DO : return "do"; case LEX_R_WHILE : return "while"; case LEX_R_FOR : return "for"; + case LEX_T_FOR_IN : return "for"; + case LEX_T_FOR_EACH_IN : return "for each"; case LEX_R_IN: return "in"; case LEX_R_BREAK : return "break"; case LEX_R_CONTINUE : return "continue"; case LEX_R_FUNCTION : return "function"; + case LEX_T_FUNCTION_OPERATOR : return "function"; + case LEX_T_GET : return "get"; + case LEX_T_SET : return "set"; + case LEX_R_RETURN : return "return"; case LEX_R_VAR : return "var"; + case LEX_R_LET : return "let"; + case LEX_R_WITH : return "with"; case LEX_R_TRUE : return "true"; case LEX_R_FALSE : return "false"; case LEX_R_NULL : return "null"; @@ -449,6 +435,9 @@ string CScriptLex::getTokenStr(int token) { case LEX_R_SWITCH : return "switch"; case LEX_R_CASE : return "case"; case LEX_R_DEFAULT : return "default"; + + case LEX_T_SKIP : return "LEX_SKIP"; + case LEX_T_LABEL: return "LABEL"; } ostringstream msg; @@ -457,12 +446,18 @@ string CScriptLex::getTokenStr(int token) { } void CScriptLex::getNextCh() { + if(currCh == '\n') { // Windows or Linux + currentLine++; + tokenStart = currentLineStart = dataPos - (nextCh == LEX_EOF ? 0 : 1); + } currCh = nextCh; - if (dataPos < dataEnd) - nextCh = data[dataPos]; - else - nextCh = 0; - dataPos++; + if ( (nextCh = *dataPos) != LEX_EOF ) dataPos++; // stay on EOF + if(currCh == '\r') { // Windows or Mac + if(nextCh == '\n') + getNextCh(); // Windows '\r\n\' --> skip '\r' + else + currCh = '\n'; // Mac (only '\r') --> convert '\r' to '\n' + } } void CScriptLex::getNextToken() { @@ -485,7 +480,7 @@ void CScriptLex::getNextToken() { return; } // record beginning of this token - tokenStart = dataPos-2; + tokenStart = dataPos - (nextCh == LEX_EOF ? (currCh == LEX_EOF ? 0 : 1) : 2); // tokens if (isAlpha(currCh)) { // IDs while (isAlpha(currCh) || isNumeric(currCh)) { @@ -493,40 +488,42 @@ void CScriptLex::getNextToken() { getNextCh(); } tk = LEX_ID; - if (tkStr=="if") tk = LEX_R_IF; - else if (tkStr=="else") tk = LEX_R_ELSE; - else if (tkStr=="do") tk = LEX_R_DO; - else if (tkStr=="while") tk = LEX_R_WHILE; - else if (tkStr=="for") tk = LEX_R_FOR; - else if (tkStr=="in") tk = LEX_R_IN; - else if (tkStr=="break") tk = LEX_R_BREAK; - else if (tkStr=="continue") tk = LEX_R_CONTINUE; - else if (tkStr=="function") tk = LEX_R_FUNCTION; - else if (tkStr=="return") tk = LEX_R_RETURN; - else if (tkStr=="var") tk = LEX_R_VAR; - else if (tkStr=="true") tk = LEX_R_TRUE; - else if (tkStr=="false") tk = LEX_R_FALSE; - else if (tkStr=="null") tk = LEX_R_NULL; - else if (tkStr=="undefined") tk = LEX_R_UNDEFINED; - else if (tkStr=="Infinity") tk = LEX_R_INFINITY; - else if (tkStr=="NaN") tk = LEX_R_NAN; - else if (tkStr=="new") tk = LEX_R_NEW; - else if (tkStr=="try") tk = LEX_R_TRY; - else if (tkStr=="catch") tk = LEX_R_CATCH; - else if (tkStr=="finally") tk = LEX_R_FINALLY; - else if (tkStr=="throw") tk = LEX_R_THROW; - else if (tkStr=="typeof") tk = LEX_R_TYPEOF; - else if (tkStr=="void") tk = LEX_R_VOID; - else if (tkStr=="delete") tk = LEX_R_DELETE; - else if (tkStr=="instanceof") tk = LEX_R_INSTANCEOF; - else if (tkStr=="switch") tk = LEX_R_SWITCH; - else if (tkStr=="case") tk = LEX_R_CASE; - else if (tkStr=="default") tk = LEX_R_DEFAULT; + if (tkStr=="if") tk = LEX_R_IF; + else if (tkStr=="else") tk = LEX_R_ELSE; + else if (tkStr=="do") tk = LEX_R_DO; + else if (tkStr=="while") tk = LEX_R_WHILE; + else if (tkStr=="for") tk = LEX_R_FOR; + else if (tkStr=="in") tk = LEX_R_IN; + else if (tkStr=="break") tk = LEX_R_BREAK; + else if (tkStr=="continue") tk = LEX_R_CONTINUE; + else if (tkStr=="function") tk = LEX_R_FUNCTION; + else if (tkStr=="return") tk = LEX_R_RETURN; + else if (tkStr=="var") tk = LEX_R_VAR; + else if (tkStr=="let") tk = LEX_R_LET; + else if (tkStr=="with") tk = LEX_R_WITH; + else if (tkStr=="true") tk = LEX_R_TRUE; + else if (tkStr=="false") tk = LEX_R_FALSE; + else if (tkStr=="null") tk = LEX_R_NULL; + else if (tkStr=="undefined") tk = LEX_R_UNDEFINED; + else if (tkStr=="Infinity") tk = LEX_R_INFINITY; + else if (tkStr=="NaN") tk = LEX_R_NAN; + else if (tkStr=="new") tk = LEX_R_NEW; + else if (tkStr=="try") tk = LEX_R_TRY; + else if (tkStr=="catch") tk = LEX_R_CATCH; + else if (tkStr=="finally") tk = LEX_R_FINALLY; + else if (tkStr=="throw") tk = LEX_R_THROW; + else if (tkStr=="typeof") tk = LEX_R_TYPEOF; + else if (tkStr=="void") tk = LEX_R_VOID; + else if (tkStr=="delete") tk = LEX_R_DELETE; + else if (tkStr=="instanceof") tk = LEX_R_INSTANCEOF; + else if (tkStr=="switch") tk = LEX_R_SWITCH; + else if (tkStr=="case") tk = LEX_R_CASE; + else if (tkStr=="default") tk = LEX_R_DEFAULT; } else if (isNumeric(currCh) || (currCh=='.' && isNumeric(nextCh))) { // Numbers if(currCh=='.') tkStr+='0'; bool isHex = false; if (currCh=='0') { tkStr += currCh; getNextCh(); } - if (currCh=='x') { + if (currCh=='x' || currCh=='X') { isHex = true; tkStr += currCh; getNextCh(); } @@ -545,7 +542,7 @@ void CScriptLex::getNextToken() { } } // do fancy e-style floating point - if (!isHex && currCh=='e') { + if (!isHex && (currCh=='e' || currCh=='E')) { tk = LEX_FLOAT; tkStr += currCh; getNextCh(); if (currCh=='-') { tkStr += currCh; getNextCh(); } @@ -553,42 +550,49 @@ void CScriptLex::getNextToken() { tkStr += currCh; getNextCh(); } } - } else if (currCh=='"') { - // strings... - getNextCh(); - while (currCh && currCh!='"') { - if (currCh == '\\') { - getNextCh(); - switch (currCh) { - case 'n' : tkStr += '\n'; break; - case '"' : tkStr += '"'; break; - case '\\' : tkStr += '\\'; break; - default: tkStr += currCh; - } - } else { - tkStr += currCh; - } - getNextCh(); - } + } else if (currCh=='"' || currCh=='\'') { // strings... + char endCh = currCh; getNextCh(); - tk = LEX_STR; - } else if (currCh=='\'') { - // strings again... - getNextCh(); - while (currCh && currCh!='\'') { + while (currCh && currCh!=endCh && currCh!='\n') { if (currCh == '\\') { getNextCh(); switch (currCh) { - case 'n' : tkStr += '\n'; break; - case '\'' : tkStr += '\''; break; - case '\\' : tkStr += '\\'; break; - default: tkStr += currCh; + case '\n' : break; // ignore newline after '\' + case 'n': tkStr += '\n'; break; + case 'r': tkStr += '\r'; break; + case 'a': tkStr += '\a'; break; + case 'b': tkStr += '\b'; break; + case 'f': tkStr += '\f'; break; + case 't': tkStr += '\t'; break; + case 'v': tkStr += '\v'; break; + case 'x': { // hex digits + getNextCh(); + char buf[3]="\0\0"; + for(int i=0; i<2 && isHexadecimal(currCh); i++) + { + buf[i] = currCh; getNextCh(); + } + tkStr += (char)strtol(buf, 0, 16); + } + default: { + if(isOctal(currCh)) { + char buf[4]="\0\0\0"; + for(int i=0; i<3 && isOctal(currCh); i++) + { + buf[i] = currCh; getNextCh(); + } + tkStr += (char)strtol(buf, 0, 8); + } + else tkStr += currCh; + } } } else { tkStr += currCh; } getNextCh(); } + if(currCh != endCh) + throw new CScriptException("unterminated string literal", currentFile, currentLine, currentColumn()); getNextCh(); tk = LEX_STR; } else { @@ -631,6 +635,13 @@ void CScriptLex::getNextToken() { if (currCh=='=') { // >>= tk = LEX_RSHIFTEQUAL; getNextCh(); + } else if (currCh=='>') { // >>> + tk = LEX_RSHIFTU; + getNextCh(); + if (currCh=='=') { // >>>= + tk = LEX_RSHIFTUEQUAL; + getNextCh(); + } } } } else if (tk=='+') { @@ -680,649 +691,1290 @@ void CScriptLex::getNextToken() { } } /* This isn't quite right yet */ - tokenLastEnd = tokenEnd; - tokenEnd = dataPos-3; -} - -string CScriptLex::getSubString(int lastPosition) { - int lastCharIdx = tokenLastEnd+1; - if (lastCharIdx < dataEnd) { - /* save a memory alloc by using our data array to create the substring */ - char old = data[lastCharIdx]; - data[lastCharIdx] = 0; - std::string value = &data[lastPosition]; - data[lastCharIdx] = old; - return value; - } else { - return std::string(&data[lastPosition]); - } } - -CScriptLex *CScriptLex::getSubLex(int lastPosition) { - int lastCharIdx = tokenLastEnd+1; - if (lastCharIdx < dataEnd) - return new CScriptLex( this, lastPosition, lastCharIdx); +// ----------------------------------------------------------------------------------- CSCRIPTTOKEN +CScriptToken::CScriptToken(CScriptLex *l, int Match) : line(l->currentLine), column(l->currentColumn()), token(l->tk), intData(0) +{ + if(token == LEX_INT) + intData = strtol(l->tkStr.c_str(),0,0); + else if(LEX_TOKEN_DATA_FLOAT(token)) + floatData = new double(strtod(l->tkStr.c_str(),0)); + else if(LEX_TOKEN_DATA_STRING(token)) { + stringData = new CScriptTokenDataString(l->tkStr); + stringData->ref(); + } else if(LEX_TOKEN_DATA_FUNCTION(token)) { + fncData = new CScriptTokenDataFnc; + fncData->ref(); + } + if(Match>=0) + l->match(Match); else - return new CScriptLex( this, lastPosition, dataEnd ); + l->match(l->tk); } -int CScriptLex::getDataPos() { - return dataPos; -} - -string CScriptLex::getPosition(int pos) { - if (pos<0) pos=tokenLastEnd; - int line = 1,col = 1; - for (int i=0;iref(); + } else + ASSERT(0); } -// ----------------------------------------------------------------------------------- CSCRIPTVARLINK -CScriptVarLink::CScriptVarLink(CScriptVar *var, const std::string &name) { -#if DEBUG_MEMORY - mark_allocated(this); -#endif - this->name = name; - this->var = var->ref(); - this->owned = false; - this->owner = 0; - this->dontDelete = false; +CScriptToken::CScriptToken(uint16_t Tk, const string &TkStr) : line(0), column(0), token(Tk) { + ASSERT(LEX_TOKEN_DATA_STRING(token)); + stringData = new CScriptTokenDataString(TkStr); + stringData->ref(); } -CScriptVarLink::CScriptVarLink(const CScriptVarLink &link) { - // Copy constructor -#if DEBUG_MEMORY - mark_allocated(this); -#endif - this->name = link.name; - this->var = link.var->ref(); - this->owned = false; - this->owner = 0; - this->dontDelete = false; +CScriptToken &CScriptToken::operator =(const CScriptToken &Copy) +{ + clear(); + line = Copy.line; + column = Copy.column; + token = Copy.token; + if(LEX_TOKEN_DATA_FLOAT(token)) + floatData = new double(*Copy.floatData); + else if(LEX_TOKEN_DATA_STRING(token)) { + stringData = Copy.stringData; + stringData->ref(); + } else if(LEX_TOKEN_DATA_FUNCTION(token)) { + fncData = Copy.fncData; + fncData->ref(); + } else + intData = Copy.intData; + return *this; } - -CScriptVarLink::~CScriptVarLink() { -#if DEBUG_MEMORY - mark_deallocated(this); -#endif - var->unref(this->owner); +string CScriptToken::getParsableString(string &indentString, int &newln, const string &indent) { + string OutString; + string nl = indent.size() ? "\n" : " "; + int last_newln = newln; + if(newln&2) OutString.append(nl); + if(newln&1) OutString.append(indentString); + newln = 0; + + if(LEX_TOKEN_DATA_STRING(token)) + OutString.append(String()).append(" "); + else if(LEX_TOKEN_DATA_FLOAT(token)) + OutString.append(float2string(Float())).append(" "); + else if(token == LEX_INT) + OutString.append(int2string(Int())).append(" "); + else if(LEX_TOKEN_DATA_FUNCTION(token)) { + OutString.append("function "); + if(token == LEX_R_FUNCTION) + OutString.append(Fnc().name); + OutString.append("("); + if(Fnc().parameter.size()) { + OutString.append(Fnc().parameter.front()); + for(vector::iterator it=Fnc().parameter.begin()+1; it!=Fnc().parameter.end(); ++it) + OutString.append(", ").append(*it); + } + OutString.append(") "); + for(TOKEN_VECT::iterator it=Fnc().body.begin(); it != Fnc().body.end(); ++it) { + OutString.append(it->getParsableString(indentString, newln, indent)); + } + } else if(token == '{') { + OutString.append("{"); + indentString.append(indent); + newln = 1|2|4; + } else if(token == '}') { + if(last_newln==(1|2|4)) OutString = ""; + indentString.resize(indentString.size() - min(indentString.size(),indent.size())); + OutString.append("}"); + newln = 1|2; + } else { + OutString.append(CScriptLex::getTokenStr(token)); + if(token==';') newln=1|2; else OutString.append(" "); + } + return OutString; } - -void CScriptVarLink::replaceWith(CScriptVar *newVar) { - if(!newVar) newVar = new CScriptVar(); - CScriptVar *oldVar = var; - oldVar->unref(owner); - var = newVar->ref(); - if(owner) var->recoursionCheck(owner); +string CScriptToken::getParsableString(const string indent) { + string indentString; + int newln = 0; + return getParsableString(indentString, newln, indent); } - -void CScriptVarLink::replaceWith(CScriptVarLink *newVar) { - if (newVar) - replaceWith(newVar->var); - else - replaceWith(new CScriptVar()); +void CScriptToken::print(string &indent ) +{ + if(LEX_TOKEN_DATA_STRING(token)) + printf("%s%s ", indent.c_str(), String().c_str()); + else if(LEX_TOKEN_DATA_FLOAT(token)) + printf("%s%f ", indent.c_str(), Float()); + else if(token == LEX_INT) + printf("%s%d ", indent.c_str(), Int()); + else if(LEX_TOKEN_DATA_FUNCTION(token)) { + if(token == LEX_R_FUNCTION) + printf("%sfunction %s(", indent.c_str(), Fnc().name.c_str()); + else + printf("%sfunction (", indent.c_str()); + if(Fnc().parameter.size()) { + printf("%s", Fnc().parameter.front().c_str()); + for(vector::iterator it=Fnc().parameter.begin()+1; it!=Fnc().parameter.end(); ++it) + printf(",%s", it->c_str()); + } + printf(")"); + for(TOKEN_VECT::iterator it=Fnc().body.begin(); it != Fnc().body.end(); ++it) + it->print (indent); + } else if(token == '{') { + printf("%s{\n", indent.c_str()); + indent += " "; + } else if(token == '}') { + indent.resize(indent.size()-3); + printf("}\n"); + } else { + printf("%s%s ", indent.c_str(), CScriptLex::getTokenStr(token).c_str()); + if(token==';') printf("\n"); + } } - -// ----------------------------------------------------------------------------------- CSCRIPTVAR - -CScriptVar::CScriptVar() { - refs = 0; - internalRefs = 0; - recursionFlag = 0; - recursionSet = 0; - __proto__ = NULL; -#if DEBUG_MEMORY - mark_allocated(this); -#endif - init(); - flags = SCRIPTVAR_UNDEFINED; +void CScriptToken::clear() +{ + if(LEX_TOKEN_DATA_STRING(token)) + stringData->unref(); + else if(LEX_TOKEN_DATA_FLOAT(token)) + delete floatData; + else if(LEX_TOKEN_DATA_FUNCTION(token)) + fncData->unref(); + token = 0; } -CScriptVar::CScriptVar(const string &str) { - refs = 0; - internalRefs = 0; - recursionFlag = 0; - recursionSet = 0; - __proto__ = NULL; -#if DEBUG_MEMORY - mark_allocated(this); -#endif - init(); - flags = SCRIPTVAR_STRING; - data = str; +// ----------------------------------------------------------------------------------- CSCRIPTTOKENIZER +CScriptTokenizer::CScriptTokenizer() : l(0), prevPos(&tokens) { } - -CScriptVar::CScriptVar(const char *str) { - refs = 0; - internalRefs = 0; - recursionFlag = 0; - recursionSet = 0; - __proto__ = NULL; -#if DEBUG_MEMORY - mark_allocated(this); -#endif - init(); - flags = SCRIPTVAR_STRING; - data = str; +CScriptTokenizer::CScriptTokenizer(CScriptLex &Lexer) : l(0), prevPos(&tokens) { + tokenizeCode(Lexer); } - -CScriptVar::CScriptVar(const string &varData, int varFlags) { - refs = 0; - internalRefs = 0; - recursionFlag = 0; - recursionSet = 0; - __proto__ = NULL; -#if DEBUG_MEMORY - mark_allocated(this); -#endif - init(); - flags = varFlags; - if (varFlags & SCRIPTVAR_INTEGER) { - intData = strtol(varData.c_str(),0,0); - } else if (varFlags & SCRIPTVAR_DOUBLE) { - doubleData = strtod(varData.c_str(),0); - } else - data = varData; +CScriptTokenizer::CScriptTokenizer(const char *Code, const string &File, int Line, int Column) : l(0), prevPos(&tokens) { + CScriptLex lexer(Code, File, Line, Column); + tokenizeCode(lexer); } - -CScriptVar::CScriptVar(double val) { - refs = 0; - internalRefs = 0; - recursionFlag = 0; - recursionSet = 0; - __proto__ = NULL; -#if DEBUG_MEMORY - mark_allocated(this); -#endif - init(); - setDouble(val); +void CScriptTokenizer::tokenizeCode(CScriptLex &Lexer) { + try { + l=&Lexer; + tokens.clear(); + tokenScopeStack.clear(); + vector blockStart(1, tokens.size()), marks; + bool statement = true; + if(l->tk == '�') { // special-Token at Start means the code begins not at Statement-Level + l->match('�'); + statement = false; + } + do { + tokenizeToken(tokens, statement, blockStart, marks); + } while (l->tk!=LEX_EOF); + pushToken(tokens); // add LEX_EOF-Token + TOKEN_VECT(tokens).swap(tokens);// tokens.shrink_to_fit(); + pushTokenScope(tokens); + currentFile = l->currentFile; + tk = getToken().token; + } catch (CScriptException *e) { + ostringstream msg; + msg << "Error " << e->text; + if(e->line >= 0) msg << " at Line:" << e->line+1 << " Column:" << e->column+1; + if(e->file.length()) msg << " in " << e->file; + delete e; + l=0; + throw new CScriptException(msg.str(),""); + } } -CScriptVar::CScriptVar(int val) { - refs = 0; - internalRefs = 0; - recursionFlag = 0; - recursionSet = 0; - __proto__ = NULL; -#if DEBUG_MEMORY - mark_allocated(this); -#endif - init(); - setInt(val); +void CScriptTokenizer::getNextToken() { + prevPos = tokenScopeStack.back(); + if(getToken().token == LEX_EOF) + return; + ScriptTokenPosition &_TokenPos = tokenScopeStack.back(); + _TokenPos.pos++; + if(_TokenPos.pos == _TokenPos.tokens->end()) + tokenScopeStack.pop_back(); +// ScriptTokenPosition &TokenPos = tokenScopeStack.back(); + tk = getToken().token; } -CScriptVar::CScriptVar(bool val) { - refs = 0; - internalRefs = 0; - recursionFlag = 0; - recursionSet = 0; - __proto__ = NULL; -#if DEBUG_MEMORY - mark_allocated(this); -#endif - init(); - setBool(val); -} -CScriptVar::~CScriptVar(void) { -#if DEBUG_MEMORY - mark_deallocated(this); -#endif - removeAllChildren(); - if(flags & SCRIPTVAR_NATIVE_MFNC) - delete jsCallbackClass; -} -void CScriptVar::init() { - flags = 0; - jsCallback = 0; - jsCallbackUserData = 0; - data = TINYJS_BLANK_DATA; - intData = 0; - doubleData = 0; +void CScriptTokenizer::match(int ExpectedToken) { + if(check(ExpectedToken)) + getNextToken(); } - -CScriptVar *CScriptVar::getReturnVar() { - return getParameter(TINYJS_RETURN_VAR); +bool CScriptTokenizer::check(int ExpectedToken) { + int currentToken = getToken().token; + if (ExpectedToken==';' && (currentToken==LEX_EOF || currentToken=='}')) return false; // ignore last missing ';' + if (currentToken!=ExpectedToken) { + ostringstream errorString; + errorString << "Got " << CScriptLex::getTokenStr(currentToken) << " expected " << CScriptLex::getTokenStr(ExpectedToken); + throw new CScriptException(errorString.str(), currentFile, currentLine(), currentColumn()); + } + return true; } - -void CScriptVar::setReturnVar(CScriptVar *var) { - findChildOrCreate(TINYJS_RETURN_VAR)->replaceWith(var); +void CScriptTokenizer::pushTokenScope(TOKEN_VECT &Tokens) { + tokenScopeStack.push_back(ScriptTokenPosition(&Tokens)); + tk = getToken().token; } - -CScriptVar *CScriptVar::getParameter(const std::string &name) { - return findChildOrCreate(name)->var; +void CScriptTokenizer::setPos(ScriptTokenPosition &TokenPos) { + ASSERT( TokenPos.tokens == tokenScopeStack.back().tokens); + tokenScopeStack.back().pos = TokenPos.pos; + tk = getToken().token; } - -CScriptVar *CScriptVar::getParameter(int Idx) { - CScriptVar *arguments = findChildOrCreate(TINYJS_ARGUMENTS_VAR)->var; - return arguments->findChildOrCreate(int2string(Idx))->var; +void CScriptTokenizer::skip(int Tokens) { + tokenScopeStack.back().pos+=Tokens; + tk = getToken().token; } -int CScriptVar::getParameterLength() { - CScriptVar *arguments = findChildOrCreate(TINYJS_ARGUMENTS_VAR)->var; - return arguments->findChildOrCreate("length")->var->getInt(); +void CScriptTokenizer::tokenizeTry(TOKEN_VECT &Tokens, bool &Statement, vector &BlockStart, vector &Marks) { + if(!Statement) throwTokenNotExpected(); + int tk = l->tk; + Marks.push_back(pushToken(Tokens)); // push Token & push tokenBeginIdx + Statement = true; + l->check('{'); +// BlockStart.push_back(Tokens.size()); // set a blockStart + tokenizeBlock(Tokens, Statement, BlockStart, Marks); +// BlockStart.pop_back(); + + int tokenBeginIdx = Marks.back(); + Marks.pop_back(); // clean-up Marks + Tokens[tokenBeginIdx].Int() = Tokens.size()-tokenBeginIdx; + + if(l->tk == LEX_R_CATCH && tk == LEX_R_TRY) + tokenizeTry(Tokens, Statement, BlockStart, Marks); + else if(l->tk == LEX_R_FINALLY && (tk == LEX_R_TRY || tk == LEX_R_CATCH)) + tokenizeTry(Tokens, Statement, BlockStart, Marks); } +static inline void setTokenSkip(TOKEN_VECT &Tokens, vector &Marks) { + int tokenBeginIdx = Marks.back(); + Marks.pop_back(); + Tokens[tokenBeginIdx].Int() = Tokens.size()-tokenBeginIdx; +} +void CScriptTokenizer::tokenizeSwitch(TOKEN_VECT &Tokens, bool &Statement, vector &BlockStart, vector &Marks) { + if(!Statement) throwTokenNotExpected(); + + Marks.push_back(pushToken(Tokens)); // push Token & push tokenBeginIdx + + Statement = false; + l->check('('); + tokenizeBlock(Tokens, Statement, BlockStart, Marks); + + Statement = true; + Marks.push_back(pushToken(Tokens, '{')); // push Token & push blockBeginIdx + BlockStart.push_back(Tokens.size()); // push Block-Start (one Token after '{') + + for(bool hasDefault=false;;) { + if( l->tk == LEX_R_CASE || l->tk == LEX_R_DEFAULT) { + if(l->tk == LEX_R_CASE) { + Marks.push_back(pushToken(Tokens)); // push Token & push caseBeginIdx + Statement = false; + while(l->tk != ':' && l->tk != LEX_EOF ) + tokenizeToken(Tokens, Statement, BlockStart, Marks); + setTokenSkip(Tokens, Marks); + } else { // default + if(hasDefault) throw new CScriptException("more than one switch default", l->currentFile, l->currentLine, l->currentColumn()); + hasDefault = true; + pushToken(Tokens); + } + + Marks.push_back(pushToken(Tokens, ':')); + Statement = true; + while(l->tk != '}' && l->tk != LEX_R_CASE && l->tk != LEX_R_DEFAULT && l->tk != LEX_EOF ) + tokenizeStatement(Tokens, Statement, BlockStart, Marks); + setTokenSkip(Tokens, Marks); + } else if(l->tk == '}') { + break; + } else + throw new CScriptException("invalid switch statement", l->currentFile, l->currentLine, l->currentColumn()); + } + pushToken(Tokens, '}'); + BlockStart.pop_back(); + Statement = true; + int tokenBeginIdx = Marks.back(); // get block begin + Marks.pop_back(); // clean-up Marks + Tokens[tokenBeginIdx].Int() = Tokens.size()-tokenBeginIdx; -CScriptVarLink *CScriptVar::findChild(const string &childName) { - SCRIPTVAR_CHILDS::iterator it = Childs.find(childName); - if(it != Childs.end()) - return it->second; - return NULL; + tokenBeginIdx = Marks.back(); // get switch gegin + Marks.pop_back(); // clean-up Marks + Tokens[tokenBeginIdx].Int() = Tokens.size()-tokenBeginIdx; } +void CScriptTokenizer::tokenizeWith(TOKEN_VECT &Tokens, bool &Statement, vector &BlockStart, vector &Marks) { + if(!Statement) throwTokenNotExpected(); -CScriptVarLink *CScriptVar::findChildOrCreate(const string &childName, int varFlags) { - CScriptVarLink *l = findChild(childName); - if (l) return l; - return addChild(childName, new CScriptVar(TINYJS_BLANK_DATA, varFlags)); -} + Marks.push_back(pushToken(Tokens)); // push Token & push tokenBeginIdx -CScriptVarLink *CScriptVar::findChildOrCreateByPath(const std::string &path) { - size_t p = path.find('.'); - if (p == string::npos) - return findChildOrCreate(path); + Statement = false; + l->check('('); + tokenizeBlock(Tokens, Statement, BlockStart, Marks); + Statement = true; - return findChildOrCreate(path.substr(0,p), SCRIPTVAR_OBJECT)->var-> - findChildOrCreateByPath(path.substr(p+1)); -} + BlockStart.push_back(Tokens.size()); // set a blockStart + tokenizeStatement(Tokens, Statement, BlockStart, Marks); + BlockStart.pop_back(); -CScriptVarLink *CScriptVar::addChild(const std::string &childName, CScriptVar *child) { - if (isUndefined()) { - flags = SCRIPTVAR_OBJECT; - } - // if no child supplied, create one - if (!child) - child = new CScriptVar(); -#ifdef _DEBUG - if(findChild(childName)) - ASSERT(0); // addCild - the child exists -#endif - CScriptVarLink *link = new CScriptVarLink(child, childName); - link->owned = true; - link->owner = this; - Childs[childName] = link; - link->var->recoursionCheck(this); - return link; + int tokenBeginIdx = Marks.back(); + Marks.pop_back(); // clean-up Marks + Tokens[tokenBeginIdx].Int() = Tokens.size()-tokenBeginIdx; } -CScriptVarLink *CScriptVar::addChildNoDup(const std::string &childName, CScriptVar *child) { - // if no child supplied, create one - if (!child) - child = new CScriptVar(); +void CScriptTokenizer::tokenizeWhile(TOKEN_VECT &Tokens, bool &Statement, vector &BlockStart, vector &Marks) { + if(!Statement) throwTokenNotExpected(); - CScriptVarLink *link = findChild(childName); - if (link) { - link->replaceWith(child); - } else { - link = new CScriptVarLink(child, childName); - link->owned = true; - link->owner = this; - Childs[childName] = link; - link->var->recoursionCheck(this); - } + Marks.push_back(pushToken(Tokens)); // push Token & push tokenBeginIdx + Statement = false; + l->check('('); + tokenizeBlock(Tokens, Statement, BlockStart, Marks); - return link; -} + Marks.push_back(Tokens.size()); // push skiperBeginIdx + Tokens.push_back(CScriptToken(LEX_T_SKIP)); // skip -bool CScriptVar::removeLink(CScriptVarLink *&link) { - if (!link) return false; -#ifdef _DEBUG - if(findChild(link->name) != link) - ASSERT(0); // removeLink - the link is not atached to this var -#endif - Childs.erase(link->name); - delete link; - link = 0; - return true; + Statement = true; + + BlockStart.push_back(Tokens.size()); // set a blockStart + tokenizeStatement(Tokens, Statement, BlockStart, Marks); + BlockStart.pop_back(); + + int tokenBeginIdx = Marks.back(); // set skipper + Marks.pop_back(); // clean-up Marks + Tokens[tokenBeginIdx].Int() = Tokens.size()-tokenBeginIdx; + + tokenBeginIdx = Marks.back(); + Marks.pop_back(); // clean-up Marks + Tokens[tokenBeginIdx].Int() = Tokens.size()-tokenBeginIdx; } -void CScriptVar::removeAllChildren() { - SCRIPTVAR_CHILDS::iterator it; - for(it = Childs.begin(); it!= Childs.end(); ++it) { - delete it->second; - } - Childs.clear(); +void CScriptTokenizer::tokenizeDo(TOKEN_VECT &Tokens, bool &Statement, vector &BlockStart, vector &Marks) { + if(!Statement) throwTokenNotExpected(); + Marks.push_back(pushToken(Tokens)); // push Token & push tokenBeginIdx + + BlockStart.push_back(Tokens.size()); // set a blockStart + tokenizeStatement(Tokens, Statement, BlockStart, Marks); + BlockStart.pop_back(); + pushToken(Tokens, LEX_R_WHILE); + Statement = false; + l->check('('); + tokenizeBlock(Tokens, Statement, BlockStart, Marks); + pushToken(Tokens, ';'); + Statement = true; + + int tokenBeginIdx = Marks.back(); + Marks.pop_back(); // clean-up Marks + Tokens[tokenBeginIdx].Int() = Tokens.size()-tokenBeginIdx; } -CScriptVar *CScriptVar::getArrayIndex(int idx) { - CScriptVarLink *link = findChild(int2string(idx)); - if (link) return link->var; - else return new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_NULL); // undefined -} +void CScriptTokenizer::tokenizeIf(TOKEN_VECT &Tokens, bool &Statement, vector &BlockStart, vector &Marks) { + if(!Statement) throwTokenNotExpected(); -void CScriptVar::setArrayIndex(int idx, CScriptVar *value) { - string sIdx; - CScriptVarLink *link = findChild(int2string(idx, sIdx)); + Marks.push_back(pushToken(Tokens)); // push Token & push tokenBeginIdx - if (link) { - if (value->isUndefined()) - removeLink(link); - else - link->replaceWith(value); - } else { - if (!value->isUndefined()) - addChild(sIdx, value); - } -} + Statement = false; + l->check('('); + tokenizeBlock(Tokens, Statement, BlockStart, Marks); -int CScriptVar::getArrayLength() { - int highest = -1; - if (!isArray()) return 0; + Marks.push_back(Tokens.size()); // push skiperBeginIdx + Tokens.push_back(CScriptToken(LEX_T_SKIP)); // skip - for(SCRIPTVAR_CHILDS::iterator it = Childs.begin(); it != Childs.end(); ++it) { - if (isNumber(it->first)) { - int val = atoi(it->first.c_str()); - if (val > highest) highest = val; - } + Statement = true; + BlockStart.push_back(Tokens.size()); // set a blockStart + tokenizeStatement(Tokens, Statement, BlockStart, Marks); + BlockStart.pop_back(); + + int tokenBeginIdx = Marks.back(); // set skipper + Marks.pop_back(); // clean-up Marks + Tokens[tokenBeginIdx].Int() = Tokens.size()-tokenBeginIdx; + + if(l->tk == LEX_R_ELSE) { + Marks.push_back(pushToken(Tokens)); // push Token & push tokenBeginIdx + + BlockStart.push_back(Tokens.size()); // set a blockStart + tokenizeStatement(Tokens, Statement, BlockStart, Marks); + BlockStart.pop_back(); + + tokenBeginIdx = Marks.back(); + Marks.pop_back(); // clean-up Marks + Tokens[tokenBeginIdx].Int() = Tokens.size()-tokenBeginIdx; } - return highest+1; -} -int CScriptVar::getInt() { - /* strtol understands about hex and octal */ - if (isInt() || isBool()) return intData; -// if (isNull()) return 0; -// if (isUndefined()) return 0; - if (isDouble()) return (int)doubleData; - if (isString()) return strtol(data.c_str(),0,0); - return 0; + tokenBeginIdx = Marks.back(); + Marks.pop_back(); // clean-up Marks + Tokens[tokenBeginIdx].Int() = Tokens.size()-tokenBeginIdx; } -bool CScriptVar::getBool() { - if (isInt() || isBool()) return intData!=0; -// if (isNull()) return 0; -// if (isUndefined()) return 0; - if (isDouble()) return doubleData!=0; - if (isString()) return data.length()!=0; - if (isInfinity()) return true; - return false; -} - -double CScriptVar::getDouble() { - if (isDouble()) return doubleData; - if (isInt() || isBool()) return intData; -// if (isNull()) return 0.0; -// if (isUndefined()) return 0.0; - if (isString()) return strtod(data.c_str(),0); - return 0.0; /* or NaN? */ -} - -const string &CScriptVar::getString() { - /* Because we can't return a string that is generated on demand. - * I should really just use char* :) */ - static string s_null = "null"; - static string s_undefined = "undefined"; - static string s_NaN = "NaN"; - static string s_Infinity = "Infinity"; - static string s_true = "true"; - static string s_false = "false"; - if (isBool()) return getBool() ? s_true : s_false; - if (isInt()) { - return int2string(intData, data); - } - if (isDouble()) { - char buffer[32]; - sprintf_s(buffer, sizeof(buffer), "%lf", doubleData); - data = buffer; - return data; - } - if (isNull()) return s_null; - if (isNaN()) return s_NaN; - if (isInfinity()) return s_Infinity; - if (isUndefined()) return s_undefined; - // are we just a string here? - return data; -} - -void CScriptVar::setInt(int val) { - init(); - flags = (flags&~SCRIPTVAR_VARTYPEMASK) | SCRIPTVAR_INTEGER; - intData = val; -} - -void CScriptVar::setBool(bool val) { - init(); - flags = (flags&~SCRIPTVAR_VARTYPEMASK) | SCRIPTVAR_BOOLEAN; - intData = val?1:0; -} - -void CScriptVar::setDouble(double val) { - init(); - flags = (flags&~SCRIPTVAR_VARTYPEMASK) | SCRIPTVAR_DOUBLE; - doubleData = val; -} - -void CScriptVar::setString(const string &str) { - // name sure it's not still a number or integer - init(); - flags = (flags&~SCRIPTVAR_VARTYPEMASK) | SCRIPTVAR_STRING; - data = str; -} - -void CScriptVar::setUndefined() { - // name sure it's not still a number or integer - init(); - flags = (flags&~SCRIPTVAR_VARTYPEMASK) | SCRIPTVAR_UNDEFINED; - removeAllChildren(); +static void fix_BlockStarts_Marks(vector &BlockStart, vector &Marks, int start, int diff) { + for(vector::iterator it = BlockStart.begin(); it != BlockStart.end(); ++it) + if(*it >= start) *it += diff; + for(vector::iterator it = Marks.begin(); it != Marks.end(); ++it) + if(*it >= start) *it += diff; } - -template -CScriptVar *DoMaths(CScriptVar *a, CScriptVar *b, int op) -{ - int dai = a->getInt();// use int when needed - int dbi = b->getInt(); - T da = (T)(a->isDouble()?a->getDouble():dai); +void CScriptTokenizer::tokenizeFor(TOKEN_VECT &Tokens, bool &Statement, vector &BlockStart, vector &Marks) { + if(!Statement) throwTokenNotExpected(); + + const char *prev_pos=l->tokenStart; + const char *prev_line_start=l->currentLineStart; + int prev_line = l->currentLine; + bool for_in, for_each_in; + l->match(LEX_R_FOR); + if((for_in = for_each_in = (l->tk == LEX_ID && l->tkStr == "each"))) { + l->match(LEX_ID); + } + if(!for_in) { + l->match('('); + int parentheses = 0; + while(l->tk != ')' || parentheses) + { + if(l->tk == '(') parentheses++; + else if(l->tk == ')') parentheses--; + else if(l->tk == LEX_R_IN) { + for_in = true; + break; + } else if(l->tk==LEX_EOF) { + l->match(')'); + } + l->match(l->tk); + } + } + l->reset(prev_pos, prev_line, prev_line_start); + + + l->match(LEX_R_FOR); + if(for_each_in) { + l->match(LEX_ID); + } + + Marks.push_back(Tokens.size()); // push tokenBeginIdx + Tokens.push_back(CScriptToken(for_in?(for_each_in?LEX_T_FOR_EACH_IN:LEX_T_FOR_IN):LEX_R_FOR)); + + BlockStart.push_back(pushToken(Tokens, '(')+1); // set BlockStart - no forwarde for(let ... + Statement = true; + if(for_in) { + if(l->tk == LEX_R_VAR) { + l->match(LEX_R_VAR); + + int tokenInsertIdx = BlockStart.front(); + Tokens.insert(Tokens.begin()+tokenInsertIdx++, CScriptToken(LEX_R_VAR)); + Tokens.insert(Tokens.begin()+tokenInsertIdx++, CScriptToken(LEX_ID, l->tkStr)); + Tokens.insert(Tokens.begin()+tokenInsertIdx++, CScriptToken(';')); + fix_BlockStarts_Marks(BlockStart, Marks, BlockStart.front(), 3); + } else if(l->tk == LEX_R_LET) { + pushToken(Tokens, LEX_R_LET); + } + pushToken(Tokens, LEX_ID); + pushToken(Tokens, LEX_R_IN); + + } + while(l->tk != ')' && l->tk != LEX_EOF ) { + tokenizeToken(Tokens, Statement, BlockStart, Marks); + } + BlockStart.pop_back(); // pop_back / "no forwarde for(let ... "-prevention + pushToken(Tokens, ')'); + Statement = true; + BlockStart.push_back(Tokens.size()); // set a blockStart + tokenizeStatement(Tokens, Statement, BlockStart, Marks); + BlockStart.pop_back(); + + int tokenBeginIdx = Marks.back(); + Marks.pop_back(); // clean-up Marks + Tokens[tokenBeginIdx].Int() = Tokens.size()-tokenBeginIdx; +} +void CScriptTokenizer::tokenizeFunction(TOKEN_VECT &Tokens, bool &Statement, vector &BlockStart, vector &Marks) { + bool forward = false; + int tk = l->tk; + if(tk == LEX_ID && (l->tkStr=="get"||l->tkStr=="set")) { + string tkStr = l->tkStr; + tk = tkStr=="get"?LEX_T_GET:LEX_T_SET; + l->match(l->tk); + if(l->tk != LEX_ID) { // is not a getter or setter + tokens.push_back(CScriptToken(LEX_ID, tkStr)); + return; + } + } else { + l->match(LEX_R_FUNCTION); + if(!Statement) tk = LEX_T_FUNCTION_OPERATOR; + } + if(tk == LEX_R_FUNCTION) // only forward functions + forward = TOKEN_VECT::size_type(BlockStart.front()) != Tokens.size(); + + CScriptToken FncToken(tk); + CScriptTokenDataFnc &FncData = FncToken.Fnc(); + if(l->tk == LEX_ID) { + FncData.name = l->tkStr; + l->match(LEX_ID); + } else { + //ASSERT(Statement == false); + } + + l->match('('); + while(l->tk != ')') { + FncData.parameter.push_back(l->tkStr); + l->match(LEX_ID); + if (l->tk==',') { + l->match(','); + } + } + l->match(')'); + FncData.file = l->currentFile; + FncData.line = l->currentLine; + + vector functionBlockStart, marks; + functionBlockStart.push_back(FncData.body.size()+1); + l->check('{'); + tokenizeBlock(FncData.body, Statement, functionBlockStart, marks); + + if(forward) { + int tokenInsertIdx = BlockStart.back(); + Tokens.insert(Tokens.begin()+BlockStart.front(), FncToken); + fix_BlockStarts_Marks(BlockStart, Marks, tokenInsertIdx, 1); + } + else + Tokens.push_back(FncToken); +} + +void CScriptTokenizer::tokenizeLet(TOKEN_VECT &Tokens, bool &Statement, vector &BlockStart, vector &Marks) { + + bool forward = TOKEN_VECT::size_type(BlockStart.back()) != Tokens.size(); + bool let_is_Statement = Statement, expression=false; + + Marks.push_back(pushToken(Tokens)); // push Token & push BeginIdx + Statement = false; + + if(l->tk == '(' || !let_is_Statement) { + expression = true; + pushToken(Tokens, '('); + } + vector vars; + for(;;) { + vars.push_back(l->tkStr); + pushToken(Tokens, LEX_ID); + if(l->tk=='=') { + pushToken(Tokens); + for(;;) { + if(l->tk == (expression?')':';') || l->tk == ',' || l->tk == LEX_EOF) break; + tokenizeToken(Tokens, Statement, BlockStart, Marks); + } + } + if(l->tk==',') + pushToken(Tokens); + else + break; + } + if(expression) { + pushToken(Tokens, ')'); + Statement = let_is_Statement; + if(Statement) + tokenizeStatement(Tokens, Statement, BlockStart, Marks); + int tokenBeginIdx = Marks.back(); + Marks.pop_back(); // clean-up Marks + Tokens[tokenBeginIdx].Int() = Tokens.size()-tokenBeginIdx; + } else { + pushToken(Tokens, ';'); + Statement = true; + + int tokenBeginIdx = Marks.back(); + Marks.pop_back(); // clean-up Marks + Tokens[tokenBeginIdx].Int() = Tokens.size()-tokenBeginIdx; + + if(forward) // copy a let-deklaration at the begin of the last block + { + int tokenInsertIdx = tokenBeginIdx = BlockStart.back(); + + Tokens.insert(Tokens.begin()+tokenInsertIdx++, CScriptToken(LEX_R_VAR)); + Tokens.insert(Tokens.begin()+tokenInsertIdx++, CScriptToken(LEX_ID, vars.front())); + for(vector::iterator it = vars.begin()+1; it != vars.end(); ++it) { + Tokens.insert(Tokens.begin()+tokenInsertIdx++, CScriptToken(',')); + Tokens.insert(Tokens.begin()+tokenInsertIdx++, CScriptToken(LEX_ID, *it)); + } + Tokens.insert(Tokens.begin()+tokenInsertIdx++, CScriptToken(';'));// insert at the begin var name; + Tokens[tokenBeginIdx].Int() = tokenInsertIdx-tokenBeginIdx; + + fix_BlockStarts_Marks(BlockStart, Marks, tokenBeginIdx, tokenInsertIdx-tokenBeginIdx); + } + } +} + +void CScriptTokenizer::tokenizeVar(TOKEN_VECT &Tokens, bool &Statement, vector &BlockStart, vector &Marks) { + if(!Statement) throwTokenNotExpected(); + + bool forward = TOKEN_VECT::size_type(BlockStart.front()) != Tokens.size(); // forwarde only if the var-Statement not the first Statement of the Scope + + Marks.push_back(pushToken(Tokens)); // push Token & push BeginIdx + + Statement = false; + vector vars; + for(;;) + { + vars.push_back(l->tkStr); + pushToken(Tokens, LEX_ID); + if(l->tk=='=') { + pushToken(Tokens); + for(;;) { + if(l->tk == ';' || l->tk == ',' || l->tk == LEX_EOF) break; + tokenizeToken(Tokens, Statement, BlockStart, Marks); + } + } + if(l->tk==',') + pushToken(Tokens); + else + break; + } + pushToken(Tokens, ';'); + Statement = true; + + int tokenBeginIdx = Marks.back(); + Marks.pop_back(); // clean-up Marks + Tokens[tokenBeginIdx].Int() = Tokens.size()-tokenBeginIdx; + + if(forward) // copy a var-deklaration at the begin of the scope + { + int tokenInsertIdx = tokenBeginIdx = BlockStart.front(); + + Tokens.insert(Tokens.begin()+tokenInsertIdx++, CScriptToken(LEX_R_VAR)); + Tokens.insert(Tokens.begin()+tokenInsertIdx++, CScriptToken(LEX_ID, vars.front())); + for(vector::iterator it = vars.begin()+1; it != vars.end(); ++it) { + Tokens.insert(Tokens.begin()+tokenInsertIdx++, CScriptToken(',')); + Tokens.insert(Tokens.begin()+tokenInsertIdx++, CScriptToken(LEX_ID, *it)); + } + Tokens.insert(Tokens.begin()+tokenInsertIdx++, CScriptToken(';'));// insert at the begin var name; + Tokens[tokenBeginIdx].Int() = tokenInsertIdx-tokenBeginIdx; + + fix_BlockStarts_Marks(BlockStart, Marks, tokenBeginIdx, tokenInsertIdx-tokenBeginIdx); + } +} + + +void CScriptTokenizer::tokenizeBlock(TOKEN_VECT &Tokens, bool &Statement, vector &BlockStart, vector &Marks) { + int tkEnd = 0; + bool Statement_at_end = false; + switch(l->tk) + { + case'{': tkEnd = '}'; break; + case'(': tkEnd = ')'; break; + case'[': tkEnd = ']'; break; + } + Marks.push_back(pushToken(Tokens)); // push Token & push BeginIdx + + if(tkEnd == '}') // { ... } Block + { + if( (Statement_at_end = Statement) ) // Statment-Level after '}' is the same Level as at start ('{') + BlockStart.push_back(Tokens.size()); // push Block-Start (one Token after '{') + } else + Statement = false; + while(l->tk != tkEnd && l->tk != LEX_EOF) tokenizeToken(Tokens, Statement, BlockStart, Marks); + pushToken(Tokens, tkEnd); + + if(tkEnd == '}' && Statement_at_end) { // { ... } Block + BlockStart.pop_back(); // clean-up BlockStarts + } + + int tokenBeginIdx = Marks.back(); + Marks.pop_back(); // clean-up Marks + Tokens[tokenBeginIdx].Int() = Tokens.size()-tokenBeginIdx; + + Statement = Statement_at_end; +} +void CScriptTokenizer::tokenizeStatement(TOKEN_VECT &Tokens, bool &Statement, vector &BlockStart, vector &Marks) { + do { + tokenizeToken(Tokens, Statement, BlockStart, Marks); + } while(Statement == false && l->tk != LEX_EOF ); // tokenize one Statement + Statement = true; +} +void CScriptTokenizer::tokenizeToken(TOKEN_VECT &Tokens, bool &Statement, vector &BlockStart, vector &Marks) { + switch(l->tk) + { + case '(': + case '[': + case '{': tokenizeBlock(Tokens, Statement, BlockStart, Marks); break; + case LEX_R_VAR: tokenizeVar(Tokens, Statement, BlockStart, Marks); break; + case LEX_R_LET: tokenizeLet(Tokens, Statement, BlockStart, Marks); break; + case LEX_R_FUNCTION: tokenizeFunction(Tokens, Statement, BlockStart, Marks); break; + case LEX_R_FOR: tokenizeFor(Tokens, Statement, BlockStart, Marks); break; + case LEX_R_IF: tokenizeIf(Tokens, Statement, BlockStart, Marks); break; + case LEX_R_DO: tokenizeDo(Tokens, Statement, BlockStart, Marks); break; + case LEX_R_WHILE: tokenizeWhile(Tokens, Statement, BlockStart, Marks); break; + case LEX_R_WITH: tokenizeWith(Tokens, Statement, BlockStart, Marks); break; + case LEX_R_SWITCH: tokenizeSwitch(Tokens, Statement, BlockStart, Marks); break; + case LEX_R_TRY: tokenizeTry(Tokens, Statement, BlockStart, Marks); break; + case LEX_ID: { + string str = l->tkStr; + if(!Statement && (str=="get"||str=="set")) { + tokenizeFunction(Tokens, Statement, BlockStart, Marks); + break; + } + pushToken(Tokens); + if(Statement && l->tk==':') { // label + Tokens[Tokens.size()-1].token = LEX_T_LABEL; // change LEX_ID to LEX_T_LABEL + pushToken(Tokens); + break; + } + } + Statement = false; + break; + default: + Statement = l->tk==';'; // after ';' allways Statement-Level + pushToken(Tokens); + } +#if 0 + if(l->tk == '{' || l->tk == '(' || l->tk == '[') { // block + tokenizeBlock(Tokens, Statement, BlockStart, Marks); + } else if(l->tk == LEX_R_VAR) { + tokenizeVar(Tokens, Statement, BlockStart, Marks); + } else if(l->tk == LEX_R_FUNCTION) { + tokenizeFunction(Tokens, Statement, BlockStart, Marks); + } else if(l->tk == LEX_R_FOR) { + tokenizeFor(Tokens, Statement, BlockStart, Marks); + } else if(l->tk == LEX_R_IF) { + tokenizeIf(Tokens, Statement, BlockStart, Marks); + } else if(l->tk == LEX_R_DO) { + tokenizeDo(Tokens, Statement, BlockStart, Marks); + } else if(l->tk == LEX_R_WHILE) { + tokenizeWhile(Tokens, Statement, BlockStart, Marks); + } else if(l->tk == LEX_R_WITH) { + tokenizeWith(Tokens, Statement, BlockStart, Marks); + } else if(l->tk == LEX_R_TRY) { + tokenizeTry(Tokens, Statement, BlockStart, Marks); + } else if(l->tk == LEX_ID && (l->tkStr=="get"||l->tkStr=="set")) { + string::const_iterator prev_pos=l->tokenStart; + string::const_iterator prev_line_start=l->currentLineStart; + int prev_line = l->currentLine; + string tkStr = l->tkStr; + l->match(l->tk); + bool is_set_get = l->tk == LEX_ID; // true if set/get LEX_ID + l->reset(prev_pos, prev_line, prev_line_start); + if(is_set_get) { + l->tk = tkStr=="get"?LEX_T_GET:LEX_T_SET; + tokenizeFunction(Tokens, Statement, BlockStart, Marks); + } + else + pushToken(Tokens); + } else { + Statement = l->tk==';'; // after ';' allways Statement-Level + pushToken(Tokens); + } +#endif +} +int CScriptTokenizer::pushToken(TOKEN_VECT &Tokens, int Match) { + Tokens.push_back(CScriptToken(l, Match)); + return Tokens.size()-1; +} + +void CScriptTokenizer::throwTokenNotExpected() { + throw new CScriptException("'"+CScriptLex::getTokenStr(l->tk)+"' was not expected", l->currentFile, l->currentLine, l->currentColumn()); +} + +// ----------------------------------------------------------------------------------- CSCRIPTVARLINK + +CScriptVarLink::CScriptVarLink(const CScriptVarPtr &Var, const string &Name /*=TINYJS_TEMP_NAME*/, int Flags /*=SCRIPTVARLINK_DEFAULT*/) + : name(Name), owner(0), flags(Flags&~SCRIPTVARLINK_OWNED) { +#if DEBUG_MEMORY + mark_allocated(this); +#endif + var = Var->ref(); +} + +CScriptVarLink::CScriptVarLink(const CScriptVarLink &link) + : name(link.name), owner(0), flags(SCRIPTVARLINK_DEFAULT) { + // Copy constructor +#if DEBUG_MEMORY + mark_allocated(this); +#endif + var = link.var->ref(); +} + +CScriptVarLink::~CScriptVarLink() { +#if DEBUG_MEMORY + mark_deallocated(this); +#endif + var->unref(this->owner); +} + +CScriptVarPtr CScriptVarLink::getValue() { + if(var->isAccessor()) { + CScriptVarSmartLink getter = var->findChild(TINYJS_ACCESSOR_GET_VAR); + if(getter) { + vector Params; + bool execute; + ASSERT(this->owner); + return (*getter)->getContext()->callFunction(getter, Params, owner, execute); + } + return var->newScriptVar(Undefined); + } else { + return var; + } +} + +void CScriptVarLink::replaceWith(const CScriptVarPtr &newVar) { + if(!newVar) ASSERT(0);//newVar = new CScriptVar(); + if(var->isAccessor()) { + CScriptVarSmartLink setter = var->findChild(TINYJS_ACCESSOR_SET_VAR); + if(setter) { + vector Params(1, newVar); + bool execute; + ASSERT(this->owner); + (*setter)->getContext()->callFunction(setter, Params, owner, execute); + } + } else { + CScriptVar *oldVar = var; + var = newVar->ref(); + oldVar->unref(owner); + if(owner) var->recoursionCheck(owner); + } +} + +void CScriptVarLink::replaceWith(CScriptVarLink *newVar) { + if (newVar) + replaceWith(newVar->getValue()); + else + ASSERT(0);//replaceWith(new CScriptVar()); +} + +// ----------------------------------------------------------------------------------- CSCRIPTVAR +CScriptVar::CScriptVar(CTinyJS *Context, const CScriptVarPtr &Prototype) : context(Context) , temporaryID(0) { + if(context->first) { + next = context->first; + next->prev = this; + } else { + next = 0; + } + context->first = this; + prev = 0; + refs = 0; + internalRefs = 0; + recursionFlag = 0; + recursionSet = 0; + if(Prototype) + addChild(TINYJS___PROTO___VAR, Prototype, 0); + else if(context->objectPrototype) + addChild(TINYJS___PROTO___VAR, context->objectPrototype, 0); +#if DEBUG_MEMORY + mark_allocated(this); +#endif +} +CScriptVar::CScriptVar(const CScriptVar &Copy) : context(Copy.context), temporaryID(0) { + if(context->first) { + next = context->first; + next->prev = this; + } else { + next = 0; + } + context->first = this; + prev = 0; + refs = 0; + internalRefs = 0; + recursionFlag = 0; + recursionSet = 0; + SCRIPTVAR_CHILDS_cit it; + for(it = Copy.Childs.begin(); it!= Copy.Childs.end(); ++it) { + addChild((*it)->name, (*it), (*it)->flags); + } + +#if DEBUG_MEMORY + mark_allocated(this); +#endif +} +CScriptVar::~CScriptVar(void) { +#if DEBUG_MEMORY + mark_deallocated(this); +#endif + removeAllChildren(); + if(prev) + prev->next = next; + else + context->first = next; + if(next) + next->prev = prev; +} + +/// Type +bool CScriptVar::isInt() {return false;} +bool CScriptVar::isBool() {return false;} +bool CScriptVar::isDouble() {return false;} +bool CScriptVar::isString() {return false;} +bool CScriptVar::isNumber() {return false;} +bool CScriptVar::isNumeric() {return false;} +bool CScriptVar::isFunction() {return false;} +bool CScriptVar::isObject() {return false;} +bool CScriptVar::isArray() {return false;} +bool CScriptVar::isNative() {return false;} +bool CScriptVar::isUndefined() {return false;} +bool CScriptVar::isNull() {return false;} +bool CScriptVar::isNaN() {return false;} +int CScriptVar::isInfinity() { return 0; } ///< +1==POSITIVE_INFINITY, -1==NEGATIVE_INFINITY, 0==is not an InfinityVar +bool CScriptVar::isAccessor() {return false;} + + +/// Value +int CScriptVar::getInt() {return 0;} +bool CScriptVar::getBool() {return false;} +double CScriptVar::getDouble() {return 0.0;} +CScriptTokenDataFnc *CScriptVar::getFunctionData() { return 0; } +string CScriptVar::getString() {return "";} + +string CScriptVar::getParsableString(const string &indentString, const string &indent) { + return getString(); +} +string CScriptVar::getVarType() { + if(this->isBool()) + return "boolean"; + else if(this->isFunction()) + return "function"; + else if(this->isNumeric() || this->isInfinity() || this->isNaN()) + return "number"; + else if(this->isString()) + return "string"; + else if(this->isUndefined()) + return "undefined"; + return "object"; // Object / Array / null +} +///< returns an Integer, a Double, an Infinity or a NaN +CScriptVarLink *CScriptVar::getNumericVar() +{ + if(isNumber() || isInfinity()) + return new CScriptVarLink(this); + else if(isNull()) + return new CScriptVarLink(newScriptVar(0)); + else if(isBool()) + return new CScriptVarLink(newScriptVar(getInt())); +/* ToDo + else if(Var->isObject()) + return ToDo call Object.valueOff(); + else if(Var->isArray()) + return ToDo call Object.valueOff(); +*/ + else if(isString()) { + string the_string = getString(); + double d; + char *endptr;//=NULL; + int i = strtol(the_string.c_str(),&endptr,0); + if(*endptr == '\0') + return new CScriptVarLink(newScriptVar(i)); + if(*endptr=='.' || *endptr=='e' || *endptr=='E') { + d = strtod(the_string.c_str(),&endptr); + if(*endptr == '\0') + return new CScriptVarLink(newScriptVar(d)); + } + } + return new CScriptVarLink(newScriptVar(NaN)); +} + + +////// Childs + +/// find +static bool compare_child_name(CScriptVarLink *Link, const string &Name) { + return Link->name < Name; +} + +CScriptVarLink *CScriptVar::findChild(const string &childName) { + if(Childs.empty()) return 0; + SCRIPTVAR_CHILDS_it it = lower_bound(Childs.begin(), Childs.end(), + childName.c_str(), + compare_child_name); + if(it != Childs.end() && (*it)->name == childName) + return *it; + return 0; +} +CScriptVarLink *CScriptVar::findChildByPath(const string &path) { + string::size_type p = path.find('.'); + CScriptVarLink *child; + if (p == string::npos) + return findChild(path); + if( (child = findChild(path.substr(0,p))) ) + (*child)->findChildByPath(path.substr(p+1)); + return 0; +} + +CScriptVarLink *CScriptVar::findChildOrCreate(const string &childName/*, int varFlags*/) { + CScriptVarLink *l = findChild(childName); + if (l) return l; + return addChild(childName, newScriptVar(Undefined)); + // return addChild(childName, new CScriptVar(context, TINYJS_BLANK_DATA, varFlags)); +} + +CScriptVarLink *CScriptVar::findChildOrCreateByPath(const string &path) { + string::size_type p = path.find('.'); + if (p == string::npos) + return findChildOrCreate(path); + string childName(path, 0, p); + CScriptVarLink *l = findChild(childName); + if (!l) l = addChild(childName, newScriptVar(Object)); + return (*l)->findChildOrCreateByPath(path.substr(p+1)); +} + +/// add & remove +CScriptVarLink *CScriptVar::addChild(const string &childName, const CScriptVarPtr &child, int linkFlags /*= SCRIPTVARLINK_DEFAULT*/) { +#ifdef _DEBUG + if(findChild(childName)) + ASSERT(0); // addCild - the child exists +#endif + CScriptVarLink *link = new CScriptVarLink(child?child:newScriptVar(Undefined), childName, linkFlags); + link->owner = this; + link->SetOwned(true); + SCRIPTVAR_CHILDS_it it = lower_bound(Childs.begin(), Childs.end(), childName, compare_child_name); + if(it == Childs.end() || (*it)->name != childName) { + it = Childs.insert(it, link); + } + (*link)->recoursionCheck(this); + return link; +} +CScriptVarLink *CScriptVar::addChildNoDup(const string &childName, const CScriptVarPtr &child, int linkFlags /*= SCRIPTVARLINK_DEFAULT*/) { + // if no child supplied, create one + + CScriptVarLink *link = findChild(childName); + if (link) { + if(link->operator->() != child.operator->()) link->replaceWith(child); + } else { + link = new CScriptVarLink(child, childName, linkFlags); + link->owner = this; + link->SetOwned(true); + SCRIPTVAR_CHILDS_it it = lower_bound(Childs.begin(), Childs.end(), childName, compare_child_name); + if(it == Childs.end() || (*it)->name != childName) { + it = Childs.insert(it, link); + } + (*link)->recoursionCheck(this); + } + + return link; +} + +bool CScriptVar::removeLink(CScriptVarLink *&link) { + if (!link) return false; +#ifdef _DEBUG + if(findChild(link->name) != link) + ASSERT(0); // removeLink - the link is not atached to this var +#endif + SCRIPTVAR_CHILDS_it it = lower_bound(Childs.begin(), Childs.end(), link->name, compare_child_name); + if(it != Childs.end() && (*it)->name == link->name) { + Childs.erase(it); + } + delete link; + link = 0; + return true; +} +void CScriptVar::removeAllChildren() { + SCRIPTVAR_CHILDS_it it; + for(it = Childs.begin(); it!= Childs.end(); ++it) { + delete *it; + } + Childs.clear(); +} + + +/// funcions for FUNCTION + +void CScriptVar::setReturnVar(const CScriptVarPtr &var) { + CScriptVarLink *l = findChild(TINYJS_RETURN_VAR); + if (l) l->replaceWith(var); + else addChild(TINYJS_RETURN_VAR, var); +} + +CScriptVarPtr CScriptVar::getParameter(const string &name) { + return findChildOrCreate(name); +} + +CScriptVarPtr CScriptVar::getParameter(int Idx) { + CScriptVarLink *arguments = findChildOrCreate(TINYJS_ARGUMENTS_VAR); + return (*arguments)->findChildOrCreate(int2string(Idx)); +} +int CScriptVar::getParameterLength() { + CScriptVarLink *arguments = findChildOrCreate(TINYJS_ARGUMENTS_VAR); + return (*(*arguments)->findChildOrCreate("length"))->getInt(); +} + + + + +CScriptVarPtr CScriptVar::getArrayIndex(int idx) { + CScriptVarLink *link = findChild(int2string(idx)); + if (link) return link; + else return newScriptVar(Undefined); // undefined +} + +void CScriptVar::setArrayIndex(int idx, const CScriptVarPtr &value) { + string sIdx = int2string(idx); + CScriptVarLink *link = findChild(sIdx); + + if (link) { + if (value->isUndefined()) + removeLink(link); + else + link->replaceWith(value); + } else { + if (!value->isUndefined()) + addChild(sIdx, value); + } +} + +int CScriptVar::getArrayLength() { + int highest = -1; + if (!isArray()) return 0; + + for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it) { + if (::isNumber((*it)->name)) { + int val = atoi((*it)->name.c_str()); + if (val > highest) highest = val; + } + } + return highest+1; +} + +template +CScriptVarPtr DoMaths(CScriptVarPtr &a, CScriptVarPtr &b, int op) +{ + int dai = a->getInt();// use int when needed + int dbi = b->getInt(); + T da = (T)(a->isDouble()?a->getDouble():dai); T db = (T)(b->isDouble()?b->getDouble():dbi); switch (op) { - case '+': return new CScriptVar(da+db); - case '-': return new CScriptVar(da-db); - case '*': return new CScriptVar(da*db); + case '+': return a->newScriptVar(da+db); + case '-': return a->newScriptVar(da-db); + case '*': return a->newScriptVar(da*db); case '/': - if(db==0) - if(da==0) return new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_NAN); // 0/0 = NaN - else return new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_INFINITY); // /0 = Infinity - else return new CScriptVar(da/db); - case '&': return new CScriptVar(dai&dbi); - case '|': return new CScriptVar(dai|dbi); - case '^': return new CScriptVar(dai^dbi); - case '%': return new CScriptVar(dai%dbi); - case '~': return new CScriptVar(~dai); - case LEX_LSHIFT: return new CScriptVar(dai<>dbi); - case LEX_EQUAL: return new CScriptVar(da==db); - case LEX_NEQUAL: return new CScriptVar(da!=db); - case '<': return new CScriptVar(da': return new CScriptVar(da>db); - case LEX_GEQUAL: return new CScriptVar(da>=db); + if(db==0) { + if(da==0) return a->newScriptVar(NaN); // 0/0 = NaN + else return a->newScriptVar(Infinity(da<0 ? -1 : 1)); // /0 = Infinity + } else return a->newScriptVar(da/db); + case '%': + if(db==0) return a->newScriptVar(NaN); // 0/0 = NaN + else return a->newScriptVar(dai%dbi); + case '&': return a->newScriptVar(dai&dbi); + case '|': return a->newScriptVar(dai|dbi); + case '^': return a->newScriptVar(dai^dbi); + case '~': return a->newScriptVar(~dai); + case LEX_LSHIFT: return a->newScriptVar(dai<newScriptVar(dai>>dbi); + case LEX_RSHIFTU: return a->newScriptVar((int)(((unsigned int)dai)>>dbi)); + case LEX_EQUAL: return a->newScriptVar(da==db); + case LEX_NEQUAL: return a->newScriptVar(da!=db); + case '<': return a->newScriptVar(danewScriptVar(da<=db); + case '>': return a->newScriptVar(da>db); + case LEX_GEQUAL: return a->newScriptVar(da>=db); default: throw new CScriptException("This operation not supported on the int datatype"); } } -static CScriptVar *String2NumericVar(string &numeric) { - double d; - char *endptr;//=NULL; - int i = strtol(numeric.c_str(),&endptr,10); - if(*endptr == 'x' || *endptr == 'X') - i = strtol(numeric.c_str(),&endptr,16); - if(*endptr == '\0') - return new CScriptVar(i); - if(*endptr=='.' || *endptr=='e' || *endptr=='E') { - d = strtod(numeric.c_str(),&endptr); - if(*endptr == '\0') - return new CScriptVar(d); - } - return NULL; -} -CScriptVar *CScriptVar::mathsOp(CScriptVar *b, int op) { - CScriptVar *a = this; +#define RETURN_NAN return newScriptVar(NaN) +CScriptVarPtr CScriptVar::mathsOp(const CScriptVarPtr &b, int op) { + CScriptVarPtr a = this; // TODO Equality checks on classes/structures // Type equality check if (op == LEX_TYPEEQUAL || op == LEX_NTYPEEQUAL) { // check type first, then call again to check data bool eql = false; - if(!a->isNaN() && !b->isNaN() && - ((a->flags & SCRIPTVAR_VARTYPEMASK) == - (b->flags & SCRIPTVAR_VARTYPEMASK))) { - CScriptVar * e = a->mathsOp(b, LEX_EQUAL); + if(!a->isNaN() && !b->isNaN() && typeid(*a.getVar()) == typeid(*b.getVar()) ) { + CScriptVarPtr e = a->mathsOp(b, LEX_EQUAL); eql = e->getBool(); - delete e; } if (op == LEX_TYPEEQUAL) - return new CScriptVar(eql); + return newScriptVar(eql); else - return new CScriptVar(!eql); + return newScriptVar(!eql); } // do maths... - if( a->isNaN() || b->isNaN() || ((a->isUndefined() || b->isUndefined()) && !a->isString() && !b->isString())) { + bool a_isString = a->isString(); + bool b_isString = b->isString(); + // special for strings and string '+' + // both a String or one a String and op='+' + if( (a_isString && b_isString) || ((a_isString || b_isString) && op == '+')) { + string da = a->getString(); + string db = b->getString(); switch (op) { - case LEX_NEQUAL: return new CScriptVar( !( ( a->isUndefined() || a->isNull() ) && ( b->isUndefined() || b->isNull() ) ) ); - case LEX_EQUAL: return new CScriptVar( ( a->isUndefined() || a->isNull() ) && ( b->isUndefined() || b->isNull() ) ); - case LEX_GEQUAL: - case LEX_LEQUAL: - case '<': - case '>': return new CScriptVar(false); - default: return new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_NAN); - } - } else if (a->isNumeric() && b->isNumeric()) { - if (!a->isDouble() && !b->isDouble()) { - // use ints - return DoMaths(a, b, op); - } else { - // use doubles - return DoMaths(a, b, op); + case '+': return newScriptVar(da+db); + case LEX_EQUAL: return newScriptVar(da==db); + case LEX_NEQUAL: return newScriptVar(da!=db); + case '<': return newScriptVar(da': return newScriptVar(da>db); + case LEX_GEQUAL: return newScriptVar(da>=db); + default: RETURN_NAN; } - } else if((a->isInfinity() || b->isInfinity()) && !a->isString() && !b->isString()) { + } + // special for undefined and null + else if( (a->isUndefined() || a->isNull()) && (b->isUndefined() || b->isNull()) ) { switch (op) { - case LEX_EQUAL: - case LEX_GEQUAL: - case LEX_LEQUAL: return new CScriptVar(a->isInfinity() == b->isInfinity()); - case LEX_NEQUAL: return new CScriptVar(a->isInfinity() != b->isInfinity()); - case '<': - case '>': return new CScriptVar(false); - case '/': - case '-': if(a->isInfinity() && b->isInfinity()) return new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_NAN); - default: return new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_INFINITY); + case LEX_NEQUAL: return newScriptVar( !( ( a->isUndefined() || a->isNull() ) && ( b->isUndefined() || b->isNull() ) ) ); + case LEX_EQUAL: return newScriptVar( ( a->isUndefined() || a->isNull() ) && ( b->isUndefined() || b->isNull() ) ); + case LEX_GEQUAL: + case LEX_LEQUAL: + case '<': + case '>': return newScriptVar(false); + default: RETURN_NAN; } - } else if (a->isArray()) { + } + if (a->isArray() && b->isArray()) { /* Just check pointers */ switch (op) { - case LEX_EQUAL: return new CScriptVar(a==b); - case LEX_NEQUAL: return new CScriptVar(a!=b); - default: return new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_NAN); + case LEX_EQUAL: return newScriptVar(a==b); + case LEX_NEQUAL: return newScriptVar(a!=b); } - } else if (a->isObject()) { - /* Just check pointers */ - switch (op) { - case LEX_EQUAL: return new CScriptVar(a==b); - case LEX_NEQUAL: return new CScriptVar(a!=b); - default: return new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_NAN); - } - } else { //Strings - string da = a->getString(); - string db = b->getString(); -/* - if((a->isNumeric() || b->isNumeric()) && op != '+') - { - CScriptVar *ret = 0; - CScriptVar *a_num = String2NumericVar(da); - CScriptVar *b_num = String2NumericVar(db); - if(a_num && b_num) - ret = a_num->mathsOp(b_num, op); - if(a_num) delete a_num; - if(b_num) delete b_num; - if(ret) - return ret; - } -*/ - // use strings + } else if (a->isObject() && b->isObject()) { + /* Just check pointers */ switch (op) { - case '+': return new CScriptVar(da+db, SCRIPTVAR_STRING); - case LEX_EQUAL: return new CScriptVar(da==db); - case LEX_NEQUAL: return new CScriptVar(da!=db); - case '<': return new CScriptVar(da': return new CScriptVar(da>db); - case LEX_GEQUAL: return new CScriptVar(da>=db); - default: - { - CScriptVar *ret = 0; - CScriptVar *a_num = String2NumericVar(da); - CScriptVar *b_num = String2NumericVar(db); - if(a_num && b_num) - ret = a_num->mathsOp(b_num, op); - if(a_num) delete a_num; - if(b_num) delete b_num; - if(ret) - return ret; - } - return new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_NAN); + case LEX_EQUAL: return newScriptVar(a==b); + case LEX_NEQUAL: return newScriptVar(a!=b); } } - ASSERT(0); - return 0; -} - -void CScriptVar::copySimpleData(CScriptVar *val) { - data = val->data; - intData = val->intData; - doubleData = val->doubleData; - flags = (flags & ~SCRIPTVAR_VARTYPEMASK) | (val->flags & SCRIPTVAR_VARTYPEMASK); -} - -void CScriptVar::copyValue(CScriptVar *val) { - if (val) { - copySimpleData(val); - // remove all current children - removeAllChildren(); - // copy children of 'val' - for(SCRIPTVAR_CHILDS::iterator it = val->Childs.begin(); it != val->Childs.end(); ++it) { - CScriptVar *copied; - // don't copy the 'parent' object... - if (it->first != TINYJS_PROTOTYPE_CLASS) - copied = it->second->var->deepCopy(); - else - copied = it->second->var; + + // gets only an Integer, a Double, in Infinity or a NaN + CScriptVarSmartLink a_l = a->getNumericVar(); + CScriptVarSmartLink b_l = b->getNumericVar(); + { + CScriptVarPtr a = a_l; + CScriptVarPtr b = b_l; - addChild(it->first, copied); + if( a->isNaN() || b->isNaN() ) { + switch (op) { + case LEX_NEQUAL: return newScriptVar(true); + case LEX_EQUAL: + case LEX_GEQUAL: + case LEX_LEQUAL: + case '<': + case '>': return newScriptVar(false); + default: RETURN_NAN; + } + } + else if((a->isInfinity() || b->isInfinity())) { + int tmp = 0; + int a_i=a->isInfinity(), a_sig=a->getInt()>0?1:-1; + int b_i=b->isInfinity(), b_sig=a->getInt()>0?1:-1; + switch (op) { + case LEX_EQUAL: return newScriptVar(a_i == b_i); + case LEX_GEQUAL: + case '>': return newScriptVar(a_i >= b_i); + case LEX_LEQUAL: + case '<': return newScriptVar(a_i <= b_i); + case LEX_NEQUAL: return newScriptVar(a_i != b_i); + case '+': if(a_i && b_i && a_i != b_i) RETURN_NAN; + return newScriptVar(Infinity(b_i?b_i:a_i)); + case '-': if(a_i && a_i == b_i) RETURN_NAN; + return newScriptVar(Infinity(b_i?-b_i:a_i)); + case '*': tmp = a->getInt() * b->getInt(); + if(tmp == 0) RETURN_NAN; + return newScriptVar(Infinity(tmp)); + case '/': if(a_i && b_i) RETURN_NAN; + if(b_i) return newScriptVar(0); + return newScriptVar(Infinity(a_sig*b_sig)); + case '%': if(a_i) RETURN_NAN; + return newScriptVar(Infinity(a_sig)); + case '&': return newScriptVar( 0); + case '|': + case '^': if(a_i && b_i) return newScriptVar( 0); + return newScriptVar(a_i?b->getInt():a->getInt()); + case LEX_LSHIFT: + case LEX_RSHIFT: + case LEX_RSHIFTU: if(a_i) return newScriptVar(0); + return newScriptVar(a->getInt()); + default: throw new CScriptException("This operation not supported on the int datatype"); + } + } else { + if (!a->isDouble() && !b->isDouble()) { + // use ints + return DoMaths(a, b, op); + } else { + // use doubles + return DoMaths(a, b, op); + } } - } else { - setUndefined(); - } -} - -CScriptVar *CScriptVar::deepCopy() { - CScriptVar *newVar = new CScriptVar(); - newVar->copySimpleData(this); - // copy children - for(SCRIPTVAR_CHILDS::iterator it = Childs.begin(); it != Childs.end(); ++it) { - CScriptVar *copied; - // don't copy the 'parent' object... - if (it->first != TINYJS_PROTOTYPE_CLASS) - copied = it->second->var->deepCopy(); - else - copied = it->second->var; - - newVar->addChild(it->first, copied); } - return newVar; + + ASSERT(0); + return CScriptVarPtr(); } void CScriptVar::trace(const string &name) { - string tmp; - trace(tmp, name); + string indentStr; + trace(indentStr, name); } void CScriptVar::trace(string &indentStr, const string &name) { string indent = " "; @@ -1355,8 +2007,9 @@ void CScriptVar::trace(string &indentStr, const string &name) { getFlagsAsString().c_str()); indentStr+=indent; - for(SCRIPTVAR_CHILDS::iterator it = Childs.begin(); it != Childs.end(); ++it) { - it->second->var->trace(indentStr, it->first); + for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it) { + if((*it)->IsEnumerable()) + (*(*it))->trace(indentStr, (*it)->name); } if(recursionSet) { recursionSet->recursionPathBase = 0; @@ -1366,70 +2019,35 @@ void CScriptVar::trace(string &indentStr, const string &name) { } string CScriptVar::getFlagsAsString() { - string flagstr = ""; - if (flags&SCRIPTVAR_FUNCTION) flagstr = flagstr + "FUNCTION "; - if (flags&SCRIPTVAR_OBJECT) flagstr = flagstr + "OBJECT "; - if (flags&SCRIPTVAR_ARRAY) flagstr = flagstr + "ARRAY "; - if (flags&SCRIPTVAR_NATIVE) flagstr = flagstr + "NATIVE "; - if (flags&SCRIPTVAR_DOUBLE) flagstr = flagstr + "DOUBLE "; - if (flags&SCRIPTVAR_INTEGER) flagstr = flagstr + "INTEGER "; - if (flags&SCRIPTVAR_BOOLEAN) flagstr = flagstr + "BOOLEAN "; - if (flags&SCRIPTVAR_STRING) flagstr = flagstr + "STRING "; - return flagstr; -} - -string CScriptVar::getParsableString() { - // Numbers can just be put in directly - if (isNumeric()) - return getString(); - if (isFunction()) { - ostringstream funcStr; - funcStr << "function ("; - // get list of parameters - int count = Childs.size(); - for(SCRIPTVAR_CHILDS::iterator it = Childs.begin(); it != Childs.end(); ++it) { - funcStr << it->first; - if(--count) funcStr << ", "; - } - // add function body - funcStr << ") " << getString(); - return funcStr.str(); - } - // if it is a string then we quote it - if (isString()) - return getJSString(getString()); - if (isNull()) - return "null"; - return "undefined"; -} -string CScriptVar::getVarType() { - if(this->isBool()) - return "boolean"; - else if(this->isFunction()) - return "function"; - else if(this->isNumeric() || this->isInfinity() || this->isNaN()) - return "number"; - else if(this->isString()) - return "string"; - else if(this->isUndefined()) - return "undefined"; - return "object"; // Object / Array / null + string flagstr = ""; + if (isFunction()) flagstr = flagstr + "FUNCTION "; + if (isObject()) flagstr = flagstr + "OBJECT "; + if (isArray()) flagstr = flagstr + "ARRAY "; + if (isNative()) flagstr = flagstr + "NATIVE "; + if (isDouble()) flagstr = flagstr + "DOUBLE "; + if (isInt()) flagstr = flagstr + "INTEGER "; + if (isBool()) flagstr = flagstr + "BOOLEAN "; + if (isString()) flagstr = flagstr + "STRING "; + if (isNaN()) flagstr = flagstr + "NaN "; + if (isInfinity()) flagstr = flagstr + "INFINITY "; + return flagstr; } +#ifdef TODO void CScriptVar::getJSON(ostringstream &destination, const string linePrefix) { if (isObject()) { string indentedLinePrefix = linePrefix+" "; // children - handle with bracketed list destination << "{ \n"; int count = Childs.size(); - for(SCRIPTVAR_CHILDS::iterator it = Childs.begin(); it != Childs.end(); ++it) { + for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it) { destination << indentedLinePrefix; - if (isAlphaNum(it->first)) - destination << it->first; + if (isAlphaNum((*it)->name)) + destination << (*it)->name; else - destination << getJSString(it->first); + destination << getJSString((*it)->name); destination << " : "; - it->second->var->getJSON(destination, indentedLinePrefix); + (*(*it))->getJSON(destination, indentedLinePrefix); if (--count) destination << ",\n"; } destination << "\n" << linePrefix << "}"; @@ -1450,18 +2068,8 @@ void CScriptVar::getJSON(ostringstream &destination, const string linePrefix) { destination << getParsableString(); } } - - -void CScriptVar::setCallback(JSCallback callback, void *userdata) { - jsCallback = callback; - jsCallbackUserData = userdata; - flags = (flags & ~SCRIPTVAR_NATIVE) | SCRIPTVAR_NATIVE_FNC; -} -void CScriptVar::setCallback(NativeFncBase *callback, void *userdata) { - jsCallbackClass = callback; - jsCallbackUserData = userdata; - flags = (flags & ~SCRIPTVAR_NATIVE) | SCRIPTVAR_NATIVE_MFNC; -} +#endif + CScriptVar *CScriptVar::ref() { refs++; @@ -1478,9 +2086,9 @@ RECURSION_SET_VAR *CScriptVar::unrefInternal(){ recursionSet->sumRefs -= refs; recursionSet->recursionSet.erase(this); if(recursionSet->recursionSet.size()) { - for(SCRIPTVAR_CHILDS::iterator it = Childs.begin(); it!= Childs.end(); ++it) { - if(it->second->var->recursionSet == recursionSet) - it->second->var->unrefInternal(); + for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it!= Childs.end(); ++it) { + if((*(*it))->recursionSet == recursionSet) + (*(*it))->unrefInternal(); } ret = recursionSet; } else @@ -1529,11 +2137,11 @@ void CScriptVar::unref(CScriptVar* Owner) { else { //external Ref ASSERT(refs); // by a Set it is not possible to remove an external Ref as the last ref if(recursionSet->sumRefs==recursionSet->sumInternalRefs) { - SCRIPTVAR_CHILDS copyOfChilds(Childs); + SCRIPTVAR_CHILDS_t copyOfChilds(Childs); Childs.clear(); - SCRIPTVAR_CHILDS::iterator it; + SCRIPTVAR_CHILDS_it it; for(it = copyOfChilds.begin(); it!= copyOfChilds.end(); ++it) { - delete it->second; + delete *it; } copyOfChilds.clear(); } // otherwise nothing to do @@ -1554,8 +2162,8 @@ RECURSION_VECT::iterator find_last(RECURSION_VECT &Vector, const RECURSION_VECT: } return Vector.end(); } - void CScriptVar::recoursionCheck(CScriptVar *Owner) { + return; if(recursionSet && Owner &&recursionSet == Owner->recursionSet) { // special thing for self-linking Owner->internalRefs++; Owner->recursionSet->sumInternalRefs++; @@ -1618,9 +2226,9 @@ void CScriptVar::recoursionCheck(RECURSION_VECT &recursionPath) // goes to all Childs of the Set RECURSION_SET Set(recursionSet->recursionSet); for(RECURSION_SET::iterator set_it = Set.begin(); set_it != Set.end(); ++set_it) { - for(SCRIPTVAR_CHILDS::iterator it = (*set_it)->Childs.begin(); it != (*set_it)->Childs.end(); ++it) { - if(it->second->var->recursionSet != recursionSet) - it->second->var->recoursionCheck(recursionPath); + for(SCRIPTVAR_CHILDS_it it = (*set_it)->Childs.begin(); it != (*set_it)->Childs.end(); ++it) { + if((*(*it))->recursionSet != recursionSet) + (*(*it))->recoursionCheck(recursionPath); } } if(old_set == recursionSet) { // old_set == recursionSet means this Set is *not* included in an other Set @@ -1631,8 +2239,8 @@ void CScriptVar::recoursionCheck(RECURSION_VECT &recursionPath) recursionPath.push_back(this); // push "this" in the Path recursionFlag = 1; // marked this Var as "the Var is in the Path" // goes to all Childs of the Var - for(SCRIPTVAR_CHILDS::iterator it = Childs.begin(); it != Childs.end(); ++it) { - it->second->var->recoursionCheck(recursionPath); + for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it) { + (*(*it))->recoursionCheck(recursionPath); } if(recursionFlag) { // recursionFlag!=0 means this Var is not included in a Set recursionFlag = 0; @@ -1648,201 +2256,657 @@ void CScriptVar::recoursionCheck(RECURSION_VECT &recursionPath) } } } +void CScriptVar::setTempraryIDrecursive(int ID) { + if(temporaryID != ID) { + temporaryID = ID; + for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it) { + (*(*it))->setTempraryIDrecursive(ID); + } + } +} -// ----------------------------------------------------------------------------------- CSCRIPT -CTinyJS::CTinyJS(bool TwoPass) { - twoPass=TwoPass; - funcOffset = 0; - l = NULL; +////////////////////////////////////////////////////////////////////////// CScriptVarObject + +declare_dummy_t(Object); +//CScriptVarObject::CScriptVarObject(CTinyJS *Context, bool withPrototype=true) : CScriptVar(Context, withPrototype?Context->objectPrototype:CScriptVarPtr()) {} +CScriptVarObject::~CScriptVarObject() {} +CScriptVarPtr CScriptVarObject::clone() { return new CScriptVarObject(*this); } +bool CScriptVarObject::isObject() { return true; } +string CScriptVarObject::getString() { return "[ Object ]"; }; +string CScriptVarObject::getParsableString(const string &indentString, const string &indent) { + ostringstream destination; + string nl = indent.size() ? "\n" : " "; + + destination << "{"; + if(Childs.size()) { + string new_indentString = indentString + indent; + int count = 0; + destination << nl; + for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it) { + if((*it)->IsEnumerable()) { + if (count++) destination << "," << nl; + if (isAlphaNum((*it)->name)) + destination << new_indentString << (*it)->name; + else + destination << new_indentString << "\"" << getJSString((*it)->name) << "\""; + destination << " : "; + destination << (*(*it))->getParsableString(new_indentString, indent); + } + } + destination << nl << indentString; + } + destination << "}"; + return destination.str(); +} +string CScriptVarObject::getVarType() { return "object"; } + + +////////////////////////////////////////////////////////////////////////// CScriptVarAccessor + +declare_dummy_t(Accessor); +CScriptVarAccessor::~CScriptVarAccessor() {} +CScriptVarPtr CScriptVarAccessor::clone() { return new CScriptVarAccessor(*this); } +bool CScriptVarAccessor::isAccessor() { return true; } +string CScriptVarAccessor::getString() { return "[ Object ]"; }; +string CScriptVarAccessor::getParsableString(const string &indentString, const string &indent) { + + /* + ostringstream destination; + string nl = indent.size() ? "\n" : " "; + + destination << "{"; + int count = Childs.size(); + if(count) { + string new_indentString = indentString + indent; + + destination << nl; + for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it) { + if (isAlphaNum((*it)->name)) + destination << new_indentString << (*it)->name; + else + destination << new_indentString << "\"" << getJSString((*it)->name) << "\""; + destination << " : "; + destination << (*(*it))->getParsableString(new_indentString, indent); + if (--count) destination << "," << nl; + } + destination << nl << indentString; + } + destination << "}"; + return destination.str(); +*/ + return ""; +} +string CScriptVarAccessor::getVarType() { return "accessor"; } + + + +////////////////////////////////////////////////////////////////////////// CScriptVarArray + +declare_dummy_t(Array); +CScriptVarArray::CScriptVarArray(CTinyJS *Context) : CScriptVar(Context, Context->arrayPrototype) { + CScriptVarLink *acc = addChild("length", newScriptVar(Accessor), 0); + CScriptVarFunctionPtr getter(::newScriptVar(Context, this, &CScriptVarArray::native_Length, 0)); + getter->setFunctionData(new CScriptTokenDataFnc); + (*acc)->addChild(TINYJS_ACCESSOR_GET_VAR, getter, 0); +} + +CScriptVarArray::~CScriptVarArray() {} +CScriptVarPtr CScriptVarArray::clone() { return new CScriptVarArray(*this); } +bool CScriptVarArray::isArray() { return true; } +string CScriptVarArray::getString() { + ostringstream destination; + int len = getArrayLength(); + for (int i=0;igetString(); + if (igetParsableString(new_indentString, indent); + if (isetReturnVar(newScriptVar(c->getParameter("this")->getArrayLength())); +} + +////////////////////////////////////////////////////////////////////////// CScriptVarNull + +declare_dummy_t(Null); +CScriptVarNull::~CScriptVarNull() {} +CScriptVarPtr CScriptVarNull::clone() { return new CScriptVarNull(*this); } +bool CScriptVarNull::isNull() { return true; } +string CScriptVarNull::getString() { return "null"; }; +string CScriptVarNull::getVarType() { return "null"; } + + +////////////////////////////////////////////////////////////////////////// CScriptVarUndefined + +declare_dummy_t(Undefined); +CScriptVarUndefined::~CScriptVarUndefined() {} +CScriptVarPtr CScriptVarUndefined::clone() { return new CScriptVarUndefined(*this); } +bool CScriptVarUndefined::isUndefined() { return true; } +string CScriptVarUndefined::getString() { return "undefined"; }; +string CScriptVarUndefined::getVarType() { return "undefined"; } + + +////////////////////////////////////////////////////////////////////////// CScriptVarNaN + +declare_dummy_t(NaN); +CScriptVarNaN::~CScriptVarNaN() {} +CScriptVarPtr CScriptVarNaN::clone() { return new CScriptVarNaN(*this); } +bool CScriptVarNaN::isNaN() { return true; } +string CScriptVarNaN::getString() { return "NaN"; }; +string CScriptVarNaN::getVarType() { return "number"; } + + +////////////////////////////////////////////////////////////////////////// CScriptVarString + +CScriptVarString::CScriptVarString(CTinyJS *Context, const std::string &Data) : CScriptVar(Context, Context->stringPrototype), data(Data) { + CScriptVarLink *acc = addChild("length", newScriptVar(Accessor), 0); + CScriptVarFunctionPtr getter(::newScriptVar(Context, this, &CScriptVarString::native_Length, 0)); + getter->setFunctionData(new CScriptTokenDataFnc); + (*acc)->addChild(TINYJS_ACCESSOR_GET_VAR, getter, 0); +} +CScriptVarString::~CScriptVarString() {} +CScriptVarPtr CScriptVarString::clone() { return new CScriptVarString(*this); } +bool CScriptVarString::isString() { return true; } +int CScriptVarString::getInt() {return strtol(data.c_str(),0,0); } +bool CScriptVarString::getBool() {return data.length()!=0;} +double CScriptVarString::getDouble() {return strtod(data.c_str(),0);} +string CScriptVarString::getString() { return data; } +string CScriptVarString::getVarType() { return "string"; } +string CScriptVarString::getParsableString() { return getJSString(data); } + +void CScriptVarString::native_Length(const CFunctionsScopePtr &c, void *data) +{ + c->setReturnVar(newScriptVar((int)this->data.size())); +} + + +////////////////////////////////////////////////////////////////////////// CScriptVarIntegerBase + +CScriptVarIntegerBase::CScriptVarIntegerBase(CTinyJS *Context, int Data) : CScriptVar(Context, Context->numberPrototype), data(Data) {} +CScriptVarIntegerBase::~CScriptVarIntegerBase() {} +int CScriptVarIntegerBase::getInt() {return data; } +bool CScriptVarIntegerBase::getBool() {return data!=0;} +double CScriptVarIntegerBase::getDouble() {return data;} +string CScriptVarIntegerBase::getString() {return int2string(data);} +string CScriptVarIntegerBase::getVarType() { return "number"; } + + +////////////////////////////////////////////////////////////////////////// CScriptVarInteger + +CScriptVarInteger::~CScriptVarInteger() {} +CScriptVarPtr CScriptVarInteger::clone() { return new CScriptVarInteger(*this); } +bool CScriptVarInteger::isNumber() { return true; } +bool CScriptVarInteger::isInt() { return true; } + + +////////////////////////////////////////////////////////////////////////// CScriptVarBool + +CScriptVarBool::~CScriptVarBool() {} +CScriptVarPtr CScriptVarBool::clone() { return new CScriptVarBool(*this); } +bool CScriptVarBool::isBool() { return true; } +string CScriptVarBool::getString() {return data!=0?"true":"false";} +string CScriptVarBool::getVarType() { return "boolean"; } + + +////////////////////////////////////////////////////////////////////////// CScriptVarInfinity + +Infinity InfinityPositive(1); +Infinity InfinityNegative(-1); +CScriptVarInfinity::~CScriptVarInfinity() {} +CScriptVarPtr CScriptVarInfinity::clone() { return new CScriptVarInfinity(*this); } +int CScriptVarInfinity::isInfinity() { return data; } +string CScriptVarInfinity::getString() {return data<0?"-Infinity":"Infinity";} + + +////////////////////////////////////////////////////////////////////////// CScriptVarDouble + +CScriptVarDouble::CScriptVarDouble(CTinyJS *Context, double Data) : CScriptVar(Context, Context->numberPrototype), data(Data) {} +CScriptVarDouble::~CScriptVarDouble() {} +CScriptVarPtr CScriptVarDouble::clone() { return new CScriptVarDouble(*this); } +bool CScriptVarDouble::isNumber() { return true; } +bool CScriptVarDouble::isDouble() { return true; } + +int CScriptVarDouble::getInt() {return (int)data; } +bool CScriptVarDouble::getBool() {return data!=0.0;} +double CScriptVarDouble::getDouble() {return data;} +string CScriptVarDouble::getString() {return float2string(data);} +string CScriptVarDouble::getVarType() { return "number"; } + + +////////////////////////////////////////////////////////////////////////// CScriptVarFunction + +CScriptVarFunction::CScriptVarFunction(CTinyJS *Context, CScriptTokenDataFnc *Data) : CScriptVar(Context, Context->functionPrototype), data(0) { + setFunctionData(Data); +} +CScriptVarFunction::~CScriptVarFunction() { setFunctionData(0); } +CScriptVarPtr CScriptVarFunction::clone() { return new CScriptVarFunction(*this); } +bool CScriptVarFunction::isFunction() { return true; } + +string CScriptVarFunction::getString() {return "[ Function ]";} +string CScriptVarFunction::getVarType() { return "function"; } +string CScriptVarFunction::getParsableBlockString(TOKEN_VECT::iterator &it, TOKEN_VECT::iterator end, const string indentString, const string indent) { + ostringstream destination; + string nl = indent.size() ? "\n" : " "; + string my_indentString = indentString; + bool add_nl=false, block_start=false; + + for(; it != end; ++it) { + + string OutString; + if(add_nl) OutString.append(nl).append(my_indentString); + bool old_block_start = block_start; + add_nl = block_start = false; + if(LEX_TOKEN_DATA_STRING(it->token)) + OutString.append(it->String()).append(" "); + else if(LEX_TOKEN_DATA_FLOAT(it->token)) + OutString.append(float2string(it->Float())).append(" "); + else if(it->token == LEX_INT) + OutString.append(int2string(it->Int())).append(" "); + else if(LEX_TOKEN_DATA_FUNCTION(it->token)) { + OutString.append("function "); + if(it->token == LEX_R_FUNCTION) + OutString.append(data->name); + OutString.append("("); + if(data->parameter.size()) { + OutString.append(data->parameter.front()); + for(vector::iterator it=data->parameter.begin()+1; it!=data->parameter.end(); ++it) + OutString.append(", ").append(*it); + } + OutString.append(") "); + TOKEN_VECT::iterator it=data->body.begin(); + OutString += getParsableBlockString(it, data->body.end(), indentString, indent); + + } else if(it->token == '{') { + OutString.append("{"); + my_indentString.append(indent); + add_nl = block_start = true; + } else if(it->token == '}') { + my_indentString.resize(my_indentString.size() - min(my_indentString.size(),indent.size())); + if(old_block_start) + OutString = "}"; + else + OutString = nl + my_indentString + "}"; + add_nl = true; + } else if(it->token == LEX_T_SKIP) { + // ignore SKIP-Token + } else { + OutString.append(CScriptLex::getTokenStr(it->token)); + if(it->token==';') add_nl=true; else OutString.append(" "); + } + destination << OutString; + } + return destination.str(); +} + +string CScriptVarFunction::getParsableString(const string &indentString, const string &indent) { + ostringstream destination; + string nl = indent.size() ? "\n" : " "; + + destination << "function "<name<<"("; + + // get list of parameters + if(data->parameter.size()) { + destination << data->parameter.front(); + for(vector::iterator it = data->parameter.begin()+1; it != data->parameter.end(); ++it) { + destination << ", " << *it; + } + } + + // add function body + destination << ") "; + + + if(isNative()) { + destination << "{ /* native Code */ }"; + } else { + TOKEN_VECT::iterator it=data->body.begin(); + destination << getParsableBlockString(it, data->body.end(), indentString, indent); + } + return destination.str(); +} +CScriptTokenDataFnc *CScriptVarFunction::getFunctionData() { return data; } + +void CScriptVarFunction::setFunctionData(CScriptTokenDataFnc *Data) { + if(data) { data->unref(); data = 0; } + if(Data) { + data = Data; data->ref(); + addChildNoDup("length", newScriptVar((int)data->parameter.size()), 0); + } +} + + +////////////////////////////////////////////////////////////////////////// CScriptVarFunctionNative + +CScriptVarFunctionNative::~CScriptVarFunctionNative() {} +bool CScriptVarFunctionNative::isNative() { return true; } +string CScriptVarFunctionNative::getString() {return "[ Function Native ]";} + + +////////////////////////////////////////////////////////////////////////// CScriptVarFunctionNativeCallback + +CScriptVarFunctionNativeCallback::~CScriptVarFunctionNativeCallback() {} +CScriptVarPtr CScriptVarFunctionNativeCallback::clone() { return new CScriptVarFunctionNativeCallback(*this); } +void CScriptVarFunctionNativeCallback::callFunction(const CFunctionsScopePtr &c) { jsCallback(c, jsUserData); } + + +////////////////////////////////////////////////////////////////////////// CScriptVarScope + + +declare_dummy_t(Scope); +CScriptVarScope::~CScriptVarScope() {} +CScriptVarPtr CScriptVarScope::clone() { return CScriptVarPtr(); } +bool CScriptVarScope::isObject() { return false; } +CScriptVarPtr CScriptVarScope::scopeVar() { return this; } ///< to create var like: var a = ... +CScriptVarPtr CScriptVarScope::scopeLet() { return this; } ///< to create var like: let a = ... +CScriptVarLink *CScriptVarScope::findInScopes(const string &childName) { + return CScriptVar::findChild(childName); +} +CScriptVarScopePtr CScriptVarScope::getParent() { return CScriptVarScopePtr(); } ///< no Parent + + +////////////////////////////////////////////////////////////////////////// CScriptVarScopeFnc + +declare_dummy_t(ScopeFnc); +CScriptVarScopeFnc::~CScriptVarScopeFnc() {} +CScriptVarLink *CScriptVarScopeFnc::findInScopes(const string &childName) { + CScriptVarLink * ret = findChild(childName); + if( !ret ) { + if(closure) ret = CScriptVarScopePtr(closure)->findInScopes(childName); + else ret = context->getRoot()->findChild(childName); + } + return ret; +} + + +////////////////////////////////////////////////////////////////////////// CScriptVarScopeLet + +declare_dummy_t(ScopeLet); +CScriptVarScopeLet::CScriptVarScopeLet(const CScriptVarScopePtr &Parent) // constructor for LetScope + : CScriptVarScope(Parent->getContext()), parent(addChild(TINYJS_SCOPE_PARENT_VAR, Parent, 0)) {} + +CScriptVarScopeLet::~CScriptVarScopeLet() {} +CScriptVarPtr CScriptVarScopeLet::scopeVar() { // to create var like: var a = ... + return getParent()->scopeVar(); +} +CScriptVarScopePtr CScriptVarScopeLet::getParent() { return parent; } +CScriptVarLink *CScriptVarScopeLet::findInScopes(const string &childName) { + CScriptVarLink *ret = findChild(childName); + if( !ret ) ret = getParent()->findInScopes(childName); + return ret; +} + +////////////////////////////////////////////////////////////////////////// CScriptVarScopeWith + +declare_dummy_t(ScopeWith); +CScriptVarScopeWith::~CScriptVarScopeWith() {} +CScriptVarPtr CScriptVarScopeWith::scopeLet() { // to create var like: let a = ... + return getParent()->scopeLet(); +} +CScriptVarLink *CScriptVarScopeWith::findInScopes(const string &childName) { + CScriptVarLink * ret = (*with)->findChild(childName); + if( !ret ) ret = getParent()->findInScopes(childName); + return ret; +} + +// ----------------------------------------------------------------------------------- CSCRIPT +bool CTinyJS::noexecute = false; +CTinyJS::CTinyJS() { + CScriptVarPtr var; + t = 0; runtimeFlags = 0; - root = (new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT))->ref(); - scopes.push_back(root); + first = 0; + uniqueID = 0; + + + ////////////////////////////////////////////////////////////////////////// + // Object-Prototype + objectPrototype = newScriptVar(Object); + + ////////////////////////////////////////////////////////////////////////// + // Scopes + root = ::newScriptVar(this, Scope); + _scopes.push_back(root); + + + ////////////////////////////////////////////////////////////////////////// // Add built-in classes - stringClass = (new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT))->ref(); - arrayClass = (new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT))->ref(); - objectClass = (new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT))->ref(); - root->addChild("String", stringClass); - root->addChild("Array", arrayClass); - root->addChild("Object", objectClass); - exceptionVar = NULL; - addNative("function eval(jsCode)", this, &CTinyJS::scEval); // execute the given string and return the result + + + ////////////////////////////////////////////////////////////////////////// + // Object + var = addNative("function Object()", this, &CTinyJS::native_Object); + var->addChild(TINYJS_PROTOTYPE_CLASS, objectPrototype); + addNative("function Object.prototype.hasOwnProperty(prop)", this, &CTinyJS::native_Object_hasOwnProperty); // execute the given string and return the result + + ////////////////////////////////////////////////////////////////////////// + // Array + arrayPrototype = newScriptVar(Object); + var = addNative("function Array()", this, &CTinyJS::native_Array); + var->addChild(TINYJS_PROTOTYPE_CLASS, arrayPrototype); + + ////////////////////////////////////////////////////////////////////////// + // String + stringPrototype = newScriptVar(Object); + var = addNative("function String()", this, &CTinyJS::native_String); + var->addChild(TINYJS_PROTOTYPE_CLASS, stringPrototype); + + ////////////////////////////////////////////////////////////////////////// + // Number + numberPrototype = newScriptVar(Object); + var = addNative("function Number()", this, &CTinyJS::native_String); + var->addChild(TINYJS_PROTOTYPE_CLASS, numberPrototype); + + ////////////////////////////////////////////////////////////////////////// + // Function + functionPrototype = newScriptVar(Object); + var = addNative("function Function(params, body)", this, &CTinyJS::native_Function); // execute the given string and return the result + var->addChild(TINYJS_PROTOTYPE_CLASS, functionPrototype); + addNative("function Function.prototype.call(objc)", this, &CTinyJS::native_Function_call); // execute the given string and return the result + addNative("function Function.prototype.apply(objc, args)", this, &CTinyJS::native_Function_apply); // execute the given string and return the result + + ////////////////////////////////////////////////////////////////////////// + addNative("function eval(jsCode)", this, &CTinyJS::native_Eval); // execute the given string and return the result + addNative("function JSON.parse(text, reviver)", this, &CTinyJS::native_JSON_parse); // execute the given string and return the result + } CTinyJS::~CTinyJS() { - ASSERT(!l); - scopes.clear(); - stringClass->unref(0); - arrayClass->unref(0); - objectClass->unref(0); - root->unref(0); - + ASSERT(!t); + root->removeAllChildren(); + ClearLostVars(); +#ifdef _DEBUG + for(CScriptVar *p = first; p; p=p->next) + { + if(objectPrototype != CScriptVarPtr(p) && + arrayPrototype != CScriptVarPtr(p) && + numberPrototype != CScriptVarPtr(p) && + stringPrototype != CScriptVarPtr(p) && + functionPrototype != CScriptVarPtr(p) && + root != CScriptVarPtr(p) + ) + printf("%p\n", p); + } +#endif #if DEBUG_MEMORY show_allocated(); #endif } -void CTinyJS::throwError(bool &execute, const string &message, int pos /*=-1*/) { +void CTinyJS::throwError(bool &execute, const string &message) { + if(execute && (runtimeFlags & RUNTIME_CANTHROW)) { + exceptionVar = newScriptVar(message); + runtimeFlags |= RUNTIME_THROW; + execute = false; + return; + } + throw new CScriptException(message, t->currentFile, t->currentLine(), t->currentColumn()); +} + +void CTinyJS::throwError(bool &execute, const string &message, CScriptTokenizer::ScriptTokenPosition &Pos) { if(execute && (runtimeFlags & RUNTIME_CANTHROW)) { - exceptionVar = (new CScriptVar(message))->ref(); + exceptionVar = newScriptVar(message); runtimeFlags |= RUNTIME_THROW; execute = false; return; } - throw new CScriptException(message, pos); + throw new CScriptException(message, t->currentFile, Pos.currentLine(), Pos.currentColumn()); } void CTinyJS::trace() { root->trace(); } -void CTinyJS::execute(const string &code) { - evaluateComplex(code); +void CTinyJS::execute(CScriptTokenizer &Tokenizer) { + evaluateComplex(Tokenizer); } -CScriptVarLink CTinyJS::evaluateComplex(const string &code) { - CScriptLex *oldLex = l; +void CTinyJS::execute(const char *Code, const string &File, int Line, int Column) { + evaluateComplex(Code, File, Line, Column); +} + +void CTinyJS::execute(const string &Code, const string &File, int Line, int Column) { + evaluateComplex(Code, File, Line, Column); +} - l = new CScriptLex(code); +CScriptVarLink CTinyJS::evaluateComplex(CScriptTokenizer &Tokenizer) { CScriptVarSmartLink v; + t = &Tokenizer; try { bool execute = true; - bool noexecute = false; - SET_RUNTIME_PASS_SINGLE; - if(twoPass) - { - SET_RUNTIME_PASS_TWO_1; - do { - statement(noexecute); - while (l->tk==';') l->match(';'); // skip empty statements - } while (l->tk!=LEX_EOF); - l->reset(); - SET_RUNTIME_PASS_TWO_2; - } do { - v = statement(execute); - while (l->tk==';') l->match(';'); // skip empty statements - } while (l->tk!=LEX_EOF); + v = execute_statement(execute); + while (t->tk==';') t->match(';'); // skip empty statements + } while (t->tk!=LEX_EOF); } catch (CScriptException *e) { runtimeFlags = 0; // clean up runtimeFlags ostringstream msg; - msg << "Error " << e->text << " at " << l->getPosition(e->pos); + msg << "Error " << e->text; + if(e->line >= 0) msg << " at Line:" << e->line+1 << " Column:" << e->column+1; + if(e->file.length()) msg << " in " << e->file; delete e; - delete l; - l = oldLex; - throw new CScriptException(msg.str()); + t=0; + throw new CScriptException(msg.str(),""); + } + t=0; + + ClearLostVars(v); + + int UniqueID = getUniqueID(); + root->setTempraryIDrecursive(UniqueID); + if(v) (*v)->setTempraryIDrecursive(UniqueID); + for(CScriptVar *p = first; p; p=p->next) + { + if(p->temporaryID != UniqueID) + printf("%p\n", p); } - delete l; - l = oldLex; if (v) { CScriptVarLink r = *v; return r; } // return undefined... - return CScriptVarLink(new CScriptVar()); + return CScriptVarLink(newScriptVar(Undefined)); } - -string CTinyJS::evaluate(const string &code) { - return evaluateComplex(code).var->getString(); +CScriptVarLink CTinyJS::evaluateComplex(const char *Code, const string &File, int Line, int Column) { + CScriptTokenizer Tokenizer(Code, File, Line, Column); + return evaluateComplex(Tokenizer); +} +CScriptVarLink CTinyJS::evaluateComplex(const string &Code, const string &File, int Line, int Column) { + CScriptTokenizer Tokenizer(Code.c_str(), File, Line, Column); + return evaluateComplex(Tokenizer); } -void CTinyJS::parseFunctionArguments(CScriptVar *funcVar) { - l->match('('); - CScriptVar *arguments = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT); - funcVar->addChild(TINYJS_ARGUMENTS_VAR, arguments); - int idx = 0; - while (l->tk!=')') { - arguments->addChild(int2string(idx++), new CScriptVar(l->tkStr)); - l->match(LEX_ID); - if (l->tk!=')') l->match(','); - } - l->match(')'); - funcVar->addChild("length", new CScriptVar(idx)); +string CTinyJS::evaluate(CScriptTokenizer &Tokenizer) { + return evaluateComplex(Tokenizer).getVarPtr()->getString(); +} +string CTinyJS::evaluate(const char *Code, const string &File, int Line, int Column) { + return evaluateComplex(Code, File, Line, Column).getVarPtr()->getString(); } -void CTinyJS::addNative(const string &funcDesc, JSCallback ptr, void *userdata) { - CScriptVar *funcVar = addNative(funcDesc); - funcVar->setCallback(ptr, userdata); +string CTinyJS::evaluate(const string &Code, const string &File, int Line, int Column) { + return evaluate(Code.c_str(), File, Line, Column); } -void CTinyJS::addNative(const string &funcDesc, NativeFncBase *ptr, void *userdata) { - CScriptVar *funcVar = addNative(funcDesc); - funcVar->setCallback(ptr, userdata); + +CScriptVarFunctionNativePtr CTinyJS::addNative(const string &funcDesc, JSCallback ptr, void *userdata) { + return addNative(funcDesc, ::newScriptVar(this, ptr, userdata)); } -CScriptVar *CTinyJS::addNative(const string &funcDesc) { - CScriptLex *oldLex = l; - l = new CScriptLex(funcDesc); - CScriptVar *base = root; - l->match(LEX_R_FUNCTION); - string funcName = l->tkStr; - l->match(LEX_ID); +CScriptVarFunctionNativePtr CTinyJS::addNative(const string &funcDesc, CScriptVarFunctionNativePtr Var) { + CScriptLex lex(funcDesc.c_str()); + CScriptVarPtr base = root; + + lex.match(LEX_R_FUNCTION); + string funcName = lex.tkStr; + lex.match(LEX_ID); /* Check for dots, we might want to do something like function String.substring ... */ - while (l->tk == '.') { - l->match('.'); + while (lex.tk == '.') { + lex.match('.'); CScriptVarLink *link = base->findChild(funcName); // if it doesn't exist, make an object class - if (!link) link = base->addChild(funcName, new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT)); - base = link->var; - funcName = l->tkStr; - l->match(LEX_ID); + if (!link) link = base->addChild(funcName, newScriptVar(Object)); + base = link->getVarPtr(); + funcName = lex.tkStr; + lex.match(LEX_ID); } - CScriptVar *funcVar = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_FUNCTION); - parseFunctionArguments(funcVar); - delete l; - l = oldLex; + auto_ptr pFunctionData(new CScriptTokenDataFnc); + lex.match('('); + while (lex.tk!=')') { + pFunctionData->parameter.push_back(lex.tkStr); + lex.match(LEX_ID); + if (lex.tk!=')') lex.match(','); + } + lex.match(')'); + Var->setFunctionData(pFunctionData.release()); + base->addChild(funcName, Var); + return Var; - base->addChild(funcName, funcVar); - return funcVar; } +CScriptVarSmartLink CTinyJS::parseFunctionDefinition(CScriptToken &FncToken) { + CScriptTokenDataFnc &Fnc = FncToken.Fnc(); + string fncName = (FncToken.token == LEX_T_FUNCTION_OPERATOR) ? TINYJS_TEMP_NAME : Fnc.name; + CScriptVarSmartLink funcVar = new CScriptVarLink(newScriptVar(&Fnc), fncName); + if(scope() != root) + funcVar->getVarPtr()->addChild(TINYJS_FUNCTION_CLOSURE_VAR, scope(), 0); + return funcVar; +} +/* CScriptVarSmartLink CTinyJS::parseFunctionDefinition() { - // actually parse a function... - int oldFuncOffset = funcOffset; - funcOffset = l->getDataPos(); - l->match(LEX_R_FUNCTION); - string funcName = TINYJS_TEMP_NAME; - /* we can have functions without names */ - if (l->tk==LEX_ID) { - funcName = l->tkStr; - l->match(LEX_ID); - } - funcOffset -= l->getDataPos(); - CScriptVarSmartLink funcVar = new CScriptVarLink(new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_FUNCTION), funcName); - parseFunctionArguments(funcVar->var); - funcOffset += l->getDataPos(); - bool noexecute = false; - if(IS_RUNTIME_PASS_TWO_2) { - SAVE_RUNTIME_PASS; - SET_RUNTIME_PASS_SINGLE; - block(noexecute); - RESTORE_RUNTIME_PASS; - } else if(IS_RUNTIME_PASS_TWO_1) { - int funcBegin = l->tokenStart; - CScriptVar *locale = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT); - CScriptVarLink *anonymous_link = locale->addChild(TINYJS_ANONYMOUS_VAR, new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT)); - CScriptVarLink *locale_link = funcVar->var->addChild(TINYJS_LOKALE_VAR, locale); - locale_link->hidden = locale_link->dontEnumerable = true; - scopes.push_back(locale); - try { - block(noexecute); - } catch(CScriptException *e) { - scopes.pop_back(); - throw e; - } - scopes.pop_back(); - funcVar->var->data = l->getSubString(funcBegin); - if(anonymous_link->var->Childs.size() == 0) - locale->removeLink(anonymous_link); - } else { - int funcBegin = l->tokenStart; - block(noexecute); - funcVar->var->data = l->getSubString(funcBegin); - } - funcOffset = oldFuncOffset; - + CScriptTokenDataFnc &Fnc = t->getToken().Fnc(); + string fncName = (t->tk == LEX_T_FUNCTION_FORCE_ANONYMOUS) ? TINYJS_TEMP_NAME : Fnc.name; + CScriptVarSmartLink funcVar = new CScriptVarLink(new CScriptVar(Fnc), fncName); + (*funcVar)->addChild("length", new CScriptVar((int)Fnc.parameter.size())); return funcVar; } +*/ + +CScriptVarSmartLink CTinyJS::parseFunctionsBodyFromString(const string &Parameter, const string &FncBody) { + string Fnc = "function ("+Parameter+"){"+FncBody+"}"; + CScriptTokenizer tokenizer(Fnc.c_str()); + return parseFunctionDefinition(tokenizer.getToken()); +} class CScriptEvalException { public: @@ -1850,571 +2914,619 @@ class CScriptEvalException { CScriptEvalException(int RuntimeFlags) : runtimeFlags(RuntimeFlags){} }; -// Precedence 2, Precedence 1 & literals -CScriptVarSmartLink CTinyJS::factor(bool &execute) { - if (l->tk==LEX_R_TRUE) { - l->match(LEX_R_TRUE); - return new CScriptVarLink(new CScriptVar(true)); - } - if (l->tk==LEX_R_FALSE) { - l->match(LEX_R_FALSE); - return new CScriptVarLink(new CScriptVar(false)); - } - if (l->tk==LEX_R_NULL) { - l->match(LEX_R_NULL); - return new CScriptVarLink(new CScriptVar(TINYJS_BLANK_DATA,SCRIPTVAR_NULL)); - } - if (l->tk==LEX_R_UNDEFINED) { - l->match(LEX_R_UNDEFINED); - return new CScriptVarLink(new CScriptVar(TINYJS_BLANK_DATA,SCRIPTVAR_UNDEFINED)); - } - if (l->tk==LEX_R_INFINITY) { - l->match(LEX_R_INFINITY); - return new CScriptVarLink(new CScriptVar(TINYJS_BLANK_DATA,SCRIPTVAR_INFINITY)); - } - if (l->tk==LEX_R_NAN) { - l->match(LEX_R_NAN); - return new CScriptVarLink(new CScriptVar(TINYJS_BLANK_DATA,SCRIPTVAR_NAN)); - } - if (l->tk==LEX_ID || l->tk=='(') { - CScriptVarSmartLink a; - int op = l->tk; - if(l->tk=='(') - { - l->match('('); - a = base(execute); - l->match(')'); +CScriptVarPtr CTinyJS::callFunction(const CScriptVarPtr &Function, std::vector &Arguments, const CScriptVarPtr& This, bool &execute) { +//CScriptVarSmartLink CTinyJS::callFunction(CScriptVarSmartLink &Function, vector &Arguments, const CScriptVarPtr& This, bool &execute) { + ASSERT(Function->isFunction()); + + CScriptVarScopeFncPtr functionRoot(::newScriptVar(this, ScopeFnc, Function->findChild(TINYJS_FUNCTION_CLOSURE_VAR))); + functionRoot->addChild("this", This); + CScriptVarSmartLink arguments = functionRoot->addChild(TINYJS_ARGUMENTS_VAR, newScriptVar(Object)); + CScriptTokenDataFnc *Fnc = Function->getFunctionData(); + if(Fnc->name.size()) functionRoot->addChild(Fnc->name, Function); + + int length_proto = Fnc->parameter.size(); + int length_arguments = Arguments.size(); + int length = max(length_proto, length_arguments); + for(int arguments_idx = 0; arguments_idxaddChild(arguments_idx_str, Arguments[arguments_idx]); + } else { + value = newScriptVar(Undefined); } - else - a = execute ? findInScopes(l->tkStr) : new CScriptVarLink(new CScriptVar()); - /* The parent if we're executing a method call */ - CScriptVar *parent = 0; - - if (execute && !a) { - /* Variable doesn't exist! JavaScript says we should create it - * (we won't add it here. This is done in the assignment operator)*/ - if(l->tkStr == "this") { - a = new CScriptVarLink(this->root, l->tkStr); - parent = this->root; - } else - a = new CScriptVarLink(new CScriptVar(), l->tkStr); + if(arguments_idx < length_proto) { + functionRoot->addChildNoDup(Fnc->parameter[arguments_idx], value); } - string path = a->name; - if(op==LEX_ID) - l->match(LEX_ID); - while (l->tk=='(' || l->tk=='.' || l->tk=='[') { - if(execute && a->var->isUndefined()) { - throwError(execute, path + " is undefined", l->tokenEnd); + } + (*arguments)->addChild("length", newScriptVar(length_arguments)); + CScriptVarSmartLink returnVar; + + int old_function_runtimeFlags = runtimeFlags; // save runtimeFlags + runtimeFlags &= ~RUNTIME_LOOP_MASK; // clear LOOP-Flags + // execute function! + // add the function's execute space to the symbol table so we can recurse + // scopes.push_back(SCOPES::SCOPE_TYPE_FUNCTION, functionRoot->get()); + CScopeControl ScopeControl(this); + ScopeControl.addFncScope(functionRoot); + if (Function->isNative()) { + try { + CScriptVarFunctionNativePtr(Function)->callFunction(functionRoot); +// ((CScriptVarFunctionNative*)*Function.getVar())->callFunction(functionRoot.getVar()); + runtimeFlags = old_function_runtimeFlags | (runtimeFlags & RUNTIME_THROW); // restore runtimeFlags + if(runtimeFlags & RUNTIME_THROW) { + execute = false; } - if (l->tk=='(') { // ------------------------------------- Function Call - if (execute) { - if (!a->var->isFunction()) { - string errorMsg = "Expecting '"; - errorMsg = errorMsg + a->name + "' to be a function"; - throw new CScriptException(errorMsg.c_str()); - } - l->match('('); path += '('; - // create a new symbol table entry for execution of this function - CScriptVarSmartLink functionRoot(new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_FUNCTION)); - if (parent) - functionRoot->var->addChildNoDup("this", parent); -// else -// functionRoot->var->addChildNoDup("this", this->root);//new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT)); // always add a this-Object -// functionRoot->var->addChildNoDup("this", new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT)); // always add a this-Object - // insert locale - CScriptVarLink *__locale__ = a->var->findChild(TINYJS_LOKALE_VAR); - if(__locale__) { - for(SCRIPTVAR_CHILDS::iterator it = __locale__->var->Childs.begin(); it != __locale__->var->Childs.end(); ++it) { - functionRoot->var->addChild(it->first , it->second->var->deepCopy()); - } - } - // grab in all parameters - CScriptVarLink *arguments_proto = a->var->findChild("arguments"); - int length_proto = arguments_proto ? arguments_proto->var->getChildren() : 0; - CScriptVar *arguments = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT); - int arguments_idx = 0; - for(; l->tk!=')' || arguments_idxtk!=')') { - try { - value = assignment(execute); - path += value->var->getString(); - } catch (CScriptException *e) { - delete arguments; - throw e; - } + } catch (CScriptEvalException *e) { + int exeption_runtimeFlags = e->runtimeFlags; + delete e; + runtimeFlags = old_function_runtimeFlags; // restore runtimeFlags + if(exeption_runtimeFlags & RUNTIME_BREAK) { + delete e; + if(runtimeFlags & RUNTIME_CANBREAK) { + runtimeFlags |= RUNTIME_BREAK; + execute = false; + } + else + throw new CScriptException("'break' must be inside loop or switch"); + } else if(exeption_runtimeFlags & RUNTIME_CONTINUE) { + if(runtimeFlags & RUNTIME_CANCONTINUE) { + runtimeFlags |= RUNTIME_CONTINUE; + execute = false; + } + else + throw new CScriptException("'continue' must be inside loop"); + } + } catch (CScriptVarPtr v) { + if(runtimeFlags & RUNTIME_CANTHROW) { + runtimeFlags |= RUNTIME_THROW; + execute = false; + exceptionVar = v; + } + else { + string e = "uncaught exception: '"+v->getString()+"' in: "+Function->getFunctionData()->name+"()"; + throw new CScriptException(e); + } + } + } else { + /* we just want to execute the block, but something could + * have messed up and left us with the wrong ScriptLex, so + * we want to be careful here... */ + string oldFile = t->currentFile; + t->currentFile = Fnc->file; + t->pushTokenScope(Fnc->body); + SET_RUNTIME_CANRETURN; + execute_block(execute); + t->currentFile = oldFile; + + // because return will probably have called this, and set execute to false + runtimeFlags = old_function_runtimeFlags | (runtimeFlags & RUNTIME_THROW); // restore runtimeFlags + if(!(runtimeFlags & RUNTIME_THROW)) { + execute = true; + } + } + /* get the real return var before we remove it from our function */ + if(returnVar = functionRoot->findChild(TINYJS_RETURN_VAR)) + return returnVar->getVarPtr(); + else + return newScriptVar(Undefined); +} - } else - value = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_UNDEFINED); - if (execute) { - string arguments_idx_str; int2string(arguments_idx, arguments_idx_str); - if (value->var->isBasic()) { - // pass by value - value = arguments->addChild(arguments_idx_str , value->var->deepCopy()); - } else { - // pass by reference - value = arguments->addChild(arguments_idx_str, value->var); - } - CScriptVarLink *argument_name; - if(arguments_proto && (argument_name = arguments_proto->var->findChild(arguments_idx_str))) - functionRoot->var->addChildNoDup(argument_name->var->getString(), value->var); - } - if (l->tk!=')') { l->match(','); path+=','; } - } - l->match(')'); path+=')'; - arguments->addChild("length", new CScriptVar(arguments_idx)); - functionRoot->var->addChild("arguments", arguments); - // setup a return variable - CScriptVarSmartLink returnVar; - if(execute) { - int old_function_runtimeFlags = runtimeFlags; // save runtimeFlags - runtimeFlags &= ~RUNTIME_LOOP_MASK; // clear LOOP-Flags - // execute function! - // add the function's execute space to the symbol table so we can recurse -// CScriptVarLink *returnVarLink = functionRoot->addChild(TINYJS_RETURN_VAR); - scopes.push_back(functionRoot->var); - try { - if (a->var->isNative()) { - ASSERT(a->var->jsCallback); - try { - if (a->var->isNative_ClassMemberFnc()) - (*a->var->jsCallbackClass)(functionRoot->var, a->var->jsCallbackUserData); - else - a->var->jsCallback(functionRoot->var, a->var->jsCallbackUserData); - // special thinks for eval() // set execute to false when runtimeFlags setted - runtimeFlags = old_function_runtimeFlags | (runtimeFlags & RUNTIME_THROW); // restore runtimeFlags - if(runtimeFlags & RUNTIME_THROW) { - execute = false; - } - } catch (CScriptEvalException *e) { - runtimeFlags = old_function_runtimeFlags; // restore runtimeFlags - if(e->runtimeFlags & RUNTIME_BREAK) { - if(runtimeFlags & RUNTIME_CANBREAK) { - runtimeFlags |= RUNTIME_BREAK; - execute = false; - } - else - throw new CScriptException("'break' must be inside loop or switch"); - } else if(e->runtimeFlags & RUNTIME_CONTINUE) { - if(runtimeFlags & RUNTIME_CANCONTINUE) { - runtimeFlags |= RUNTIME_CONTINUE; - execute = false; - } - else - throw new CScriptException("'continue' must be inside loop"); - } - } catch (CScriptVarLink *v) { - CScriptVarSmartLink e = v; - if(runtimeFlags & RUNTIME_CANTHROW) { - runtimeFlags |= RUNTIME_THROW; - execute = false; - this->exceptionVar = e->var->ref(); - } - else - throw new CScriptException("uncaught exception: '"+e->var->getString()+"' in: "+a->name+"()"); - } - } else { - /* we just want to execute the block, but something could - * have messed up and left us with the wrong ScriptLex, so - * we want to be careful here... */ - CScriptLex *oldLex = l; - CScriptLex *newLex = new CScriptLex(a->var->getString()); - l = newLex; - try { - SET_RUNTIME_CANRETURN; - SAVE_RUNTIME_PASS; - if(__locale__) - SET_RUNTIME_PASS_TWO_2; - block(execute); - RESTORE_RUNTIME_PASS; - // because return will probably have called this, and set execute to false - runtimeFlags = old_function_runtimeFlags | (runtimeFlags & RUNTIME_THROW); // restore runtimeFlags - if(!(runtimeFlags & RUNTIME_THROW)) { - execute = true; - } - } catch (CScriptException *e) { - delete newLex; - l = oldLex; - throw e; - } - delete newLex; - l = oldLex; - } - } catch (CScriptException *e) { - scopes.pop_back(); - throw e; - } +/* +CScriptVarSmartLink CTinyJS::setValue( CScriptVarSmartLink Var ) +{ + if((*Var)->isAccessor()) { - scopes.pop_back(); - /* get the real return var before we remove it from our function */ - if(returnVar = functionRoot->var->findChild(TINYJS_RETURN_VAR)) - returnVar = new CScriptVarLink(returnVar->var); -// returnVar = functionRoot->getReturnVar(); -// functionRoot->removeLink(returnVarLink); - } - if (returnVar) - a = returnVar; - else - a = new CScriptVarLink( - new CScriptVar()); + } +} + +CScriptVarSmartLink CTinyJS::getValue( CScriptVarSmartLink Var, bool execute ) +{ + if((*Var)->isAccessor()) { + CScriptVarLink *get = (*Var)->findChild("__accessor_get__"); + if(get) { + } else + return CScriptVarSmartLink(new CScriptVar()); + } else + return Var; +} +*/ +CScriptVarSmartLink CTinyJS::execute_literals(bool &execute) { + if(t->tk == LEX_ID) { + CScriptVarSmartLink a; + if(execute) { + a = findInScopes(t->tkStr()); + if (!a) { + /* Variable doesn't exist! JavaScript says we should create it + * (we won't add it here. This is done in the assignment operator)*/ + if(t->tkStr() == "this") { + a = new CScriptVarLink(root); // fake this } else { - // function, but not executing - just parse args and be done - l->match('('); - while (l->tk != ')') { - CScriptVarSmartLink value = base(execute); - if (l->tk!=')') l->match(','); + a = new CScriptVarLink(newScriptVar(Undefined), t->tkStr()); + } + } + t->match(t->tk); + return a; + } + t->match(t->tk); + } else if (t->tk==LEX_INT) { + CScriptVarPtr a = newScriptVar(t->getToken().Int()); + t->match(t->tk); + return new CScriptVarLink(a); + } else if (t->tk==LEX_FLOAT) { + CScriptVarPtr a = newScriptVar(t->getToken().Float()); + t->match(t->tk); + return new CScriptVarLink(a); + } else if (t->tk==LEX_STR) { + CScriptVarPtr a = newScriptVar(t->getToken().String()); + t->match(LEX_STR); + return new CScriptVarLink(a); + } else if (t->tk=='{') { + if(execute) { + CScriptVarPtr contents(newScriptVar(Object)); + /* JSON-style object definition */ + t->match('{'); + while (t->tk != '}') { + // we only allow strings or IDs on the left hand side of an initialisation + if (t->tk==LEX_STR || t->tk==LEX_ID) { + string id = t->tkStr(); + t->match(t->tk); + t->match(':'); + CScriptVarSmartLink a = execute_assignment(execute); + if (execute) { + contents->addChildNoDup(id, a->getValue()); + } + } else if(t->tk==LEX_T_GET || t->tk==LEX_T_SET) { + CScriptTokenDataFnc &Fnc = t->getToken().Fnc(); + if((t->tk == LEX_T_GET && Fnc.parameter.size()==0) || (t->tk == LEX_T_SET && Fnc.parameter.size()==1)) { + CScriptVarSmartLink funcVar = parseFunctionDefinition(t->getToken()); + CScriptVarLink *child = contents->findChild(Fnc.name); + if(child && !(*child)->isAccessor()) child = 0; + if(!child) child = contents->addChildNoDup(Fnc.name, newScriptVar(Accessor)); + child->getVarPtr()->addChildNoDup((t->tk==LEX_T_GET?TINYJS_ACCESSOR_GET_VAR:TINYJS_ACCESSOR_SET_VAR), funcVar->getVarPtr()); } - l->match(')'); -// if (l->tk == '{') { -// block(execute, SINGLE_PASS); -// } - } - } else if (l->tk == '.' || l->tk == '[') { // ------------------------------------- Record Access - string name; - if(l->tk == '.') { - l->match('.'); - name = l->tkStr; - path += "."+name; - l->match(LEX_ID); - } else { - l->match('['); - CScriptVarSmartLink index = expression(execute); - name = index->var->getString(); - path += "["+name+"]"; - l->match(']'); + t->match(t->tk); } + else + t->match(LEX_ID); + // no need to clean here, as it will definitely be used + if (t->tk != ',') break; + t->match(','); + } + t->match('}'); + return new CScriptVarLink(contents); + } else + t->skip(t->getToken().Int()); + } else if (t->tk=='[') { + if(execute) { + CScriptVarPtr contents(newScriptVar(Array)); + /* JSON-style array */ + t->match('['); + int idx = 0; + while (t->tk != ']') { + CScriptVarSmartLink a = execute_assignment(execute); if (execute) { - CScriptVarLink *child = a->var->findChild(name); - if (!child) child = findInParentClasses(a->var, name); - if (!child) { - /* if we haven't found this defined yet, use the built-in - 'length' properly */ - if (a->var->isArray() && name == "length") { - int l = a->var->getArrayLength(); - child = new CScriptVarLink(new CScriptVar(l)); - } else if (a->var->isString() && name == "length") { - int l = a->var->getString().size(); - child = new CScriptVarLink(new CScriptVar(l)); - } else { - //child = a->var->addChild(name); + contents->addChild(int2string(idx), a->getValue()); + } + // no need to clean here, as it will definitely be used + if (t->tk != ']') t->match(','); + idx++; + } + t->match(']'); + // return new CScriptVarLink(contents.release()); + return new CScriptVarLink(contents); + } else + t->skip(t->getToken().Int()); + } else if (t->tk==LEX_R_LET) { // let as expression + if(execute) { + t->match(LEX_R_LET); + t->match('('); + CScopeControl ScopeControl(this); + ScopeControl.addLetScope(); + for(;;) { + CScriptVarSmartLink a; + string var = t->tkStr(); + t->match(LEX_ID); + a = scope()->scopeLet()->findChildOrCreate(var); + a->SetDeletable(false); + // sort out initialiser + if (t->tk == '=') { + t->match('='); + CScriptVarSmartLink var = execute_assignment(execute); + a << var; + } + if (t->tk == ',') + t->match(','); + else + break; + } + t->match(')'); + return new CScriptVarLink(execute_base(execute)); + } else { + t->skip(t->getToken().Int()); + execute_base(execute); + } + } else if (t->tk==LEX_R_FUNCTION) { + ASSERT(0); // no function as operator + } else if (t->tk==LEX_T_FUNCTION_OPERATOR) { + if(execute) { + CScriptVarSmartLink a = parseFunctionDefinition(t->getToken()); + t->match(t->tk); + return a; + } + t->match(t->tk); + } else if (t->tk==LEX_R_NEW) { + // new -> create a new object + t->match(LEX_R_NEW); + CScriptVarSmartLink parent = execute_literals(execute); + CScriptVarSmartLink objClass = execute_member(parent, execute); + if (execute) { + if((*objClass)->isFunction()) { + CScriptVarPtr obj(newScriptVar(Object)); + CScriptVarLink *prototype = (*objClass)->findChild(TINYJS_PROTOTYPE_CLASS); + if(prototype) obj->addChildNoDup(TINYJS___PROTO___VAR, prototype); + vector arguments; + if (t->tk == '(') { + t->match('('); + while(t->tk!=')') { + CScriptVarPtr value = execute_assignment(execute); + if (execute) { + arguments.push_back(value); } + if (t->tk!=')') { t->match(','); } } - if(child) { - parent = a->var; - a = child; - } else { - CScriptVar *owner = a->var; - a = new CScriptVarLink(new CScriptVar(), name); - a->owner = owner; - } + t->match(')'); } - } else ASSERT(0); - } - return a; - } - if (l->tk==LEX_INT || l->tk==LEX_FLOAT) { - CScriptVar *a = new CScriptVar(l->tkStr, - ((l->tk==LEX_INT)?SCRIPTVAR_INTEGER:SCRIPTVAR_DOUBLE)); - l->match(l->tk); - return new CScriptVarLink(a); - } - if (l->tk==LEX_STR) { - CScriptVar *a = new CScriptVar(l->tkStr, SCRIPTVAR_STRING); - l->match(LEX_STR); - return new CScriptVarLink(a); - } - if (l->tk=='{') { - CScriptVar *contents = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT); - /* JSON-style object definition */ - l->match('{'); - while (l->tk != '}') { - string id = l->tkStr; - // we only allow strings or IDs on the left hand side of an initialisation - if (l->tk==LEX_STR) l->match(LEX_STR); - else l->match(LEX_ID); - l->match(':'); - CScriptVarSmartLink a = assignment(execute); - if (execute) { - contents->addChild(id, a->var); + if(execute) { + CScriptVarPtr returnVar = callFunction(objClass->getVarPtr(), arguments, obj, execute); + if(returnVar->isArray() || returnVar->isObject()) + return CScriptVarSmartLink(returnVar); + return CScriptVarSmartLink(obj); + } + } else { + throwError(execute, objClass->name + " is not a constructor"); + } + } else { + if (t->tk == '(') { + t->match('('); + while(t->tk != ')') execute_base(execute); + t->match(')'); } - // no need to clean here, as it will definitely be used - if (l->tk != '}') l->match(','); } + } else if (t->tk==LEX_R_TRUE) { + t->match(LEX_R_TRUE); + return new CScriptVarLink(newScriptVar(true)); + } else if (t->tk==LEX_R_FALSE) { + t->match(LEX_R_FALSE); + return new CScriptVarLink(newScriptVar(false)); + } else if (t->tk==LEX_R_NULL) { + t->match(LEX_R_NULL); + return new CScriptVarLink(newScriptVar(Null)); + } else if (t->tk==LEX_R_UNDEFINED) { + t->match(LEX_R_UNDEFINED); + return new CScriptVarLink(newScriptVar(Undefined)); + } else if (t->tk==LEX_R_INFINITY) { + t->match(LEX_R_INFINITY); + return new CScriptVarLink(newScriptVar(InfinityPositive)); + } else if (t->tk==LEX_R_NAN) { + t->match(LEX_R_NAN); + return new CScriptVarLink(newScriptVar(NaN)); + } else if (t->tk=='(') { + t->match('('); + CScriptVarSmartLink a = execute_base(execute); + t->match(')'); + return a; + } else + t->match(LEX_EOF); + return new CScriptVarLink(newScriptVar(Undefined)); - l->match('}'); - return new CScriptVarLink(contents); - } - if (l->tk=='[') { - CScriptVar *contents = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_ARRAY); - /* JSON-style array */ - l->match('['); - int idx = 0; - while (l->tk != ']') { - CScriptVarSmartLink a = assignment(execute); +} +CScriptVarSmartLink CTinyJS::execute_member(CScriptVarSmartLink &parent, bool &execute) { + CScriptVarSmartLink a = parent; + if(t->tk == '.' || t->tk == '[') { +// string path = a->name; +// CScriptVar *parent = 0; + while(t->tk == '.' || t->tk == '[') { + + if(execute && ((*a)->isUndefined() || (*a)->isNull())) { + throwError(execute, a->name + " is " + (*a)->getString()); + } + string name; + if(t->tk == '.') { + t->match('.'); + name = t->tkStr(); +// path += "."+name; + t->match(LEX_ID); + } else { + t->match('['); + CScriptVarSmartLink index = execute_expression(execute); + name = (*index)->getString(); +// path += "["+name+"]"; + t->match(']'); + } if (execute) { - contents->addChild(int2string(idx), a->var); + bool first_in_prototype = false; + CScriptVarLink *child = (*a)->findChild(name); + if ( !child && (child = findInPrototypeChain(a, name)) ) + first_in_prototype = true; + if (!child) { + /* if we haven't found this defined yet, use the built-in + 'length' properly */ + if ((*a)->isArray() && name == "length") { + int l = (*a)->getArrayLength(); + child = new CScriptVarLink(newScriptVar(l)); + } else if ((*a)->isString() && name == "length") { + int l = (*a)->getString().size(); + child = new CScriptVarLink(newScriptVar(l)); + } else { + //child = (*a)->addChild(name); + } + } + if(child) { + parent = a; + if(first_in_prototype) { + a = new CScriptVarLink(child->getVarPtr(), child->name); + a->owner = parent->operator->(); // fake owner - but not set Owned -> for assignment stuff + } else + a = child; + } else { + CScriptVar *owner = a->operator->(); + a = new CScriptVarLink(newScriptVar(Undefined), name); + a->owner = owner; // fake owner - but not set Owned -> for assignment stuff + } } - // no need to clean here, as it will definitely be used - if (l->tk != ']') l->match(','); - idx++; } - l->match(']'); - return new CScriptVarLink(contents); } - if (l->tk==LEX_R_FUNCTION) { - CScriptVarLink *anonymous = scopes.back()->findChild(TINYJS_ANONYMOUS_VAR); - string anonymous_name = int2string(l->getDataPos()-this->funcOffset); - CScriptVarSmartLink funcVar = parseFunctionDefinition(); - this->runtimeFlags; -// funcName = anonymous ? int2string(l->tokenStart) : l->tkStr; + return a; +} - funcVar->name = TINYJS_TEMP_NAME; // always anonymous - if(IS_RUNTIME_PASS_TWO_1) - { - CScriptVarLink *anonymous = scopes.back()->findChildOrCreate(TINYJS_ANONYMOUS_VAR, SCRIPTVAR_OBJECT); - anonymous->var->addChild(anonymous_name, funcVar->var); - } - if(IS_RUNTIME_PASS_TWO_2) - { - CScriptVarLink *anonymous = scopes.back()->findChild(TINYJS_ANONYMOUS_VAR); - if(anonymous) - funcVar->replaceWith(anonymous->var->findChild(anonymous_name)); - } - return funcVar; - } - if (l->tk==LEX_R_NEW) { - // new -> create a new object - l->match(LEX_R_NEW); - const string &className = l->tkStr; +CScriptVarSmartLink CTinyJS::execute_function_call(bool &execute) { + CScriptVarSmartLink parent = execute_literals(execute); + CScriptVarSmartLink a = execute_member(parent, execute); + while (t->tk == '(') { if (execute) { - CScriptVarLink *objClass = findInScopes(className); - if (!objClass) { - TRACE("%s is not a valid class name", className.c_str()); - return new CScriptVarLink(new CScriptVar()); - } - l->match(LEX_ID); - CScriptVar *obj = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT); - obj->addChild(TINYJS_PROTOTYPE_CLASS, objClass->var); - if (l->tk == '(') { - l->match('('); - l->match(')'); + if (!a->getValue()->isFunction()) { + string errorMsg = "Expecting '"; + errorMsg = errorMsg + a->name + "' to be a function"; + throw new CScriptException(errorMsg.c_str()); + } + t->match('('); // path += '('; + + // grab in all parameters + vector arguments; + while(t->tk!=')') { + CScriptVarSmartLink value = execute_assignment(execute); +// path += (*value)->getString(); + if (execute) { + arguments.push_back(value->getValue()); + } + if (t->tk!=')') { t->match(','); /*path+=',';*/ } + } + t->match(')'); //path+=')'; + // setup a return variable + CScriptVarSmartLink returnVar; + if(execute) { + // if no parent use the root-scope + CScriptVarPtr This(parent ? parent->getVarPtr() : (CScriptVarPtr )root); + a = callFunction(a->getValue(), arguments, This, execute); } - // TODO: Object constructors - return new CScriptVarLink(obj); } else { - l->match(LEX_ID); - if (l->tk == '(') { - l->match('('); - while(l->tk != ')') l->match(l->tk); - l->match(')'); + // function, but not executing - just parse args and be done + t->match('('); + while (t->tk != ')') { + CScriptVarSmartLink value = execute_base(execute); + // if (t->tk!=')') t->match(','); } + t->match(')'); } + a = execute_member(a, execute); } - // Nothing we can do here... just hope it's the end... - l->match(LEX_EOF); - return 0; + return a; } -// R-L: Precedence 4 (TODO: add typeof, void, delete) & Precedence 3 -CScriptVarSmartLink CTinyJS::unary(bool &execute) { +// R->L: Precedence 3 (in-/decrement) ++ -- +// R<-L: Precedence 4 (unary) ! ~ + - typeof void delete +CScriptVarSmartLink CTinyJS::execute_unary(bool &execute) { CScriptVarSmartLink a; - if (l->tk=='-') { - l->match('-'); - a = unary(execute); + if (t->tk=='-') { + t->match('-'); + a = execute_unary(execute); if (execute) { CheckRightHandVar(execute, a); - CScriptVar zero(0); - a = zero.mathsOp(a->var, '-'); + CScriptVarPtr zero = newScriptVar(0); + a = zero->mathsOp(a->getValue(), '-'); } - } else if (l->tk=='+') { - l->match('+'); - a = unary(execute); + } else if (t->tk=='+') { + t->match('+'); + a = execute_unary(execute); CheckRightHandVar(execute, a); - } else if (l->tk=='!') { - l->match('!'); // binary not - a = unary(execute); + } else if (t->tk=='!') { + t->match('!'); // binary not + a = execute_unary(execute); if (execute) { CheckRightHandVar(execute, a); - CScriptVar zero(0); - a = a->var->mathsOp(&zero, LEX_EQUAL); + CScriptVarPtr zero = newScriptVar(0); + a = a->getValue()->mathsOp(zero, LEX_EQUAL); } - } else if (l->tk=='~') { - l->match('~'); // binary neg - a = unary(execute); + } else if (t->tk=='~') { + t->match('~'); // binary neg + a = execute_unary(execute); if (execute) { CheckRightHandVar(execute, a); - a = a->var->mathsOp(NULL, '~'); + CScriptVarPtr zero = newScriptVar(0); + a = a->getValue()->mathsOp(zero, '~'); } - } else if (l->tk==LEX_R_TYPEOF) { - l->match(LEX_R_TYPEOF); // void - a = unary(execute); + } else if (t->tk==LEX_R_TYPEOF) { + t->match(LEX_R_TYPEOF); // void + a = execute_unary(execute); if (execute) { // !!! no right-hand-check by delete - a = new CScriptVarLink(new CScriptVar(a->var->getVarType(),SCRIPTVAR_STRING)); + a = new CScriptVarLink(newScriptVar(a->getValue()->getVarType())); } - } else if (l->tk==LEX_R_VOID) { - l->match(LEX_R_VOID); // void - a = unary(execute); + } else if (t->tk==LEX_R_VOID) { + t->match(LEX_R_VOID); // void + a = execute_unary(execute); if (execute) { CheckRightHandVar(execute, a); - a = new CScriptVarLink(new CScriptVar(TINYJS_BLANK_DATA,SCRIPTVAR_UNDEFINED)); + a = new CScriptVarLink(newScriptVar(Undefined)); } - } else if (l->tk==LEX_R_DELETE) { - l->match(LEX_R_DELETE); // delete - a = unary(execute); + } else if (t->tk==LEX_R_DELETE) { + t->match(LEX_R_DELETE); // delete + a = execute_unary(execute); if (execute) { // !!! no right-hand-check by delete - if(a->owned && !a->dontDelete) { + if(a->IsOwned() && a->IsDeletable()) { a->owner->removeLink(a.getLink()); // removes the link from owner - a = new CScriptVarLink(new CScriptVar(true)); + a = new CScriptVarLink(newScriptVar(true)); } else - a = new CScriptVarLink(new CScriptVar(false)); + a = new CScriptVarLink(newScriptVar(false)); } - } else if (l->tk==LEX_PLUSPLUS || l->tk==LEX_MINUSMINUS) { - int op = l->tk; - l->match(l->tk); // pre increment/decrement - a = factor(execute); + } else if (t->tk==LEX_PLUSPLUS || t->tk==LEX_MINUSMINUS) { + int op = t->tk; + t->match(t->tk); // pre increment/decrement + a = execute_function_call(execute); if (execute) { - if(a->owned) + if(a->IsOwned() && a->IsWritable()) { - CScriptVar one(1); - CScriptVar *res = a->var->mathsOp(&one, op==LEX_PLUSPLUS ? '+' : '-'); + CScriptVarPtr one = newScriptVar(1); + CScriptVarPtr res = a->getValue()->mathsOp(one, op==LEX_PLUSPLUS ? '+' : '-'); // in-place add/subtract a->replaceWith(res); a = res; } } } else { - a = factor(execute); + a = execute_function_call(execute); } // post increment/decrement - if (l->tk==LEX_PLUSPLUS || l->tk==LEX_MINUSMINUS) { - int op = l->tk; - l->match(l->tk); + if (t->tk==LEX_PLUSPLUS || t->tk==LEX_MINUSMINUS) { + int op = t->tk; + t->match(t->tk); if (execute) { - if(a->owned) + if(a->IsOwned() && a->IsWritable()) { // TRACE("post-increment of %s and a is %sthe owner\n", a->name.c_str(), a->owned?"":"not "); - CScriptVar one(1); - CScriptVar *res = a->var->ref(); - CScriptVar *new_a = a->var->mathsOp(&one, op==LEX_PLUSPLUS ? '+' : '-'); + CScriptVarPtr one = newScriptVar(1); + CScriptVarPtr res = a->getVarPtr(); + CScriptVarPtr new_a = a->getValue()->mathsOp(one, op==LEX_PLUSPLUS ? '+' : '-'); a->replaceWith(new_a); a = res; - res->unref(0); } } } return a; } -// L->R: Precedence 5 -CScriptVarSmartLink CTinyJS::term(bool &execute) { - CScriptVarSmartLink a = unary(execute); - if (l->tk=='*' || l->tk=='/' || l->tk=='%') { +// L->R: Precedence 5 (term) * / % +CScriptVarSmartLink CTinyJS::execute_term(bool &execute) { + CScriptVarSmartLink a = execute_unary(execute); + if (t->tk=='*' || t->tk=='/' || t->tk=='%') { CheckRightHandVar(execute, a); - while (l->tk=='*' || l->tk=='/' || l->tk=='%') { - int op = l->tk; - l->match(l->tk); - CScriptVarSmartLink b = unary(execute); // L->R + while (t->tk=='*' || t->tk=='/' || t->tk=='%') { + int op = t->tk; + t->match(t->tk); + CScriptVarSmartLink b = execute_unary(execute); // L->R if (execute) { CheckRightHandVar(execute, b); - a = a->var->mathsOp(b->var, op); + a = a->getValue()->mathsOp(b->getValue(), op); } } } return a; } -// L->R: Precedence 6 -CScriptVarSmartLink CTinyJS::expression(bool &execute) { - CScriptVarSmartLink a = term(execute); - if (l->tk=='+' || l->tk=='-') { +// L->R: Precedence 6 (addition/subtraction) + - +CScriptVarSmartLink CTinyJS::execute_expression(bool &execute) { + CScriptVarSmartLink a = execute_term(execute); + if (t->tk=='+' || t->tk=='-') { CheckRightHandVar(execute, a); - while (l->tk=='+' || l->tk=='-') { - int op = l->tk; - l->match(l->tk); - CScriptVarSmartLink b = term(execute); // L->R + while (t->tk=='+' || t->tk=='-') { + int op = t->tk; + t->match(t->tk); + CScriptVarSmartLink b = execute_term(execute); // L->R if (execute) { CheckRightHandVar(execute, b); // not in-place, so just replace - a = a->var->mathsOp(b->var, op); + a = a->getValue()->mathsOp(b->getValue(), op); } } } return a; } -// L->R: Precedence 7 (TODO: add >>>) -CScriptVarSmartLink CTinyJS::binary_shift(bool &execute) { - CScriptVarSmartLink a = expression(execute); - if (l->tk==LEX_LSHIFT || l->tk==LEX_RSHIFT) { +// L->R: Precedence 7 (bitwise shift) << >> >>> +CScriptVarSmartLink CTinyJS::execute_binary_shift(bool &execute) { + CScriptVarSmartLink a = execute_expression(execute); + if (t->tk==LEX_LSHIFT || t->tk==LEX_RSHIFT || t->tk==LEX_RSHIFTU) { CheckRightHandVar(execute, a); - while (l->tk==LEX_LSHIFT || l->tk==LEX_RSHIFT) { - int op = l->tk; - l->match(l->tk); + while (t->tk>=LEX_SHIFTS_BEGIN && t->tk<=LEX_SHIFTS_END) { + int op = t->tk; + t->match(t->tk); - CScriptVarSmartLink b = expression(execute); // L->R + CScriptVarSmartLink b = execute_expression(execute); // L->R if (execute) { CheckRightHandVar(execute, a); // not in-place, so just replace - a = a->var->mathsOp(b->var, op); + a = a->getValue()->mathsOp(b->getValue(), op); } } } return a; } -// L->R: Precedence 9 & Precedence 8 (TODO: add instanceof and in) -CScriptVarSmartLink CTinyJS::relation(bool &execute, int set, int set_n) { - CScriptVarSmartLink a = set_n ? relation(execute, set_n, 0) : binary_shift(execute); - if ((set==LEX_EQUAL && (l->tk==LEX_EQUAL || l->tk==LEX_NEQUAL || l->tk==LEX_TYPEEQUAL || l->tk==LEX_NTYPEEQUAL)) - || (set=='<' && (l->tk==LEX_LEQUAL || l->tk==LEX_GEQUAL || l->tk=='<' || l->tk=='>' || l->tk == LEX_R_IN))) { +// L->R: Precedence 8 (relational) < <= > <= in (TODO: instanceof) +// L->R: Precedence 9 (equality) == != === !=== +CScriptVarSmartLink CTinyJS::execute_relation(bool &execute, int set, int set_n) { + CScriptVarSmartLink a = set_n ? execute_relation(execute, set_n, 0) : execute_binary_shift(execute); + if ((set==LEX_EQUAL && t->tk>=LEX_RELATIONS_1_BEGIN && t->tk<=LEX_RELATIONS_1_END) + || (set=='<' && (t->tk==LEX_LEQUAL || t->tk==LEX_GEQUAL || t->tk=='<' || t->tk=='>' || t->tk == LEX_R_IN))) { CheckRightHandVar(execute, a); - while ((set==LEX_EQUAL && (l->tk==LEX_EQUAL || l->tk==LEX_NEQUAL || l->tk==LEX_TYPEEQUAL || l->tk==LEX_NTYPEEQUAL)) - || (set=='<' && (l->tk==LEX_LEQUAL || l->tk==LEX_GEQUAL || l->tk=='<' || l->tk=='>' || l->tk == LEX_R_IN))) { - int op = l->tk; - l->match(l->tk); - CScriptVarSmartLink b = set_n ? relation(execute, set_n, 0) : binary_shift(execute); // L->R + while ((set==LEX_EQUAL && t->tk>=LEX_RELATIONS_1_BEGIN && t->tk<=LEX_RELATIONS_1_END) + || (set=='<' && (t->tk==LEX_LEQUAL || t->tk==LEX_GEQUAL || t->tk=='<' || t->tk=='>' || t->tk == LEX_R_IN))) { + int op = t->tk; + t->match(t->tk); + CScriptVarSmartLink b = set_n ? execute_relation(execute, set_n, 0) : execute_binary_shift(execute); // L->R if (execute) { CheckRightHandVar(execute, b); if(op == LEX_R_IN) { - a = new CScriptVarLink(new CScriptVar( b->var->findChild(a->var->getString())!= 0 )); + a = new CScriptVarLink(newScriptVar( findInPrototypeChain(b->getValue(), a->getValue()->getString())!= 0 )); } else - a = a->var->mathsOp(b->var, op); + a = a->getValue()->mathsOp(b->getValue(), op); } } } return a; } -// L->R: Precedence 12, Precedence 11 & Precedence 10 -CScriptVarSmartLink CTinyJS::logic_binary(bool &execute, int op, int op_n1, int op_n2) { - CScriptVarSmartLink a = op_n1 ? logic_binary(execute, op_n1, op_n2, 0) : relation(execute); - if (l->tk==op) { +// L->R: Precedence 10 (bitwise-and) & +// L->R: Precedence 11 (bitwise-xor) ^ +// L->R: Precedence 12 (bitwise-or) | +CScriptVarSmartLink CTinyJS::execute_binary_logic(bool &execute, int op, int op_n1, int op_n2) { + CScriptVarSmartLink a = op_n1 ? execute_binary_logic(execute, op_n1, op_n2, 0) : execute_relation(execute); + if (t->tk==op) { CheckRightHandVar(execute, a); - while (l->tk==op) { - l->match(l->tk); - CScriptVarSmartLink b = op_n1 ? logic_binary(execute, op_n1, op_n2, 0) : relation(execute); // L->R + while (t->tk==op) { + t->match(t->tk); + CScriptVarSmartLink b = op_n1 ? execute_binary_logic(execute, op_n1, op_n2, 0) : execute_relation(execute); // L->R if (execute) { CheckRightHandVar(execute, b); - a = a->var->mathsOp(b->var, op); + a = a->getValue()->mathsOp(b->getValue(), op); } } } return a; } - -// L->R: Precedence 14 & Precedence 13 -CScriptVarSmartLink CTinyJS::logic(bool &execute, int op, int op_n) { - CScriptVarSmartLink a = op_n ? logic(execute, op_n, 0) : logic_binary(execute); - if (l->tk==op) { +// L->R: Precedence 13 ==> (logical-or) && +// L->R: Precedence 14 ==> (logical-or) || +CScriptVarSmartLink CTinyJS::execute_logic(bool &execute, int op, int op_n) { + CScriptVarSmartLink a = op_n ? execute_logic(execute, op_n, 0) : execute_binary_logic(execute); + if (t->tk==op) { CheckRightHandVar(execute, a); - while (l->tk==op) { + while (t->tk==op) { CheckRightHandVar(execute, a); - bool noexecute = false; - int bop = l->tk; - l->match(l->tk); + int bop = t->tk; + t->match(t->tk); bool shortCircuit = false; bool boolean = false; // if we have short-circuit ops, then if we know the outcome @@ -2422,486 +3534,448 @@ CScriptVarSmartLink CTinyJS::logic(bool &execute, int op, int op_n) { // we need to tell mathsOp it's an & or | if (op==LEX_ANDAND) { bop = '&'; - shortCircuit = !a->var->getBool(); + shortCircuit = !a->getValue()->getBool(); boolean = true; } else if (op==LEX_OROR) { bop = '|'; - shortCircuit = a->var->getBool(); + shortCircuit = a->getValue()->getBool(); boolean = true; } - CScriptVarSmartLink b = op_n ? logic(shortCircuit ? noexecute : execute, op_n, 0) : logic_binary(shortCircuit ? noexecute : execute); // L->R + CScriptVarSmartLink b = op_n ? execute_logic(shortCircuit ? noexecute : execute, op_n, 0) : execute_binary_logic(shortCircuit ? noexecute : execute); // L->R CheckRightHandVar(execute, b); if (execute && !shortCircuit) { if (boolean) { CheckRightHandVar(execute, b); - a = new CScriptVar(a->var->getBool()); - b = new CScriptVar(b->var->getBool()); + a = newScriptVar(a->getValue()->getBool()); + b = newScriptVar(b->getValue()->getBool()); } - a = a->var->mathsOp(b->var, bop); + a = a->getValue()->mathsOp(b->getValue(), bop); } } } return a; } -// R->L: Precedence 15 -CScriptVarSmartLink CTinyJS::condition(bool &execute) +// L<-R: Precedence 15 (condition) ?: +CScriptVarSmartLink CTinyJS::execute_condition(bool &execute) { - CScriptVarSmartLink a = logic(execute); - if (l->tk=='?') + CScriptVarSmartLink a = execute_logic(execute); + if (t->tk=='?') { CheckRightHandVar(execute, a); - l->match(l->tk); - bool cond = a->var->getBool(); + t->match(t->tk); + bool cond = execute && a->getValue()->getBool(); CScriptVarSmartLink b; - bool noexecute=false; - a = condition(cond ? execute : noexecute ); // R->L - CheckRightHandVar(execute, a); - l->match(':'); - b = condition(cond ? noexecute : execute); // R-L - CheckRightHandVar(execute, b); + a = execute_condition(cond ? execute : noexecute ); // L<-R +// CheckRightHandVar(execute, a); + t->match(':'); + b = execute_condition(cond ? noexecute : execute); // R-L +// CheckRightHandVar(execute, b); if(!cond) - a=b; + return b; } return a; } -// R->L: Precedence 16 -CScriptVarSmartLink CTinyJS::assignment(bool &execute) { - CScriptVarSmartLink lhs = condition(execute); - if (l->tk=='=' || l->tk==LEX_PLUSEQUAL || l->tk==LEX_MINUSEQUAL || - l->tk==LEX_ASTERISKEQUAL || l->tk==LEX_SLASHEQUAL || l->tk==LEX_PERCENTEQUAL || - l->tk==LEX_LSHIFTEQUAL || l->tk==LEX_RSHIFTEQUAL || - l->tk==LEX_ANDEQUAL || l->tk==LEX_XOREQUAL || l->tk==LEX_OREQUAL) { - int op = l->tk; - int pos = l->tokenEnd; - l->match(l->tk); - CScriptVarSmartLink rhs = assignment(execute); // R->L +// L<-R: Precedence 16 (assignment) = += -= *= /= %= <<= >>= >>>= &= |= ^= +CScriptVarSmartLink CTinyJS::execute_assignment(bool &execute) { + CScriptVarSmartLink lhs = execute_condition(execute); + if (t->tk=='=' || (t->tk>=LEX_ASSIGNMENTS_BEGIN && t->tk<=LEX_ASSIGNMENTS_END) ) { + int op = t->tk; + CScriptTokenizer::ScriptTokenPosition leftHandPos = t->getPos(); + t->match(t->tk); + CScriptVarSmartLink rhs = execute_assignment(execute); // L<-R if (execute) { - if (!lhs->owned && lhs->name.length()==0) { - throw new CScriptException("invalid assignment left-hand side", pos); - } else if (op != '=' && !lhs->owned) { - throwError(execute, lhs->name + " is not defined", pos); - } - if (op=='=') { - if (!lhs->owned) { - CScriptVarLink *realLhs; - if(lhs->owner) - realLhs = lhs->owner->addChildNoDup(lhs->name, lhs->var); - else - realLhs = root->addChildNoDup(lhs->name, lhs->var); - lhs = realLhs; - } - lhs << rhs; - } else if (op==LEX_PLUSEQUAL) - lhs->replaceWith(lhs->var->mathsOp(rhs->var, '+')); - else if (op==LEX_MINUSEQUAL) - lhs->replaceWith(lhs->var->mathsOp(rhs->var, '-')); - else if (op==LEX_ASTERISKEQUAL) - lhs->replaceWith(lhs->var->mathsOp(rhs->var, '*')); - else if (op==LEX_SLASHEQUAL) - lhs->replaceWith(lhs->var->mathsOp(rhs->var, '/')); - else if (op==LEX_PERCENTEQUAL) - lhs->replaceWith(lhs->var->mathsOp(rhs->var, '%')); - else if (op==LEX_LSHIFTEQUAL) - lhs->replaceWith(lhs->var->mathsOp(rhs->var, LEX_LSHIFT)); - else if (op==LEX_RSHIFTEQUAL) - lhs->replaceWith(lhs->var->mathsOp(rhs->var, LEX_RSHIFT)); - else if (op==LEX_ANDEQUAL) - lhs->replaceWith(lhs->var->mathsOp(rhs->var, '&')); - else if (op==LEX_OREQUAL) - lhs->replaceWith(lhs->var->mathsOp(rhs->var, '|')); - else if (op==LEX_XOREQUAL) - lhs->replaceWith(lhs->var->mathsOp(rhs->var, '^')); + if (!lhs->IsOwned() && lhs->name.length()==0) { + throw new CScriptException("invalid assignment left-hand side", t->currentFile, leftHandPos.currentLine(), leftHandPos.currentColumn()); + } else if (op != '=' && !lhs->IsOwned()) { + throwError(execute, lhs->name + " is not defined"); + } + else if(lhs->IsWritable()) { + if (op=='=') { + if (!lhs->IsOwned()) { + CScriptVarLink *realLhs; + if(lhs->owner) + realLhs = lhs->owner->addChildNoDup(lhs->name, lhs); + else + realLhs = root->addChildNoDup(lhs->name, lhs); + lhs = realLhs; + } + lhs << rhs; + } else if (op==LEX_PLUSEQUAL) + lhs->replaceWith(lhs->getValue()->mathsOp(rhs->getValue(), '+')); + else if (op==LEX_MINUSEQUAL) + lhs->replaceWith(lhs->getValue()->mathsOp(rhs->getValue(), '-')); + else if (op==LEX_ASTERISKEQUAL) + lhs->replaceWith(lhs->getValue()->mathsOp(rhs->getValue(), '*')); + else if (op==LEX_SLASHEQUAL) + lhs->replaceWith(lhs->getValue()->mathsOp(rhs->getValue(), '/')); + else if (op==LEX_PERCENTEQUAL) + lhs->replaceWith(lhs->getValue()->mathsOp(rhs->getValue(), '%')); + else if (op==LEX_LSHIFTEQUAL) + lhs->replaceWith(lhs->getValue()->mathsOp(rhs->getValue(), LEX_LSHIFT)); + else if (op==LEX_RSHIFTEQUAL) + lhs->replaceWith(lhs->getValue()->mathsOp(rhs->getValue(), LEX_RSHIFT)); + else if (op==LEX_RSHIFTUEQUAL) + lhs->replaceWith(lhs->getValue()->mathsOp(rhs->getValue(), LEX_RSHIFTU)); + else if (op==LEX_ANDEQUAL) + lhs->replaceWith(lhs->getValue()->mathsOp(rhs->getValue(), '&')); + else if (op==LEX_OREQUAL) + lhs->replaceWith(lhs->getValue()->mathsOp(rhs->getValue(), '|')); + else if (op==LEX_XOREQUAL) + lhs->replaceWith(lhs->getValue()->mathsOp(rhs->getValue(), '^')); + } } } else CheckRightHandVar(execute, lhs); return lhs; } -// L->R: Precedence 17 -CScriptVarSmartLink CTinyJS::base(bool &execute) { +// L->R: Precedence 17 (comma) , +CScriptVarSmartLink CTinyJS::execute_base(bool &execute) { CScriptVarSmartLink a; for(;;) { - a = assignment(execute); // L->R - if (l->tk == ',') { - l->match(','); + a = execute_assignment(execute); // L->R + if (t->tk == ',') { + t->match(','); } else break; } return a; } -void CTinyJS::block(bool &execute) { - l->match('{'); - if (execute || IS_RUNTIME_PASS_TWO_1) { - while (l->tk && l->tk!='}') - statement(execute); - l->match('}'); - } else { - int brackets = 1; - while (l->tk && brackets) { - if (l->tk == '{') brackets++; - if (l->tk == '}') brackets--; - l->match(l->tk); +void CTinyJS::execute_block(bool &execute) { + if(t->tk == '{') { + if(execute) { + t->match('{'); + CScopeControl ScopeControl(this); + ScopeControl.addLetScope(); + while (t->tk && t->tk!='}') + execute_statement(execute); + t->match('}'); + // scopes.pop_back(); } + else + t->skip(t->getToken().Int()); } } - -CScriptVarSmartLink CTinyJS::statement(bool &execute) { +CScriptVarSmartLink CTinyJS::execute_statement(bool &execute) { CScriptVarSmartLink ret; - if (l->tk=='{') { + if (t->tk=='{') { /* A block of code */ - block(execute); - } else if (l->tk==';') { + execute_block(execute); + } else if (t->tk==';') { /* Empty statement - to allow things like ;;; */ - l->match(';'); - } else if (l->tk==LEX_R_VAR) { - /* variable creation. TODO - we need a better way of parsing the left - * hand side. Maybe just have a flag called can_create_var that we - * set and then we parse as if we're doing a normal equals.*/ - l->match(LEX_R_VAR); - for(;;) + t->match(';'); + } else if (t->tk==LEX_ID) { + ret = execute_base(execute); + t->match(';'); + } else if (t->tk==LEX_R_VAR || t->tk==LEX_R_LET) { + if(execute) { - CScriptVarSmartLink a; - string var = l->tkStr; - l->match(LEX_ID); - if (execute || IS_RUNTIME_PASS_TWO_1) { - a = scopes.back()->findChildOrCreate(var); - a->dontDelete = true; - }// sort out initialiser - if (l->tk == '=') { - l->match('='); - CScriptVarSmartLink var = assignment(execute); - if (execute) + bool let = t->tk==LEX_R_LET, let_ext=false; + t->match(t->tk); + CScopeControl ScopeControl(this); + if(let && t->tk=='(') { + let_ext = true; + t->match(t->tk); + ScopeControl.addLetScope(); + } + CScriptVarPtr in_scope = let ? scope()->scopeLet() : scope()->scopeVar(); + for(;;) { + CScriptVarSmartLink a; + string var = t->tkStr(); + t->match(LEX_ID); + a = in_scope->findChildOrCreate(var); + a->SetDeletable(false); + // sort out initialiser + if (t->tk == '=') { + t->match('='); + CScriptVarSmartLink var = execute_assignment(execute); a << var; + } + if (t->tk == ',') + t->match(','); + else + break; } - if (l->tk == ',') - l->match(','); - else - break; - } - l->match(';'); - } else if (l->tk==LEX_R_IF) { - l->match(LEX_R_IF); - l->match('('); - CScriptVarSmartLink var = base(execute); - l->match(')'); - bool cond = execute && var->var->getBool(); - bool noexecute = false; // because we need to be abl;e to write to it - statement(cond ? execute : noexecute); - if (l->tk==LEX_R_ELSE) { - l->match(LEX_R_ELSE); - statement(cond ? noexecute : execute); - } - } else if (l->tk==LEX_R_DO) { - // We do repetition by pulling out the string representing our statement - // there's definitely some opportunity for optimisation here - l->match(LEX_R_DO); - if(IS_RUNTIME_PASS_TWO_1) - { - statement(execute); - l->match(LEX_R_WHILE); - l->match('('); - base(execute); - l->match(')'); - l->match(';'); - } else { - bool loopCond = true; - bool old_execute = execute; + if(let_ext) { + t->match(')'); + execute_statement(execute); + } else + t->match(';'); + } else + t->skip(t->getToken().Int()); + } else if (t->tk==LEX_R_WITH) { + if(execute) { + t->match(LEX_R_WITH); + t->match('('); + CScriptVarSmartLink var = execute_base(execute); + t->match(')'); + CScopeControl ScopeControl(this); + ScopeControl.addWithScope(var); + ret = execute_statement(execute); + } else + t->skip(t->getToken().Int()); + } else if (t->tk==LEX_R_IF) { + if(execute) { + t->match(LEX_R_IF); + t->match('('); + bool cond = execute_base(execute)->getValue()->getBool(); + t->match(')'); + if(cond && execute) { + t->match(LEX_T_SKIP); + execute_statement(execute); + } else { + t->check(LEX_T_SKIP); + t->skip(t->getToken().Int()); + } + if (t->tk==LEX_R_ELSE) { + if(!cond && execute) { + t->match(LEX_R_ELSE); + execute_statement(execute); + } + else + t->skip(t->getToken().Int()); + } + } else + t->skip(t->getToken().Int()); + } else if (t->tk==LEX_R_DO) { + if(execute) { + t->match(LEX_R_DO); + CScriptTokenizer::ScriptTokenPosition loopStart = t->getPos(); int old_loop_runtimeFlags = runtimeFlags & RUNTIME_LOOP_MASK; runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | RUNTIME_CANBREAK | RUNTIME_CANCONTINUE; - int whileBodyStart = l->tokenStart; - int whileCondStart = 0; - CScriptLex *oldLex = NULL; - CScriptLex *whileBody = NULL; - CScriptLex *whileCond = NULL; + bool loopCond = true; int loopCount = TINYJS_LOOP_MAX_ITERATIONS; - while (loopCond && loopCount-->0) - { - if(whileBody) - { - whileBody->reset(); - l = whileBody; - } - statement(execute); - if(!whileBody) whileBody = l->getSubLex(whileBodyStart); - if(old_execute && !execute) + while (loopCond && execute && loopCount-->0) { + t->setPos(loopStart); + execute_statement(execute); + if(!execute) { // break or continue if(runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE)) { - execute = old_execute; - if(runtimeFlags & RUNTIME_BREAK) - loopCond = false; + execute = true; + bool Break = (runtimeFlags & RUNTIME_BREAK) != 0; runtimeFlags &= ~(RUNTIME_BREAK | RUNTIME_CONTINUE); + if(Break) { + t->match(LEX_R_WHILE); + t->check('('); + t->skip(t->getToken().Int()); + t->match(';'); + break; + } } // other stuff e.g return, throw } - - if(!whileCond) - { - l->match(LEX_R_WHILE); - l->match('('); - whileCondStart = l->tokenStart; - } - else - { - whileCond->reset(); - l = whileCond; - } - CScriptVarSmartLink cond = base(execute); - loopCond = execute && cond->var->getBool(); - if(!whileCond) - { - whileCond = l->getSubLex(whileCondStart); - l->match(')'); - l->match(';'); - oldLex = l; + t->match(LEX_R_WHILE); + if(execute) { + t->match('('); + loopCond = execute_base(execute)->getValue()->getBool(); + t->match(')'); + } else { + t->check('('); + t->skip(t->getToken().Int()); } + t->match(';'); } runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | old_loop_runtimeFlags; - l = oldLex; - delete whileCond; - delete whileBody; - if (loopCount<=0) { - root->trace(); - TRACE("WHILE Loop exceeded %d iterations at %s\n", TINYJS_LOOP_MAX_ITERATIONS, l->getPosition(l->tokenLastEnd).c_str()); - throw new CScriptException("LOOP_ERROR"); - } - } - } else if (l->tk==LEX_R_WHILE) { - // We do repetition by pulling out the string representing our statement - // there's definitely some opportunity for optimisation here - l->match(LEX_R_WHILE); - l->match('('); - if(IS_RUNTIME_PASS_TWO_1) { - base(execute); - l->match(')'); - statement(execute); - } else { - int whileCondStart = l->tokenStart; - bool noexecute = false; - CScriptVarSmartLink cond = base(execute); - bool loopCond = execute && cond->var->getBool(); - CScriptLex *whileCond = l->getSubLex(whileCondStart); - l->match(')'); - int whileBodyStart = l->tokenStart; - - bool old_execute = execute; - int old_loop_runtimeFlags = runtimeFlags & RUNTIME_LOOP_MASK; - runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | RUNTIME_CANBREAK | RUNTIME_CANCONTINUE; - statement(loopCond ? execute : noexecute); - CScriptLex *whileBody = l->getSubLex(whileBodyStart); - CScriptLex *oldLex = l; - if(loopCond && old_execute != execute) - { - // break or continue - if(runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE)) - { - execute = old_execute; - if(runtimeFlags & RUNTIME_BREAK) - loopCond = false; - runtimeFlags &= ~(RUNTIME_BREAK | RUNTIME_CONTINUE); - } - // other stuff e.g return, throw + ostringstream errorString; + errorString << "'for'-Loop exceeded " << TINYJS_LOOP_MAX_ITERATIONS << " iterations"; + throw new CScriptException(errorString.str(), t->currentFile, loopStart.currentLine(), loopStart.currentColumn()); } - - int loopCount = TINYJS_LOOP_MAX_ITERATIONS; - while (loopCond && loopCount-->0) { - whileCond->reset(); - l = whileCond; - cond = base(execute); - loopCond = execute && cond->var->getBool(); - if (loopCond) { - whileBody->reset(); - l = whileBody; - statement(execute); - if(!execute) - { + } else + t->skip(t->getToken().Int()); + } else if (t->tk==LEX_R_WHILE) { + if(execute) { + t->match(LEX_R_WHILE); + bool loopCond; + t->match('('); + CScriptTokenizer::ScriptTokenPosition condStart = t->getPos(); + loopCond = execute_base(execute)->getValue()->getBool(); + t->match(')'); + if(loopCond && execute) { + t->match(LEX_T_SKIP); + CScriptTokenizer::ScriptTokenPosition loopStart = t->getPos(); + CScriptTokenizer::ScriptTokenPosition loopEnd = loopStart; + int old_loop_runtimeFlags = runtimeFlags & RUNTIME_LOOP_MASK; + runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | RUNTIME_CANBREAK | RUNTIME_CANCONTINUE; + int loopCount = TINYJS_LOOP_MAX_ITERATIONS; + while (loopCond && execute && loopCount-->0) { + t->setPos(loopStart); + execute_statement(execute); + if(loopEnd == loopStart) // first loop-pass + loopEnd = t->getPos(); + if(!execute) { // break or continue if(runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE)) { - execute = old_execute; - if(runtimeFlags & RUNTIME_BREAK) - loopCond = false; + execute = true; + bool Break = (runtimeFlags & RUNTIME_BREAK) != 0; runtimeFlags &= ~(RUNTIME_BREAK | RUNTIME_CONTINUE); + if(Break) break; } // other stuff e.g return, throw } + if(execute) { + t->setPos(condStart); + loopCond = execute_base(execute)->getValue()->getBool(); + } + } + t->setPos(loopEnd); + runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | old_loop_runtimeFlags; + if (loopCount<=0) { + ostringstream errorString; + errorString << "'for'-Loop exceeded " << TINYJS_LOOP_MAX_ITERATIONS << " iterations"; + throw new CScriptException(errorString.str(), t->currentFile, loopStart.currentLine(), loopStart.currentColumn()); } - } - runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | old_loop_runtimeFlags; - l = oldLex; - delete whileCond; - delete whileBody; - - if (loopCount<=0) { - root->trace(); - TRACE("WHILE Loop exceeded %d iterations at %s\n", TINYJS_LOOP_MAX_ITERATIONS, l->getPosition(l->tokenLastEnd).c_str()); - throw new CScriptException("LOOP_ERROR"); - } - } - } else if (l->tk==LEX_R_FOR) { - l->match(LEX_R_FOR); - bool for_each_in; - int error_pos = -1; - if((for_each_in = l->tk == LEX_ID && l->tkStr == "each")) l->match(LEX_ID); - l->match('('); - int pos=l->tokenStart; - bool for_in = false; - CScriptVarSmartLink for_var; - CScriptVarSmartLink for_in_var; - if(l->tk == LEX_ID) { - for_var = factor(execute); - - if(l->tk == LEX_R_IN) { - for_in = true; - l->match(LEX_R_IN); - for_in_var = factor(execute); - l->match(')'); } else { - error_pos = l->tokenEnd; - l->reset(pos); - } - } - if(for_each_in && !for_in) - throw new CScriptException("invalid for each loop", error_pos); - else if (for_in) { - if (execute && !for_var->owned) { - CScriptVarLink *real_for_var; - if(for_var->owner) - real_for_var = for_var->owner->addChildNoDup(for_var->name, for_var->var); - else - real_for_var = root->addChildNoDup(for_var->name, for_var->var); - for_var = real_for_var; + t->check(LEX_T_SKIP); + t->skip(t->getToken().Int()); } - if(IS_RUNTIME_PASS_TWO_1) - statement(execute); - else { - int forBodyStart = l->tokenStart; - bool old_execute = execute; + } else + t->skip(t->getToken().Int()); + } else if (t->tk==LEX_T_FOR_IN || t->tk == LEX_T_FOR_EACH_IN) { + if(execute) { + bool for_each = t->tk == LEX_T_FOR_EACH_IN; + t->match(t->tk); + t->match('('); + CScriptVarSmartLink for_var; + CScriptVarSmartLink for_in_var; + + CScopeControl ScopeControl(this); + ScopeControl.addLetScope(); + + if(t->tk == LEX_R_LET) { + t->match(LEX_R_LET); + string var = t->tkStr(); + t->match(LEX_ID); + for_var = scope()->scopeLet()->findChildOrCreate(var); + } + else + for_var = execute_function_call(execute); + + t->match(LEX_R_IN); + + for_in_var = execute_function_call(execute); + CheckRightHandVar(execute, for_in_var); + t->match(')'); + if( (*for_in_var)->Childs.size() ) { + if(!for_var->IsOwned()) { + CScriptVarLink *real_for_var; + if(for_var->owner) + real_for_var = for_var->owner->addChildNoDup(for_var->name, for_var); + else + real_for_var = root->addChildNoDup(for_var->name, for_var); + for_var = real_for_var; + } + + CScriptTokenizer::ScriptTokenPosition loopStart = t->getPos(); + int old_loop_runtimeFlags = runtimeFlags & RUNTIME_LOOP_MASK; runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | RUNTIME_CANBREAK | RUNTIME_CANCONTINUE; - for(SCRIPTVAR_CHILDS::iterator it = for_in_var->var->Childs.begin(); execute && it != for_in_var->var->Childs.end(); ++it) { - if (for_each_in) - for_var->replaceWith(it->second->var); + for(SCRIPTVAR_CHILDS_it it = (*for_in_var)->Childs.begin(); execute && it != (*for_in_var)->Childs.end(); ++it) { + if (for_each) + for_var->replaceWith(*it); else - for_var->replaceWith(new CScriptVar(it->first)); - l->reset(forBodyStart); - statement(execute); + for_var->replaceWith(newScriptVar((*it)->name)); + t->setPos(loopStart); + execute_statement(execute); if(!execute) { // break or continue if(runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE)) { - execute = old_execute; - if(runtimeFlags & RUNTIME_BREAK) - break; + execute = true; + bool Break = (runtimeFlags & RUNTIME_BREAK)!=0; runtimeFlags &= ~(RUNTIME_BREAK | RUNTIME_CONTINUE); + if(Break) break; } // other stuff e.g return, throw } } runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | old_loop_runtimeFlags; + } else { + execute_statement(noexecute); } } else { - statement(execute); // initialisation - //l->match(';'); - bool noexecute = false; - int forCondStart = l->tokenStart; + t->skip(t->getToken().Int()); + execute_statement(execute); + } + } else if (t->tk==LEX_R_FOR) { + if(execute) + { + t->match(LEX_R_FOR); + t->match('('); + CScopeControl ScopeControl(this); + if(t->tk == LEX_R_LET) + ScopeControl.addLetScope(); + execute_statement(execute); // initialisation + CScriptTokenizer::ScriptTokenPosition conditionStart = t->getPos(); bool cond_empty = true; - bool loopCond = true; // Empty Condition -->always true - CScriptVarSmartLink cond; - if(l->tk != ';') { + bool loopCond = execute; // Empty Condition -->always true + if(t->tk != ';') { cond_empty = false; - cond = base(execute); // condition - loopCond = execute && cond->var->getBool(); + loopCond = execute && execute_base(execute)->getValue()->getBool(); } - CScriptLex *forCond = l->getSubLex(forCondStart); - l->match(';'); + t->match(';'); + CScriptTokenizer::ScriptTokenPosition iterStart = t->getPos(); bool iter_empty = true; - CScriptLex *forIter=NULL; - if(l->tk != ')') + if(t->tk != ')') { iter_empty = false; - int forIterStart = l->tokenStart; - base(noexecute); // iterator - forIter = l->getSubLex(forIterStart); - } - l->match(')'); - if(IS_RUNTIME_PASS_TWO_1) { - delete forCond; - delete forIter; - statement(execute); - } else { - int forBodyStart = l->tokenStart; - bool old_execute = execute; + execute_base(noexecute); // iterator + } + t->match(')'); + if(loopCond) { // when execute==false then loopCond always false to + CScriptTokenizer::ScriptTokenPosition loopStart = t->getPos(); + CScriptTokenizer::ScriptTokenPosition loopEnd = t->getPos(); + int old_loop_runtimeFlags = runtimeFlags & RUNTIME_LOOP_MASK; runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | RUNTIME_CANBREAK | RUNTIME_CANCONTINUE; - statement(loopCond ? execute : noexecute); - CScriptLex *forBody = l->getSubLex(forBodyStart); - CScriptLex *oldLex = l; - if(loopCond && old_execute != execute) { - // break or continue - if(runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE)) { - execute = old_execute; - if(runtimeFlags & RUNTIME_BREAK) - loopCond = false; - runtimeFlags &= ~(RUNTIME_BREAK | RUNTIME_CONTINUE); - } - // other stuff e.g return, throw - } - - if (loopCond && !iter_empty) { - forIter->reset(); - l = forIter; - base(execute); - } int loopCount = TINYJS_LOOP_MAX_ITERATIONS; - while (execute && loopCond && loopCount-->0) { - if(!cond_empty) { - forCond->reset(); - l = forCond; - cond = base(execute); - loopCond = cond->var->getBool(); - } - if (execute && loopCond) { - forBody->reset(); - l = forBody; - statement(execute); - if(!execute) { - // break or continue; - if(runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE)) { - execute = old_execute; - if(runtimeFlags & RUNTIME_BREAK) - loopCond = false; - runtimeFlags &= ~(RUNTIME_BREAK | RUNTIME_CONTINUE); - } - // return nothing to do + while (loopCond && execute && loopCount-->0) { + t->setPos(loopStart); + execute_statement(execute); + if(loopEnd == loopStart) // first loop-pass + loopEnd = t->getPos(); + if(!execute) { + // break or continue + if(runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE)) { + execute = true; + bool Break = (runtimeFlags & RUNTIME_BREAK)!=0; + runtimeFlags &= ~(RUNTIME_BREAK | RUNTIME_CONTINUE); + if(Break) break; + } + // other stuff e.g return, throw + } + if(execute) { + if(!iter_empty) { + t->setPos(iterStart);; + execute_base(execute); + } + if(!cond_empty) { + t->setPos(conditionStart); + loopCond = execute_base(execute)->getValue()->getBool(); } - } - if (execute && loopCond && !iter_empty) { - forIter->reset(); - l = forIter; - base(execute); } } + t->setPos(loopEnd); runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | old_loop_runtimeFlags; - l = oldLex; - delete forCond; - delete forIter; - delete forBody; if (loopCount<=0) { - root->trace(); - TRACE("FOR Loop exceeded %d iterations at %s\n", TINYJS_LOOP_MAX_ITERATIONS, l->getPosition(l->tokenLastEnd).c_str()); - throw new CScriptException("LOOP_ERROR"); + ostringstream errorString; + errorString << "'for'-Loop exceeded " << TINYJS_LOOP_MAX_ITERATIONS << " iterations"; + throw new CScriptException(errorString.str(), t->currentFile, loopStart.currentLine(), loopStart.currentColumn()); } + } else { + execute_statement(noexecute); } - } - } else if (l->tk==LEX_R_BREAK) { - l->match(LEX_R_BREAK); + } else + t->skip(t->getToken().Int()); + } else if (t->tk==LEX_R_BREAK) { + t->match(LEX_R_BREAK); if(execute) { if(runtimeFlags & RUNTIME_CANBREAK) { @@ -2909,11 +3983,11 @@ CScriptVarSmartLink CTinyJS::statement(bool &execute) { execute = false; } else - throw new CScriptException("'break' must be inside loop or switch"); + throw new CScriptException("'break' must be inside loop or switch", t->currentFile, t->currentLine(), t->currentColumn()); } - l->match(';'); - } else if (l->tk==LEX_R_CONTINUE) { - l->match(LEX_R_CONTINUE); + t->match(';'); + } else if (t->tk==LEX_R_CONTINUE) { + t->match(LEX_R_CONTINUE); if(execute) { if(runtimeFlags & RUNTIME_CANCONTINUE) { @@ -2921,39 +3995,51 @@ CScriptVarSmartLink CTinyJS::statement(bool &execute) { execute = false; } else - throw new CScriptException("'continue' must be inside loop"); - } - l->match(';'); - } else if (l->tk==LEX_R_RETURN) { - l->match(LEX_R_RETURN); - CScriptVarSmartLink result; - if (l->tk != ';') - result = base(execute); + throw new CScriptException("'continue' must be inside loop or switch", t->currentFile, t->currentLine(), t->currentColumn()); + } + t->match(';'); + } else if (t->tk==LEX_R_RETURN) { if (execute) { if (IS_RUNTIME_CANRETURN) { - if(result->var) scopes.back()->setReturnVar(result->var); + t->match(LEX_R_RETURN); + CScriptVarPtr result; + if (t->tk != ';') + result = execute_base(execute)->getValue(); + if(result) scope()->scopeVar()->setReturnVar(result); } else - throw new CScriptException("'return' statement, but not in a function."); + throw new CScriptException("'return' statement, but not in a function.", t->currentFile, t->currentLine(), t->currentColumn()); execute = false; } - l->match(';'); - } else if (l->tk==LEX_R_FUNCTION) { - CScriptVarSmartLink funcVar = parseFunctionDefinition(); - if ((execute && IS_RUNTIME_PASS_SINGLE) || IS_RUNTIME_PASS_TWO_1) { - if (funcVar->name == TINYJS_TEMP_NAME) + else { + t->match(LEX_R_RETURN); + if (t->tk != ';') + execute_base(execute); + } + t->match(';'); + } else if (t->tk == LEX_T_FUNCTION_OPERATOR) { + // ignore force anonymous at statement-level + ASSERT(0); // no functions-operator statement-level + t->match(t->tk); + } else if (t->tk == LEX_R_FUNCTION) { + if(execute) { + CScriptTokenDataFnc &Fnc = t->getToken().Fnc(); + if(!Fnc.name.length()) throw new CScriptException("Functions defined at statement-level are meant to have a name."); - else - scopes.back()->addChildNoDup(funcVar->name, funcVar->var)->dontDelete = true; + else { + CScriptVarSmartLink funcVar = parseFunctionDefinition(t->getToken()); + scope()->scopeVar()->addChildNoDup(funcVar->name, funcVar)->SetDeletable(false); + } } - } else if (l->tk==LEX_R_TRY) { - l->match(LEX_R_TRY); + t->match(t->tk); + } else if (t->tk==LEX_R_TRY) { + t->match(LEX_R_TRY); bool old_execute = execute; // save runtimeFlags int old_throw_runtimeFlags = runtimeFlags & RUNTIME_THROW_MASK; // set runtimeFlags runtimeFlags = (runtimeFlags & ~RUNTIME_THROW_MASK) | RUNTIME_CANTHROW; - block(execute); + execute_block(execute); //try{print("try1");try{ print("try2");try{print("try3");throw 3;}catch(a){print("catch3");throw 3;}finally{print("finalli3");}print("after3");}catch(a){print("catch2");}finally{print("finalli2");}print("after2");}catch(a){print("catch1");}finally{print("finalli1");}print("after1"); bool isThrow = (runtimeFlags & RUNTIME_THROW) != 0; if(isThrow) execute = old_execute; @@ -2961,201 +4047,234 @@ CScriptVarSmartLink CTinyJS::statement(bool &execute) { // restore runtimeFlags runtimeFlags = (runtimeFlags & ~RUNTIME_THROW_MASK) | old_throw_runtimeFlags; - if(l->tk != LEX_R_FINALLY) // expect catch + if(t->tk != LEX_R_FINALLY) // expect catch { - l->match(LEX_R_CATCH); - l->match('('); - string exception_var_name = l->tkStr; - l->match(LEX_ID); - l->match(')'); + t->match(LEX_R_CATCH); + t->match('('); + string exception_var_name = t->tkStr(); + t->match(LEX_ID); + t->match(')'); if(isThrow) { - CScriptVar *catchScope = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT); + CScriptVarScopeFncPtr catchScope(::newScriptVar(this, ScopeFnc, (CScriptVar*)0)); catchScope->addChild(exception_var_name, exceptionVar); - exceptionVar->unref(0); exceptionVar = 0; - scopes.push_back(catchScope); - try { - block(execute); - } catch (CScriptException *e) { - scopes.pop_back(); // clean up scopes - delete catchScope; - throw e; - } - scopes.pop_back(); - delete catchScope; + CScopeControl ScopeControl(this); + ScopeControl.addFncScope(catchScope); + execute_block(execute); } else { - bool noexecute = false; - block(noexecute); + execute_block(noexecute); } } - if(l->tk == LEX_R_FINALLY) { - l->match(LEX_R_FINALLY); + if(t->tk == LEX_R_FINALLY) { + t->match(LEX_R_FINALLY); bool finally_execute = true; - block(isThrow ? finally_execute : execute); + execute_block(isThrow ? finally_execute : execute); } - } else if (l->tk==LEX_R_THROW) { - int tokenStart = l->tokenStart; - l->match(LEX_R_THROW); - CScriptVarSmartLink a = base(execute); + } else if (t->tk==LEX_R_THROW) { + CScriptTokenizer::ScriptTokenPosition tokenPos = t->getPos(); +// int tokenStart = t->getToken().pos; + t->match(LEX_R_THROW); + CScriptVarPtr a = execute_base(execute)->getValue(); if(execute) { if(runtimeFlags & RUNTIME_CANTHROW) { runtimeFlags |= RUNTIME_THROW; execute = false; - exceptionVar = a->var->ref(); + exceptionVar = a; } else - throw new CScriptException("uncaught exception: '"+a->var->getString()+"'", tokenStart); + throw new CScriptException("uncaught exception: '"+a->getString()+"'", t->currentFile, tokenPos.currentLine(), tokenPos.currentColumn()); } - } else if (l->tk==LEX_R_SWITCH) { - l->match(LEX_R_SWITCH); - l->match('('); - CScriptVarSmartLink SwitchValue = base(execute); - l->match(')'); + } else if (t->tk == LEX_R_SWITCH) { if(execute) { - // save runtimeFlags - int old_switch_runtimeFlags = runtimeFlags & RUNTIME_BREAK_MASK; - // set runtimeFlags - runtimeFlags = (runtimeFlags & ~RUNTIME_BREAK_MASK) | RUNTIME_CANBREAK; - bool noexecute = false; - bool old_execute = execute = false; - l->match('{'); - if(l->tk == LEX_R_CASE || l->tk == LEX_R_DEFAULT || l->tk == '}') { - - while (l->tk && l->tk!='}') { - if(l->tk == LEX_R_CASE) { - l->match(LEX_R_CASE); - if(execute) { - base(noexecute); - } else { - if(old_execute == execute) - old_execute = execute = true; - CScriptVarSmartLink CaseValue = base(execute); - if(execute) { - CaseValue = CaseValue->var->mathsOp(SwitchValue->var, LEX_EQUAL); - old_execute = execute = CaseValue->var->getBool(); + t->match(LEX_R_SWITCH); + t->match('('); + CScriptVarPtr SwitchValue = execute_base(execute)->getValue(); + t->match(')'); + if(execute) { + // save runtimeFlags + int old_switch_runtimeFlags = runtimeFlags & RUNTIME_BREAK_MASK; + // set runtimeFlags + runtimeFlags = (runtimeFlags & ~RUNTIME_BREAK_MASK) | RUNTIME_CANBREAK; + bool found = false ,execute = false; + t->match('{'); + CScopeControl ScopeControl(this); + ScopeControl.addLetScope(); + if(t->tk == LEX_R_CASE || t->tk == LEX_R_DEFAULT || t->tk == '}') { + CScriptTokenizer::ScriptTokenPosition defaultStart = t->getPos(); + bool hasDefault = false; + while (t->tk) { + if(t->tk == LEX_R_CASE) { + if(found) { + t->skip(t->getToken().Int()); + if(execute) t->match(':'); + else t->skip(t->getToken().Int()); + } else { + t->match(LEX_R_CASE); + execute = true; + CScriptVarSmartLink CaseValue = execute_base(execute); + if(execute) { + CaseValue = CaseValue->getValue()->mathsOp(SwitchValue, LEX_EQUAL); + found = execute = (*CaseValue)->getBool(); + if(found) t->match(':'); + else t->skip(t->getToken().Int()); + } else { + found = true; + t->skip(t->getToken().Int()); + } } - } - l->match(':'); - } else if(l->tk == LEX_R_DEFAULT) { - l->match(LEX_R_DEFAULT); - l->match(':'); - if(!old_execute && !execute ) - old_execute = execute = true; + } else if(t->tk == LEX_R_DEFAULT) { + t->match(LEX_R_DEFAULT); + if(found) { + if(execute) t->match(':'); + else t->skip(t->getToken().Int()); + } else { + hasDefault = true; + defaultStart = t->getPos(); + t->skip(t->getToken().Int()); + } + } else if(t->tk == '}') { + if(!found && hasDefault) { + found = execute = true; + t->setPos(defaultStart); + t->match(':'); + } else + break; + } else + execute_statement(execute); } - statement(execute); - } - l->match('}'); - if(runtimeFlags & RUNTIME_BREAK) - execute = true; - // restore runtimeFlags - runtimeFlags = (runtimeFlags & ~RUNTIME_BREAK_MASK) | old_switch_runtimeFlags; + t->match('}'); + if(!found || (runtimeFlags & RUNTIME_BREAK) ) + execute = true; + // restore runtimeFlags + runtimeFlags = (runtimeFlags & ~RUNTIME_BREAK_MASK) | old_switch_runtimeFlags; + } else + throw new CScriptException("invalid switch statement"); } else - throw new CScriptException("invalid switch statement"); + execute_block(execute); } else - block(execute); - } else if(l->tk != LEX_EOF) { + t->skip(t->getToken().Int()); + } else if(t->tk == LEX_T_LABEL) { + t->match(LEX_T_LABEL); // ignore Labels + t->match(':'); + } else if(t->tk != LEX_EOF) { /* Execute a simple statement that only contains basic arithmetic... */ - ret = base(execute); - l->match(';'); + ret = execute_base(execute); + t->match(';'); } else - l->match(LEX_EOF); + t->match(LEX_EOF); return ret; } -/// Get the value of the given variable, or return 0 -const string *CTinyJS::getVariable(const string &path) { - // traverse path - size_t prevIdx = 0; - size_t thisIdx = path.find('.'); - if (thisIdx == string::npos) thisIdx = path.length(); - CScriptVar *var = root; - while (var && prevIdxfindChild(el); - var = varl?varl->var:0; - prevIdx = thisIdx+1; - thisIdx = path.find('.', prevIdx); - if (thisIdx == string::npos) thisIdx = path.length(); - } - // return result - if (var) - return &var->getString(); - else - return 0; -} /// Finds a child, looking recursively up the scopes -CScriptVarLink *CTinyJS::findInScopes(const std::string &childName) { +CScriptVarLink *CTinyJS::findInScopes(const string &childName) { + return scope()->findInScopes(childName); +/* CScriptVarLink *v = scopes.back()->findChild(childName); if(!v && scopes.front() != scopes.back()) v = scopes.front()->findChild(childName); return v; +*/ } +/// Get all Keynames of en given object (optionial look up the prototype-chain) +void CTinyJS::keys(STRING_VECTOR_t &Keys, CScriptVarPtr object, bool WithPrototypeChain) { + CScriptVarLink *__proto__; + for(SCRIPTVAR_CHILDS_it it = object->Childs.begin(); it != object->Childs.end(); ++it) { + if((*it)->IsEnumerable()) + Keys.push_back((*it)->name); + + if (WithPrototypeChain) { + if( (__proto__ = (*(*it))->findChild(TINYJS___PROTO___VAR)) ) + keys(Keys, __proto__, WithPrototypeChain); + else if((*(*it))->isString()) + keys(Keys, stringPrototype, WithPrototypeChain); + else if((*(*it))->isArray()) + keys(Keys, arrayPrototype, WithPrototypeChain); + else if((*(*it))->isFunction()) + keys(Keys, functionPrototype, WithPrototypeChain); + else + keys(Keys, objectPrototype, false); + } + } +} /// Look up in any parent classes of the given object -CScriptVarLink *CTinyJS::findInParentClasses(CScriptVar *object, const std::string &name) { +CScriptVarLink *CTinyJS::findInPrototypeChain(CScriptVarPtr object, const string &name) { // Look for links to actual parent classes - CScriptVarLink *parentClass = object->findChild(TINYJS_PROTOTYPE_CLASS); - while (parentClass) { - CScriptVarLink *implementation = parentClass->var->findChild(name); - if (implementation) return implementation; - parentClass = parentClass->var->findChild(TINYJS_PROTOTYPE_CLASS); + CScriptVarLink *__proto__; +// CScriptVar *_object = object; + while( (__proto__ = object->findChild(TINYJS___PROTO___VAR)) ) { + CScriptVarLink *implementation = (*__proto__)->findChild(name); + if (implementation){ + return implementation; + } + object = __proto__->getVarPtr(); } - // else fake it for strings and finally objects +/* if (object->isString()) { - CScriptVarLink *implementation = stringClass->findChild(name); + CScriptVarLink *implementation = stringPrototype->findChild(name); if (implementation) return implementation; } if (object->isArray()) { - CScriptVarLink *implementation = arrayClass->findChild(name); + CScriptVarLink *implementation = arrayPrototype->findChild(name); + if (implementation) return implementation; + } + if (object->isFunction()) { + CScriptVarLink *implementation = functionPrototype->findChild(name); if (implementation) return implementation; } - CScriptVarLink *implementation = objectClass->findChild(name); + CScriptVarLink *implementation = objectPrototype->findChild(name); if (implementation) return implementation; - +*/ return 0; } - -void CTinyJS::scEval(CScriptVar *c, void *data) { - std::string code = c->getParameter("jsCode")->getString(); - CScriptVar *scEvalScope = scopes.back(); // save scope - scopes.pop_back(); // go back to the callers scope +void CTinyJS::native_Object(const CFunctionsScopePtr &c, void *data) { + CScriptVarPtr objc = c->getParameter(0); + if(objc->isUndefined() || objc->isNull()) + c->setReturnVar(newScriptVar(Object)); + else + c->setReturnVar(objc); +} +void CTinyJS::native_Array(const CFunctionsScopePtr &c, void *data) { +// CScriptVar *returnVar = new CScriptVarArray(c->getContext()); + CScriptVarPtr returnVar = c->newScriptVar(Array); + c->setReturnVar(returnVar); + int length = c->getParameterLength(); + if(length == 1 && c->getParameter(0)->isNumber()) + returnVar->setArrayIndex(c->getParameter(0)->getInt(), newScriptVar(Undefined)); + else for(int i=0; isetArrayIndex(i, c->getParameter(i)); +} +void CTinyJS::native_String(const CFunctionsScopePtr &c, void *data) { + if(c->getParameterLength()==0) + c->setReturnVar(c->newScriptVar("")); + else + c->setReturnVar(c->newScriptVar(c->getParameter(0)->getString())); +} +void CTinyJS::native_Eval(const CFunctionsScopePtr &c, void *data) { + string Code = c->getParameter("jsCode")->getString(); + CScriptVarScopePtr scEvalScope = scope(); // save scope + _scopes.pop_back(); // go back to the callers scope try { - CScriptLex *oldLex = l; - l = new CScriptLex(code); CScriptVarSmartLink returnVar; - runtimeFlags |= RUNTIME_CANBREAK | RUNTIME_CANCONTINUE; - - SAVE_RUNTIME_PASS; - SET_RUNTIME_PASS_SINGLE; + CScriptTokenizer *oldTokenizer = t; t=0; + t = new CScriptTokenizer(Code.c_str(), "eval"); try { bool execute = true; - bool noexecute = false; - if(twoPass) - { - SET_RUNTIME_PASS_TWO_1; - do { - statement(noexecute); - while (l->tk==';') l->match(';'); // skip empty statements - } while (l->tk!=LEX_EOF); - l->reset(); - SET_RUNTIME_PASS_TWO_2; - } do { - returnVar = statement(execute); - while (l->tk==';') l->match(';'); // skip empty statements - } while (l->tk!=LEX_EOF); + returnVar = execute_statement(execute); + while (t->tk==';') t->match(';'); // skip empty statements + } while (t->tk!=LEX_EOF); } catch (CScriptException *e) { - delete l; - l = oldLex; + delete t; + t = oldTokenizer; throw e; } - delete l; - l = oldLex; - RESTORE_RUNTIME_PASS; - - this->scopes.push_back(scEvalScope); // restore Scopes; + delete t; + t = oldTokenizer; + + _scopes.push_back(scEvalScope); // restore Scopes; if(returnVar) - c->setReturnVar(returnVar->var); + c->setReturnVar(returnVar->getVarPtr()); // check of exceptions int exceptionState = runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE); @@ -3163,8 +4282,98 @@ void CTinyJS::scEval(CScriptVar *c, void *data) { if(exceptionState) throw new CScriptEvalException(exceptionState); } catch (CScriptException *e) { - scopes.push_back(scEvalScope); // restore Scopes; - e->pos = -1; + _scopes.push_back(scEvalScope); // restore Scopes; + throw e; + } +} + +void CTinyJS::native_JSON_parse(const CFunctionsScopePtr &c, void *data) { + string Code = "�" + c->getParameter("text")->getString(); + + CScriptVarSmartLink returnVar; + CScriptTokenizer *oldTokenizer = t; t=0; + try { + CScriptTokenizer Tokenizer(Code.c_str(), "JSON.parse", 0, -1); + t = &Tokenizer; + bool execute = true; + returnVar = execute_literals(execute); + t->match(LEX_EOF); + } catch (CScriptException *e) { + t = oldTokenizer; throw e; } + t = oldTokenizer; + + if(returnVar) + c->setReturnVar(returnVar); + +// check of exceptions +// int exceptionState = runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE); +// runtimeFlags &= ~RUNTIME_LOOP_MASK; +// if(exceptionState) throw new CScriptEvalException(exceptionState); +} +void CTinyJS::native_Object_hasOwnProperty(const CFunctionsScopePtr &c, void *data) { + c->setReturnVar(c->newScriptVar(c->getParameter("this")->findChild(c->getParameter("prop")->getString()) != 0)); +} + +void CTinyJS::native_Function(const CFunctionsScopePtr &c, void *data) { + int length = c->getParameterLength(); + string params, body; + if(length>=1) + body = c->getParameter(length-1)->getString(); + if(length>=2) { + params = c->getParameter(0)->getString(); + for(int i=1; igetParameter(i)->getString()); + } + } + c->setReturnVar(parseFunctionsBodyFromString(params,body)); +} + +void CTinyJS::native_Function_call(const CFunctionsScopePtr &c, void *data) { + int length = c->getParameterLength(); + CScriptVarPtr Fnc = c->getParameter("this"); + CScriptVarPtr This = c->getParameter(0); + vector Params; + for(int i=1; igetParameter(i)); + bool execute = true; + callFunction(Fnc, Params, This, execute); +} +void CTinyJS::native_Function_apply(const CFunctionsScopePtr &c, void *data) { + CScriptVarPtr Fnc = c->getParameter("this"); + CScriptVarPtr This = c->getParameter(0); + CScriptVarPtr Array = c->getParameter(1); + CScriptVarLink *Length = Array->findChild("length"); + int length = Length ? Length->getValue()->getInt() : 0; + vector Params; + for(int i=0; ifindChild(int2string(i)); + if(value) Params.push_back(value->getValue()); + else Params.push_back(newScriptVar(Undefined)); + } + bool execute = true; + callFunction(Fnc, Params, This, execute); +} + +void CTinyJS::ClearLostVars(const CScriptVarPtr &extra/*=CScriptVarPtr()*/) { + int UniqueID = getUniqueID(); + root->setTempraryIDrecursive(UniqueID); + if(extra) extra->setTempraryIDrecursive(UniqueID); + CScriptVar *p = first; + while(p) + { + if(p->temporaryID != UniqueID) + { + CScriptVar *var = p; + var->ref(); + var->removeAllChildren(); + p = var->next; + var->unref(0); + } + else + p = p->next; + } } diff --git a/TinyJS.h b/TinyJS.h index 01b2f43..d469fd0 100755 --- a/TinyJS.h +++ b/TinyJS.h @@ -30,6 +30,14 @@ #include #include #include +#include "smart_pointer.h" +#include "pool_allocator.h" +#include +#include + +#ifndef ASSERT +# define ASSERT(X) assert(X) +#endif #ifndef __GNUC__ # define __attribute__(x) @@ -45,46 +53,63 @@ const int TINYJS_LOOP_MAX_ITERATIONS = 8192; enum LEX_TYPES { LEX_EOF = 0, - LEX_ID = 256, - LEX_INT, - LEX_FLOAT, - LEX_STR, - - LEX_EQUAL, +#define LEX_RELATIONS_1_BEGIN LEX_EQUAL + LEX_EQUAL = 256, LEX_TYPEEQUAL, LEX_NEQUAL, LEX_NTYPEEQUAL, +#define LEX_RELATIONS_1_END LEX_NTYPEEQUAL LEX_LEQUAL, - LEX_LSHIFT, - LEX_LSHIFTEQUAL, LEX_GEQUAL, +#define LEX_SHIFTS_BEGIN LEX_LSHIFT + LEX_LSHIFT, LEX_RSHIFT, - LEX_RSHIFTEQUAL, - LEX_PLUSEQUAL, - LEX_MINUSEQUAL, + LEX_RSHIFTU, // unsigned +#define LEX_SHIFTS_END LEX_RSHIFTU LEX_PLUSPLUS, LEX_MINUSMINUS, - LEX_ANDEQUAL, LEX_ANDAND, - LEX_OREQUAL, LEX_OROR, - LEX_XOREQUAL, + LEX_INT, + +#define LEX_ASSIGNMENTS_BEGIN LEX_PLUSEQUAL + LEX_PLUSEQUAL, + LEX_MINUSEQUAL, LEX_ASTERISKEQUAL, LEX_SLASHEQUAL, LEX_PERCENTEQUAL, + LEX_LSHIFTEQUAL, + LEX_RSHIFTEQUAL, + LEX_RSHIFTUEQUAL, // unsigned + LEX_ANDEQUAL, + LEX_OREQUAL, + LEX_XOREQUAL, +#define LEX_ASSIGNMENTS_END LEX_XOREQUAL + +#define LEX_TOKEN_STRING_BEGIN LEX_ID + LEX_ID, + LEX_STR, + LEX_T_LABEL, +#define LEX_TOKEN_STRING_END LEX_T_LABEL + + LEX_FLOAT, + // reserved words -#define LEX_R_LIST_START LEX_R_IF LEX_R_IF, LEX_R_ELSE, +#define LEX_TOKEN_LOOP_BEGIN LEX_R_DO LEX_R_DO, LEX_R_WHILE, LEX_R_FOR, +#define LEX_TOKEN_LOOP_END LEX_R_FOR LEX_R_IN, LEX_R_BREAK, LEX_R_CONTINUE, LEX_R_FUNCTION, LEX_R_RETURN, LEX_R_VAR, + LEX_R_LET, + LEX_R_WITH, LEX_R_TRUE, LEX_R_FALSE, LEX_R_NULL, @@ -104,41 +129,61 @@ enum LEX_TYPES { LEX_R_CASE, LEX_R_DEFAULT, - LEX_R_LIST_END /* always the last entry */ -}; + // special token +// LEX_T_FILE, + LEX_T_FUNCTION_OPERATOR, + LEX_T_GET, + LEX_T_SET, + LEX_T_FOR_IN, + LEX_T_FOR_EACH_IN, + + LEX_T_SKIP, + +}; +#define LEX_TOKEN_DATA_STRING(tk) ((LEX_TOKEN_STRING_BEGIN<= tk && tk <= LEX_TOKEN_STRING_END)) +#define LEX_TOKEN_DATA_FLOAT(tk) (tk==LEX_FLOAT) +#define LEX_TOKEN_DATA_FUNCTION(tk) (tk==LEX_R_FUNCTION || tk==LEX_T_FUNCTION_OPERATOR || tk==LEX_T_SET || tk==LEX_T_GET) +#define LEX_TOKEN_DATA_SIMPLE(tk) (!LEX_TOKEN_DATA_STRING(tk) && !LEX_TOKEN_DATA_FLOAT(tk) && !LEX_TOKEN_DATA_FUNCTION(tk)) +/* enum SCRIPTVAR_FLAGS { - SCRIPTVAR_UNDEFINED = 0, - SCRIPTVAR_FUNCTION = 1, - SCRIPTVAR_OBJECT = 2, - SCRIPTVAR_ARRAY = 4, - SCRIPTVAR_DOUBLE = 8, // floating point double - SCRIPTVAR_INTEGER = 16, // integer number - SCRIPTVAR_BOOLEAN = 32, // boolean - SCRIPTVAR_STRING = 64, // string - SCRIPTVAR_NULL = 128, // it seems null is its own data type - SCRIPTVAR_INFINITY = 256, // it seems infinity is its own data type - SCRIPTVAR_NAN = 512, // it seems NaN is its own data type - - SCRIPTVAR_NATIVE_FNC = 1024, // to specify this is a native function - SCRIPTVAR_NATIVE_MFNC = 2048, // to specify this is a native function from class->memberFunc - SCRIPTVAR_NUMERICMASK = SCRIPTVAR_NULL | - SCRIPTVAR_DOUBLE | - SCRIPTVAR_INTEGER | - SCRIPTVAR_BOOLEAN, - SCRIPTVAR_VARTYPEMASK = SCRIPTVAR_DOUBLE | - SCRIPTVAR_INTEGER | - SCRIPTVAR_BOOLEAN | - SCRIPTVAR_STRING | - SCRIPTVAR_FUNCTION | - SCRIPTVAR_OBJECT | - SCRIPTVAR_ARRAY | - SCRIPTVAR_NULL | - SCRIPTVAR_INFINITY | - SCRIPTVAR_NAN, + SCRIPTVAR_UNDEFINED = 0, + SCRIPTVAR_FUNCTION = 1<<0, + SCRIPTVAR_OBJECT = 1<<1, + SCRIPTVAR_ARRAY = 1<<2, + SCRIPTVAR_DOUBLE = 1<<3, // floating point double + SCRIPTVAR_INTEGER = 1<<4, // integer number + SCRIPTVAR_BOOLEAN = 1<<5, // boolean + SCRIPTVAR_STRING = 1<<6, // string + SCRIPTVAR_NULL = 1<<7, // it seems null is its own data type + SCRIPTVAR_INFINITY = 1<<8, // it seems infinity is its own data type + SCRIPTVAR_NAN = 1<<9, // it seems NaN is its own data type + SCRIPTVAR_ACCESSOR = 1<<10, // it seems an Object with get() and set() + + SCRIPTVAR_END_OF_TYPES = 1<<15, // it seems NaN is its own data type + + SCRIPTVAR_NATIVE_FNC = 1<<16, // to specify this is a native function + SCRIPTVAR_NATIVE_MFNC = 1<<17, // to specify this is a native function from class->memberFunc + + SCRIPTVAR_NUMBERMASK = SCRIPTVAR_DOUBLE | + SCRIPTVAR_INTEGER, + SCRIPTVAR_NUMERICMASK = SCRIPTVAR_NUMBERMASK | + SCRIPTVAR_NULL | + SCRIPTVAR_BOOLEAN, + + SCRIPTVAR_VARTYPEMASK = SCRIPTVAR_END_OF_TYPES - 1, - SCRIPTVAR_NATIVE = SCRIPTVAR_NATIVE_FNC | - SCRIPTVAR_NATIVE_MFNC, + SCRIPTVAR_NATIVE = SCRIPTVAR_NATIVE_FNC | + SCRIPTVAR_NATIVE_MFNC, +}; +*/ +enum SCRIPTVARLINK_FLAGS { + SCRIPTVARLINK_OWNED = 1<<0, + SCRIPTVARLINK_WRITABLE = 1<<1, + SCRIPTVARLINK_DELETABLE = 1<<2, + SCRIPTVARLINK_ENUMERABLE = 1<<3, + SCRIPTVARLINK_HIDDEN = 1<<4, + SCRIPTVARLINK_DEFAULT = SCRIPTVARLINK_WRITABLE | SCRIPTVARLINK_DELETABLE | SCRIPTVARLINK_ENUMERABLE, }; enum RUNTIME_FLAGS { RUNTIME_CANRETURN = 1<<0, @@ -157,9 +202,6 @@ enum RUNTIME_FLAGS { RUNTIME_THROW = 1<<7, RUNTIME_THROW_MASK = RUNTIME_CANTHROW | RUNTIME_THROW, - RUNTIME_PASS_1 = 1<<8, - RUNTIME_PASS_2 = 1<<9, - RUNTIME_PASS_MASK = RUNTIME_PASS_1 | RUNTIME_PASS_2, }; #define SAVE_RUNTIME_RETURN int old_return_runtimeFlags = runtimeFlags & RUNTIME_CANRETURN @@ -167,93 +209,331 @@ enum RUNTIME_FLAGS { #define SET_RUNTIME_CANRETURN runtimeFlags |= RUNTIME_CANRETURN #define IS_RUNTIME_CANRETURN ((runtimeFlags & RUNTIME_CANRETURN) == RUNTIME_CANRETURN) -#define SAVE_RUNTIME_PASS int old_pass_runtimeFlags = runtimeFlags & RUNTIME_PASS_MASK -#define RESTORE_RUNTIME_PASS runtimeFlags = (runtimeFlags & ~RUNTIME_PASS_MASK) | old_pass_runtimeFlags -#define SET_RUNTIME_PASS_SINGLE runtimeFlags = (runtimeFlags & ~RUNTIME_PASS_MASK) -#define IS_RUNTIME_PASS_SINGLE ((runtimeFlags & RUNTIME_PASS_MASK) == 0) -#define SET_RUNTIME_PASS_TWO_1 runtimeFlags = (runtimeFlags & ~RUNTIME_PASS_MASK) | RUNTIME_PASS_1 -#define IS_RUNTIME_PASS_TWO_1 ((runtimeFlags & RUNTIME_PASS_MASK) == RUNTIME_PASS_1) -#define SET_RUNTIME_PASS_TWO_2 runtimeFlags = (runtimeFlags & ~RUNTIME_PASS_MASK) | RUNTIME_PASS_2 -#define IS_RUNTIME_PASS_TWO_2 ((runtimeFlags & RUNTIME_PASS_MASK) == RUNTIME_PASS_2) - -#define TINYJS_RETURN_VAR "return" -#define TINYJS_LOKALE_VAR "__locale__" -#define TINYJS_ANONYMOUS_VAR "__anonymous__" -#define TINYJS_ARGUMENTS_VAR "arguments" -#define TINYJS_PROTOTYPE_CLASS "prototype" -#define TINYJS_TEMP_NAME "" -#define TINYJS_BLANK_DATA "" + +#define TINYJS_RETURN_VAR "return" +#define TINYJS_LOKALE_VAR "__locale__" +#define TINYJS_ANONYMOUS_VAR "__anonymous__" +#define TINYJS_ARGUMENTS_VAR "arguments" +#define TINYJS___PROTO___VAR "__proto__" +#define TINYJS_PROTOTYPE_CLASS "prototype" +#define TINYJS_FUNCTION_CLOSURE_VAR "__function_closure__" +#define TINYJS_SCOPE_PARENT_VAR "__scope_parent__" +#define TINYJS_SCOPE_WITH_VAR "__scope_with__" +#define TINYJS_ACCESSOR_GET_VAR "__accessor_get__" +#define TINYJS_ACCESSOR_SET_VAR "__accessor_set__" +#define TINYJS_TEMP_NAME "" +#define TINYJS_BLANK_DATA "" +#define TINYJS_NEGATIVE_INFINITY_DATA "-1" +#define TINYJS_POSITIVE_INFINITY_DATA "+1" + +typedef std::vector STRING_VECTOR_t; +typedef STRING_VECTOR_t::iterator STRING_VECTOR_it; /// convert the given string into a quoted string suitable for javascript std::string getJSString(const std::string &str); +/// convert the given int into a string +std::string int2string(int intData); +/// convert the given double into a string +std::string float2string(const double &floatData); + +// ----------------------------------------------------------------------------------- CScriptSmartPointer +template +class CScriptSmartPointer +{ +public: + CScriptSmartPointer() : pointer(0) {} + explicit CScriptSmartPointer(T *Pointer) : pointer(Pointer ? new p(Pointer) : 0) {} + CScriptSmartPointer(const CScriptSmartPointer &Copy) : pointer(0) {*this = Copy;} + CScriptSmartPointer &operator=(const CScriptSmartPointer &Copy) + { + if(this == &Copy) return *this; + if(pointer) pointer->unref(); + if(Copy.pointer) + pointer = Copy.pointer->ref(); + else + pointer = 0; + return *this; + } + ~CScriptSmartPointer() { release(); } + void release() { if(pointer) pointer->unref(); pointer = 0;} + T *operator->() { return operator T*(); } + operator T*() { return pointer ? pointer->pointer : 0; } +private: + class p + { + public: + p(T *Pointer) : pointer(Pointer), refs(1) {} + p *ref() { refs++; return this; } + void unref() { if(--refs == 0) { delete pointer; delete this; } } + T *pointer; + int refs; + } *pointer; +}; class CScriptException { public: std::string text; - int pos; - CScriptException(const std::string &exceptionText, int Pos=-1); +// int pos; + std::string file; + int line; + int column; +// CScriptException(const std::string &exceptionText, int Pos=-1); + CScriptException(const std::string &Text, const std::string &File, int Line=-1, int Column=-1) : + text(Text), file(File), line(Line), column(Column){} + CScriptException(const std::string &Text, const char *File="", int Line=-1, int Column=-1) : + text(Text), file(File), line(Line), column(Column){} }; +// ----------------------------------------------------------------------------------- CSCRIPTLEX class CScriptLex { public: - CScriptLex(const std::string &input); - CScriptLex(CScriptLex *owner, int startChar, int endChar); - ~CScriptLex(void); + CScriptLex(const char *Code, const std::string &File="", int Line=0, int Column=0); - char currCh, nextCh; int tk; ///< The type of the token that we have - int tokenStart; ///< Position in the data at the beginning of the token we have here - int tokenEnd; ///< Position in the data at the last character of the token we have here - int tokenLastEnd; ///< Position in the data at the last character of the last token + const char *tokenStart; std::string tkStr; ///< Data contained in the token we have here + void check(int expected_tk); ///< Lexical check wotsit void match(int expected_tk); ///< Lexical match wotsit static std::string getTokenStr(int token); ///< Get the string representation of the given token - void reset(int toPos=0); ///< Reset this lex so we can start again + void reset(const char *toPos, int line, const char *LineStart); ///< Reset this lex so we can start again - std::string getSubString(int pos); ///< Return a sub-string from the given position up until right now - CScriptLex *getSubLex(int lastPosition); ///< Return a sub-lexer from the given position up until right now - int getDataPos(); - std::string getPosition(int pos=-1); ///< Return a string representing the position in lines and columns of the character pos given - -protected: - /* When we go into a loop, we use getSubLex to get a lexer for just the sub-part of the - relevant string. This doesn't re-allocate and copy the string, but instead copies - the data pointer and sets dataOwned to false, and dataStart/dataEnd to the relevant things. */ - char *data; ///< Data string to get tokens from - int dataStart, dataEnd; ///< Start and end position in data string - bool dataOwned; ///< Do we own this data string? + std::string currentFile; + int currentLine; + const char *currentLineStart; + int currentColumn() { return tokenStart-currentLineStart; } - int dataPos; ///< Position in data (we CAN go past the end of the string here) +private: +// const std::string &data; + const char *data; + const char *dataPos; + char currCh, nextCh; void getNextCh(); void getNextToken(); ///< Get the text token from our text string }; +// ----------------------------------------------------------------------------------- CSCRIPTTOKEN +class CScriptToken; +typedef std::vector TOKEN_VECT; +typedef std::vector::iterator TOKEN_VECT_it; +class CScriptTokenData +{ +public: + virtual ~CScriptTokenData() {} + void ref() { refs++; } + void unref() { if(--refs == 0) delete this; } +protected: + CScriptTokenData() : refs(0){} +private: + int refs; +}; +class CScriptTokenDataString : public fixed_size_object, public CScriptTokenData { +public: + CScriptTokenDataString(const std::string &String) : tokenStr(String) {} + std::string tokenStr; +private: +}; +class CScriptTokenDataFnc : public fixed_size_object, public CScriptTokenData { +public: + std::string file; + int line; + std::string name; + std::vector parameter; + TOKEN_VECT body; +private: +}; + +class CScriptTokenizer; +/* + a Token needs 8 Byte + 2 Bytes for the Row-Position of the Token + 2 Bytes for the Token self + and + 4 Bytes for special Datas in an union + e.g. an int for interger-literals + or pointer for double-literals, + for string-literals or for functions +*/ +class CScriptToken : public fixed_size_object +{ +public: + CScriptToken() : line(0), column(0), token(0) {} + CScriptToken(CScriptLex *l, int Match=-1); + CScriptToken(uint16_t Tk, int IntData=0); + CScriptToken(uint16_t Tk, const std::string &TkStr); + CScriptToken(const CScriptToken &Copy) : token(0) { *this = Copy; } + CScriptToken &operator =(const CScriptToken &Copy); + ~CScriptToken() { clear(); } + + int &Int() { ASSERT(LEX_TOKEN_DATA_SIMPLE(token)); return intData; } + std::string &String() { ASSERT(LEX_TOKEN_DATA_STRING(token)); return stringData->tokenStr; } + double &Float() { ASSERT(LEX_TOKEN_DATA_FLOAT(token)); return *floatData; } + CScriptTokenDataFnc &Fnc() { ASSERT(LEX_TOKEN_DATA_FUNCTION(token)); return *fncData; } + uint16_t line; + uint16_t column; + uint16_t token; + void print(std::string &indent); + std::string getParsableString(const std::string Indent=""); + std::string getParsableString(std::string &indentString, int &newln, const std::string &indent); ///< newln ==> 1=inject indentString, 2=inject \n, 3=last='{' +private: + + void clear(); + union { + int intData; + double *floatData; + CScriptTokenDataString *stringData; + CScriptTokenDataFnc *fncData; + }; +}; + +// ----------------------------------------------------------------------------------- CSCRIPTTOKENIZER +/* + the tokenizer converts the code in an Vector with Tokens +*/ +class CScriptTokenizer +{ +public: + struct ScriptTokenPosition { + ScriptTokenPosition(TOKEN_VECT *Tokens) : tokens(Tokens), pos(tokens->begin())/*, currentLine(0)*//*, currentColumn(0)*/ {} + bool operator ==(const ScriptTokenPosition &eq) { return pos == eq.pos; } + ScriptTokenPosition &operator =(const ScriptTokenPosition ©) { + tokens=copy.tokens; pos=copy.pos; +// currentLine=copy.currentLine; + return *this; + } + TOKEN_VECT *tokens; + TOKEN_VECT::iterator pos; + int currentLine() { return pos->line; } + int currentColumn() { return pos->column; } + }; + CScriptTokenizer(); + CScriptTokenizer(CScriptLex &Lexer); + CScriptTokenizer(const char *Code, const std::string &File="", int Line=0, int Column=0); + void tokenizeCode(CScriptLex &Lexer); + + CScriptToken &getToken() { return *(tokenScopeStack.back().pos); } + void getNextToken(); + bool check(int ExpectedToken); + void match(int ExpectedToken); + void pushTokenScope(TOKEN_VECT &Tokens); + ScriptTokenPosition &getPos() { return tokenScopeStack.back(); } + void setPos(ScriptTokenPosition &TokenPos); + ScriptTokenPosition &getPrevPos() { return prevPos; } + void skip(int Tokens); + int tk; // current Token + std::string currentFile; + int currentLine() { return getPos().currentLine();} + int currentColumn() { return getPos().currentColumn();} + std::string &tkStr() { return getToken().String(); } +private: + void tokenizeTry(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); + void tokenizeSwitch(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); + void tokenizeWith(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); + void tokenizeWhile(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); + void tokenizeDo(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); + void tokenizeIf(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); + void tokenizeForIn(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); + void tokenizeFor(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); + void tokenizeFunction(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); + void tokenizeBlock(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); + void tokenizeLet(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); + void tokenizeVar(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); + void tokenizeStatement(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); + void tokenizeToken(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); + int pushToken(TOKEN_VECT &Tokens, int Match=-1); + void throwTokenNotExpected(); + CScriptLex *l; + TOKEN_VECT tokens; + ScriptTokenPosition prevPos; + std::vector tokenScopeStack; +}; +////////////////////////////////////////////////////////////////////////// CScriptVarPointer + class CScriptVar; +class CScriptVarLink; +class CScriptVarSmartLink; +template +class CScriptVarPointer { +public: + CScriptVarPointer() : var(0) {} ///< explizit 0-Pointer + CScriptVarPointer(CScriptVar *Var) : var(dynamic_cast(Var)) { if(Var) ASSERT(var); if(var) var->ref(); } + CScriptVarPointer(const CScriptVarLink *Link); + CScriptVarPointer(const CScriptVarSmartLink &Link); + //: var(dynamic_cast(Var)) { if(Var) ASSERT(var); if(var) var->ref(); } + ~CScriptVarPointer() { if(var) var->unref(0); } + CScriptVarPointer(const CScriptVarPointer &VarPtr) : var(0) { *this = VarPtr; } + CScriptVarPointer& operator=(const CScriptVarPointer &VarPtr) { + if(var != VarPtr.var) { + if(var) var->unref(0); + var = VarPtr.var; if(var) var->ref(); + } + return *this; + } + template + operator CScriptVarPointer() const { return CScriptVarPointer(var); } + operator bool() const { return var!=0; } + + template + bool operator ==(const CScriptVarPointer &Other) const { return (CScriptVar*)var == (CScriptVar*)(Other.getVar()); } + template + bool operator !=(const CScriptVarPointer &Other) const { return (CScriptVar*)var != (CScriptVar*)(Other.getVar()); } + + C * operator ->() const { ASSERT(var); return var; } + CScriptVar *getVar() const { ASSERT(var); return var; } +private: + C *var; +}; -typedef void (*JSCallback)(CScriptVar *var, void *userdata); +typedef CScriptVarPointer CScriptVarPtr; -class CScriptVarLink +// ----------------------------------------------------------------------------------- CSCRIPTVARLINK +class CScriptVar; +class CScriptVarLink : public fixed_size_object { public: std::string name; +// const std::string &Name() { return name; } +// void SetName(const std::string &Name) { name = Name; } +private: CScriptVar *var; +public: + CScriptVarPtr getValue(); +private: +// CScriptVar *get() { return var; } +public: + CScriptVarPtr getVarPtr() { return var; } CScriptVar *owner; // pointer to the owner CScriptVar - struct { - bool owned :1; - bool dontDelete :1; - bool dontEnumerable :1; - bool hidden :1; - }; - CScriptVarLink(CScriptVar *var, const std::string &name = TINYJS_TEMP_NAME); + uint32_t flags; + CScriptVarLink(const CScriptVarPtr &var, const std::string &name = TINYJS_TEMP_NAME, int flags = SCRIPTVARLINK_DEFAULT); CScriptVarLink(const CScriptVarLink &link); ///< Copy constructor ~CScriptVarLink(); - void replaceWith(CScriptVar *newVar); ///< Replace the Variable pointed to + void replaceWith(const CScriptVarPtr &newVar); ///< Replace the Variable pointed to void replaceWith(CScriptVarLink *newVar); ///< Replace the Variable pointed to (just dereferences) +// void SetOwner(CScriptVar *Owner) { owner = Owner; } +// void SetOwner(CScriptVar *Owner, bool Owned) { owner = Owner; SetOwned(Owned); } +// bool IsOwned() { return owner != 0; } + bool IsOwned() { return (flags & SCRIPTVARLINK_OWNED) != 0; } + void SetOwned(bool On) { On ? (flags |= SCRIPTVARLINK_OWNED) : (flags &= ~SCRIPTVARLINK_OWNED); } + + bool IsWritable() { return (flags & SCRIPTVARLINK_WRITABLE) != 0; } + void SetWritable(bool On) { On ? (flags |= SCRIPTVARLINK_WRITABLE) : (flags &= ~SCRIPTVARLINK_DELETABLE); } + bool IsDeletable() { return (flags & SCRIPTVARLINK_DELETABLE) != 0; } + void SetDeletable(bool On) { On ? (flags |= SCRIPTVARLINK_DELETABLE) : (flags &= ~SCRIPTVARLINK_DELETABLE); } + bool IsEnumerable() { return (flags & SCRIPTVARLINK_ENUMERABLE) != 0; } + void SetEnumerable(bool On) { On ? (flags |= SCRIPTVARLINK_ENUMERABLE) : (flags &= ~SCRIPTVARLINK_ENUMERABLE); } + bool IsHidden() { return (flags & SCRIPTVARLINK_HIDDEN) != 0; } + void SetHidden(bool On) { On ? (flags |= SCRIPTVARLINK_HIDDEN) : (flags &= ~SCRIPTVARLINK_HIDDEN); } + CScriptVar * operator ->() const { return var; } +// friend class CScriptVar; +// friend class CScriptVarSmartLink; +// friend class CScriptVarObject; }; +template +CScriptVarPointer::CScriptVarPointer(const CScriptVarLink *Link) : var(0) { if(Link) { var = dynamic_cast(Link->operator->()); ASSERT(var); } if(var) var->ref(); } +// ----------------------------------------------------------------------------------- RECURSION typedef std::vector RECURSION_VECT; typedef std::set RECURSION_SET; class RECURSION_SET_VAR @@ -267,180 +547,631 @@ class RECURSION_SET_VAR }; -class NativeFncBase; -typedef std::map SCRIPTVAR_CHILDS; -/// Variable class (containing a doubly-linked list of children) -class CScriptVar -{ +// ----------------------------------------------------------------------------------- CSCRIPTVAR +typedef std::vector SCRIPTVAR_CHILDS_t; +typedef SCRIPTVAR_CHILDS_t::iterator SCRIPTVAR_CHILDS_it; +typedef SCRIPTVAR_CHILDS_t::const_iterator SCRIPTVAR_CHILDS_cit; +class CTinyJS; +class CScriptVarScopeFnc; +typedef CScriptVarPointer CFunctionsScopePtr; +typedef void (*JSCallback)(const CFunctionsScopePtr &var, void *userdata); + + +class CScriptVar : public fixed_size_object { +protected: + CScriptVar(CTinyJS *Context, const CScriptVarPtr &Prototype=CScriptVarPtr()); ///< Create + CScriptVar(const CScriptVar &Copy); ///< Copy protected -> use clone for public +private: + CScriptVar & operator=(const CScriptVar &Copy); ///< private -> no assignment-Copy public: - CScriptVar(); ///< Create undefined - CScriptVar(const std::string &varData, int varFlags); ///< User defined - CScriptVar(const std::string &str); ///< Create a string - CScriptVar(const char *str); ///< Create a string - CScriptVar(double varData); - CScriptVar(int val); - CScriptVar(bool val); - ~CScriptVar(void); - - CScriptVar *getReturnVar(); ///< If this is a function, get the result value (for use by native functions) - void setReturnVar(CScriptVar *var); ///< Set the result value. Use this when setting complex return data as it avoids a deepCopy() - CScriptVar *getParameter(const std::string &name); ///< If this is a function, get the parameter with the given name (for use by native functions) - CScriptVar *getParameter(int Idx); ///< If this is a function, get the parameter with the given index (for use by native functions) - int getParameterLength(); ///< If this is a function, get the count of parameters + virtual ~CScriptVar(void); + virtual CScriptVarPtr clone()=0; + + /// Type + virtual bool isInt(); // {return false;} + virtual bool isBool(); // {return false;} + virtual bool isDouble(); // {return false;} + virtual bool isString(); // {return false;} + virtual bool isNumber(); // {return false;} + virtual bool isNumeric(); // {return false;} + virtual bool isFunction(); // {return false;} + virtual bool isObject(); // {return false;} + virtual bool isArray(); // {return false;} + virtual bool isNative(); // {return false;} + virtual bool isUndefined();// {return false;} + virtual bool isNull(); // {return false;} + virtual bool isNaN(); // {return false;} + virtual int isInfinity(); // { return 0; } ///< +1==POSITIVE_INFINITY, -1==NEGATIVE_INFINITY, 0==is not an InfinityVar + virtual bool isAccessor(); // {return false;} + + bool isBasic() { return Childs.empty(); } ///< Is this *not* an array/object/etc + + + /// Value + virtual int getInt(); ///< return 0 + virtual bool getBool(); ///< return false + virtual double getDouble(); ///< return 0.0 + virtual CScriptTokenDataFnc *getFunctionData(); ///< { return 0; } + virtual std::string getString(); ///< return "" + + virtual std::string getParsableString(const std::string &indentString, const std::string &indent); ///< get Data as a parsable javascript string + virtual std::string getVarType(); + virtual CScriptVarLink *getNumericVar(); ///< returns an Integer, a Double, an Infinity or a NaN + + /// find CScriptVarLink *findChild(const std::string &childName); ///< Tries to find a child with the given name, may return 0 - CScriptVarLink *findChildOrCreate(const std::string &childName, int varFlags=SCRIPTVAR_UNDEFINED); ///< Tries to find a child with the given name, or will create it with the given flags + CScriptVarLink *findChildByPath(const std::string &path); ///< Tries to find a child with the given path (separated by dots) + CScriptVarLink *findChildOrCreate(const std::string &childName/*, int varFlags=SCRIPTVAR_UNDEFINED*/); ///< Tries to find a child with the given name, or will create it with the given flags CScriptVarLink *findChildOrCreateByPath(const std::string &path); ///< Tries to find a child with the given path (separated by dots) - CScriptVarLink *addChild(const std::string &childName, CScriptVar *child=NULL); - CScriptVarLink *addChildNoDup(const std::string &childName, CScriptVar *child=NULL); ///< add a child overwriting any with the same name + + /// add & remove + CScriptVarLink *addChild(const std::string &childName, const CScriptVarPtr &child, int linkFlags = SCRIPTVARLINK_DEFAULT); + CScriptVarLink *addChildNoDup(const std::string &childName, const CScriptVarPtr &child, int linkFlags = SCRIPTVARLINK_DEFAULT); ///< add a child overwriting any with the same name bool removeLink(CScriptVarLink *&link); ///< Remove a specific link (this is faster than finding via a child) void removeAllChildren(); - CScriptVar *getArrayIndex(int idx); ///< The the value at an array index - void setArrayIndex(int idx, CScriptVar *value); ///< Set the value at an array index - int getArrayLength(); ///< If this is an array, return the number of items in it (else 0) - int getChildren() { return Childs.size(); } ///< Get the number of children - int getInt(); - bool getBool(); - double getDouble(); - const std::string &getString(); - std::string getParsableString(); ///< get Data as a parsable javascript string - std::string getVarType(); ///< get Data as a parsable javascript string - void setInt(int num); - void setBool(bool val); - void setDouble(double val); - void setString(const std::string &str); - void setUndefined(); - - bool isInt() { return (flags&SCRIPTVAR_INTEGER)!=0; } - bool isBool() { return (flags&SCRIPTVAR_BOOLEAN)!=0; } - bool isDouble() { return (flags&SCRIPTVAR_DOUBLE)!=0; } - bool isString() { return (flags&SCRIPTVAR_STRING)!=0; } - bool isNumeric() { return (flags&SCRIPTVAR_NUMERICMASK)!=0; } - bool isFunction() { return (flags&SCRIPTVAR_FUNCTION)!=0; } - bool isObject() { return (flags&SCRIPTVAR_OBJECT)!=0; } - bool isArray() { return (flags&SCRIPTVAR_ARRAY)!=0; } - bool isNative() { return (flags&SCRIPTVAR_NATIVE)!=0; } - bool isNative_ClassMemberFnc() { return (flags&SCRIPTVAR_NATIVE_MFNC)!=0; } - bool isUndefined() { return (flags & SCRIPTVAR_VARTYPEMASK) == SCRIPTVAR_UNDEFINED; } - bool isNull() { return (flags & SCRIPTVAR_NULL)!=0; } - bool isNaN() { return (flags & SCRIPTVAR_NAN)!=0; } - bool isInfinity() { return (flags&SCRIPTVAR_INFINITY)!=0; } - bool isBasic() { return Childs.empty(); } ///< Is this *not* an array/object/etc - CScriptVar *mathsOp(CScriptVar *b, int op); ///< do a maths op with another script variable - void copyValue(CScriptVar *val); ///< copy the value from the value given - CScriptVar *deepCopy(); ///< deep copy this node and return the result + /// funcions for FUNCTION + void setReturnVar(const CScriptVarPtr &var); ///< Set the result value. Use this when setting complex return data as it avoids a deepCopy() + CScriptVarPtr getParameter(const std::string &name); ///< If this is a function, get the parameter with the given name (for use by native functions) + CScriptVarPtr getParameter(int Idx); ///< If this is a function, get the parameter with the given index (for use by native functions) + int getParameterLength(); ///< If this is a function, get the count of parameters + + + /// ARRAY + CScriptVarPtr getArrayIndex(int idx); ///< The the value at an array index + void setArrayIndex(int idx, const CScriptVarPtr &value); ///< Set the value at an array index + int getArrayLength(); ///< If this is an array, return the number of items in it (else 0) + + ////////////////////////////////////////////////////////////////////////// + int getChildren() { return Childs.size(); } ///< Get the number of children + CTinyJS *getContext() { return context; } + CScriptVarPtr mathsOp(const CScriptVarPtr &b, int op); ///< do a maths op with another script variable void trace(const std::string &name = ""); ///< Dump out the contents of this using trace void trace(std::string &indentStr, const std::string &name = ""); ///< Dump out the contents of this using trace std::string getFlagsAsString(); ///< For debugging - just dump a string version of the flags - void getJSON(std::ostringstream &destination, const std::string linePrefix=""); ///< Write out all the JS code needed to recreate this script variable to the stream (as JSON) - void setCallback(JSCallback callback, void *userdata); - void setCallback(NativeFncBase *callback, void *userdata); +// void getJSON(std::ostringstream &destination, const std::string linePrefix=""); ///< Write out all the JS code needed to recreate this script variable to the stream (as JSON) - SCRIPTVAR_CHILDS Childs; + SCRIPTVAR_CHILDS_t Childs; /// For memory management/garbage collection void recoursionCheck(CScriptVar *Owner=0); CScriptVar *ref(); ///< Add reference to this variable void unref(CScriptVar* Owner); ///< Remove a reference, and delete this variable if required int getRefs(); ///< Get the number of references to this script variable + template + operator T *(){ T* ret = dynamic_cast(this); ASSERT(ret!=0); return ret; } + template + T *get(){ T* ret = dynamic_cast(this); ASSERT(ret!=0); return ret; } + + template + CScriptVarPtr newScriptVar(T t); // { return ::newScriptVar(context, t); } + void setTempraryID(int ID) { temporaryID = ID; } + void setTempraryIDrecursive(int ID); + int getTempraryID() { return temporaryID; } private: RECURSION_SET_VAR *unrefInternal(); protected: + CTinyJS *context; int refs; ///< The number of references held to this - used for garbage collection int internalRefs; - CScriptVarLink *__proto__; - std::string data; ///< The contents of this variable if it is a string - long intData; ///< The contents of this variable if it is an int - double doubleData; ///< The contents of this variable if it is a double - int flags; ///< the flags determine the type of the variable - int/double/string/etc - union - { - JSCallback jsCallback; ///< Callback for native functions - NativeFncBase *jsCallbackClass; ///< Wrapper for Class-Member-Functions as Callback for native functions - }; - void *jsCallbackUserData; ///< user data passed as second argument to native functions - void init(); ///< initialisation of data members - - /** Copy the basic data and flags from the variable given, with no - * children. Should be used internally only - by copyValue and deepCopy */ - void copySimpleData(CScriptVar *val); void recoursionCheck(RECURSION_VECT &recursionPath); int recursionFlag; RECURSION_SET_VAR *recursionSet; - friend class CTinyJS; + CScriptVar *prev; +public: + CScriptVar *next; + int temporaryID; }; +////////////////////////////////////////////////////////////////////////// +#define define_dummy_t(t1) struct t1##_t{}; extern t1##_t t1 +#define declare_dummy_t(t1) t1##_t t1 +#define define_newScriptVar_Fnc(t1, ...) CScriptVar##t1##Ptr newScriptVar(__VA_ARGS__) +#define define_ScriptVarPtr_Type(t1) class CScriptVar##t1; typedef CScriptVarPointer CScriptVar##t1##Ptr -class NativeFncBase -{ + +////////////////////////////////////////////////////////////////////////// CScriptVarObject + +define_dummy_t(Object); +define_ScriptVarPtr_Type(Object); + +class CScriptVarObject : public CScriptVar { +protected: + CScriptVarObject(CTinyJS *Context, const CScriptVarPtr &Prototype=CScriptVarPtr()) : CScriptVar(Context, Prototype) {} + CScriptVarObject(const CScriptVarObject &Copy) : CScriptVar(Copy) {} ///< Copy protected -> use clone for public public: - virtual void operator()(CScriptVar *v, void *userdata)=0; + virtual ~CScriptVarObject(); + virtual CScriptVarPtr clone(); + virtual bool isObject(); // { return true; } + + virtual std::string getString(); // { return "[ Object ]"; }; + virtual std::string getParsableString(const std::string &indentString, const std::string &indent); + virtual std::string getVarType(); // { return "object"; } +private: + + friend define_newScriptVar_Fnc(Object, CTinyJS* Context, Object_t); + friend define_newScriptVar_Fnc(Object, CTinyJS* Context, Object_t, const CScriptVarPtr &); }; -template -class NativeFnc : public NativeFncBase -{ +inline define_newScriptVar_Fnc(Object, CTinyJS* Context, Object_t) { return new CScriptVarObject(Context); } +inline define_newScriptVar_Fnc(Object, CTinyJS* Context, Object_t, const CScriptVarPtr &Prototype) { return new CScriptVarObject(Context, Prototype); } + + +////////////////////////////////////////////////////////////////////////// CScriptVarAccessor + +define_dummy_t(Accessor); +define_ScriptVarPtr_Type(Accessor); + +class CScriptVarAccessor : public CScriptVar { +protected: + CScriptVarAccessor(CTinyJS *Context) : CScriptVar(Context) {} + CScriptVarAccessor(const CScriptVarAccessor &Copy) : CScriptVar(Copy) {} ///< Copy protected -> use clone for public public: - NativeFnc(native *ClassPtr, void (native::*ClassFnc)(CScriptVar *, void *)) - : classPtr(ClassPtr), classFnc(ClassFnc){} - void operator()(CScriptVar *v, void *userdata) - { - (classPtr->*classFnc)(v, userdata); - } + virtual ~CScriptVarAccessor(); + virtual CScriptVarPtr clone(); + virtual bool isAccessor(); // { return true; } + + virtual std::string getString(); // { return "[ Object ]"; }; + virtual std::string getParsableString(const std::string &indentString, const std::string &indent); + virtual std::string getVarType(); // { return "object"; } + + CScriptVarPtr getValue(); + + friend define_newScriptVar_Fnc(Accessor, CTinyJS* Context, Accessor_t); + +}; +inline define_newScriptVar_Fnc(Accessor, CTinyJS* Context, Accessor_t) { return new CScriptVarAccessor(Context); } + + +////////////////////////////////////////////////////////////////////////// CScriptVarArray + +define_dummy_t(Array); +define_ScriptVarPtr_Type(Array); +class CScriptVarArray : public CScriptVar { +protected: + CScriptVarArray(CTinyJS *Context); + CScriptVarArray(const CScriptVarArray &Copy) : CScriptVar(Copy) {} ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarArray(); + virtual CScriptVarPtr clone(); + virtual bool isArray(); // { return true; } + virtual std::string getString(); + virtual std::string getVarType(); // { return "object"; } + virtual std::string getParsableString(const std::string &indentString, const std::string &indent); + friend define_newScriptVar_Fnc(Array, CTinyJS* Context, Array_t); +private: + void native_Length(const CFunctionsScopePtr &c, void *data); +}; +inline define_newScriptVar_Fnc(Array, CTinyJS* Context, Array_t) { return new CScriptVarArray(Context); } + + +////////////////////////////////////////////////////////////////////////// CScriptVarNull + +define_dummy_t(Null); +define_ScriptVarPtr_Type(Null); +class CScriptVarNull : public CScriptVar { +protected: + CScriptVarNull(CTinyJS *Context) : CScriptVar(Context) {} + CScriptVarNull(const CScriptVarNull &Copy) : CScriptVar(Copy) {} ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarNull(); + virtual CScriptVarPtr clone(); + virtual bool isNull(); // { return true; } + virtual std::string getString(); // { return "null"; }; + virtual std::string getVarType(); // { return "null"; } + friend define_newScriptVar_Fnc(Null, CTinyJS* Context, Null_t); +}; +inline define_newScriptVar_Fnc(Null, CTinyJS* Context, Null_t) { return new CScriptVarNull(Context); } + + +////////////////////////////////////////////////////////////////////////// CScriptVarUndefined + +define_dummy_t(Undefined); +define_ScriptVarPtr_Type(Undefined); +class CScriptVarUndefined : public CScriptVar { +protected: + CScriptVarUndefined(CTinyJS *Context) : CScriptVar(Context) {} + CScriptVarUndefined(const CScriptVarUndefined &Copy) : CScriptVar(Copy) {} ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarUndefined(); + virtual CScriptVarPtr clone(); + virtual bool isUndefined(); // { return true; } + virtual std::string getString(); // { return "undefined"; }; + virtual std::string getVarType(); // { return "undefined"; } + friend define_newScriptVar_Fnc(Undefined, CTinyJS* Context, Undefined_t); +}; +inline define_newScriptVar_Fnc(Undefined, CTinyJS* Context, Undefined_t) { return new CScriptVarUndefined(Context); } + + +////////////////////////////////////////////////////////////////////////// CScriptVarNaN + +define_dummy_t(NaN); +define_ScriptVarPtr_Type(NaN); +class CScriptVarNaN : public CScriptVar { +protected: + CScriptVarNaN(CTinyJS *Context) : CScriptVar(Context) {} + CScriptVarNaN(const CScriptVarNaN &Copy) : CScriptVar(Copy) {} ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarNaN(); + virtual CScriptVarPtr clone(); + virtual bool isNaN();// { return true; } + virtual std::string getString(); // { return "NaN"; }; + virtual std::string getVarType(); // { return "number"; } + friend define_newScriptVar_Fnc(NaN, CTinyJS* Context, NaN_t); +}; +inline define_newScriptVar_Fnc(NaN, CTinyJS* Context, NaN_t) { return new CScriptVarNaN(Context); } + + +////////////////////////////////////////////////////////////////////////// CScriptVarString + +define_ScriptVarPtr_Type(String); +class CScriptVarString : public CScriptVar { +protected: + CScriptVarString(CTinyJS *Context, const std::string &Data); + CScriptVarString(const CScriptVarString &Copy) : CScriptVar(Copy), data(Copy.data) {} ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarString(); + virtual CScriptVarPtr clone(); + virtual bool isString(); // { return true; } + virtual int getInt(); // {return strtol(data.c_str(),0,0); } + virtual bool getBool(); // {return data.length()!=0;} + virtual double getDouble(); // {return strtod(data.c_str(),0);} + virtual std::string getString(); // { return data; } + virtual std::string getVarType(); // { return "string"; } + virtual std::string getParsableString(); // { return getJSString(data); } +protected: + std::string data; +private: + void native_Length(const CFunctionsScopePtr &c, void *data); + + friend define_newScriptVar_Fnc(String, CTinyJS* Context, const std::string &); + friend define_newScriptVar_Fnc(String, CTinyJS* Context, const char *); +}; +inline define_newScriptVar_Fnc(String, CTinyJS* Context, const std::string &Obj) { return new CScriptVarString(Context, Obj); } +inline define_newScriptVar_Fnc(String, CTinyJS* Context, const char *Obj) { return new CScriptVarString(Context, Obj); } + + +////////////////////////////////////////////////////////////////////////// CScriptVarIntegerBase + +class CScriptVarIntegerBase : public CScriptVar { +protected: + CScriptVarIntegerBase(CTinyJS *Context, int Data); + CScriptVarIntegerBase(const CScriptVarIntegerBase &Copy) : CScriptVar(Copy), data(Copy.data) {} ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarIntegerBase(); + virtual int getInt(); // {return data; } + virtual bool getBool(); // {return data!=0;} + virtual double getDouble(); // {return data;} + virtual std::string getString(); // {return int2string(data);} + virtual std::string getVarType(); // { return "number"; } +protected: + int data; +}; + + +////////////////////////////////////////////////////////////////////////// CScriptVarInteger + +define_ScriptVarPtr_Type(Integer); +class CScriptVarInteger : public CScriptVarIntegerBase { +protected: + CScriptVarInteger(CTinyJS *Context, int Data) : CScriptVarIntegerBase(Context, Data) {} + CScriptVarInteger(const CScriptVarInteger &Copy) : CScriptVarIntegerBase(Copy) {} ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarInteger(); + virtual CScriptVarPtr clone(); + virtual bool isNumber(); // { return true; } + virtual bool isInt(); // { return true; } + friend define_newScriptVar_Fnc(Integer, CTinyJS* Context, int); +}; +inline define_newScriptVar_Fnc(Integer, CTinyJS* Context, int Obj) { return new CScriptVarInteger(Context, Obj); } + + +////////////////////////////////////////////////////////////////////////// CScriptVarBool + +define_ScriptVarPtr_Type(Bool); +class CScriptVarBool : public CScriptVarIntegerBase { +protected: + CScriptVarBool(CTinyJS *Context, bool Data) : CScriptVarIntegerBase(Context, Data?1:0) {} + CScriptVarBool(const CScriptVarBool &Copy) : CScriptVarIntegerBase(Copy) {} ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarBool(); + virtual CScriptVarPtr clone(); + virtual bool isBool(); // { return true; } + virtual std::string getString(); // {return data!=0?"true":"false";} + virtual std::string getVarType(); // { return "boolean"; } + friend define_newScriptVar_Fnc(Bool, CTinyJS* Context, bool); +}; +inline define_newScriptVar_Fnc(Bool, CTinyJS* Context, bool Obj) { return new CScriptVarBool(Context, Obj); } + + +////////////////////////////////////////////////////////////////////////// CScriptVarInfinity + +struct Infinity{Infinity(int Sig=1):sig(Sig){} int sig; }; +extern Infinity InfinityPositive; +extern Infinity InfinityNegative; +define_ScriptVarPtr_Type(Infinity); +class CScriptVarInfinity : public CScriptVarIntegerBase { +protected: + CScriptVarInfinity(CTinyJS *Context, int Data) : CScriptVarIntegerBase(Context, Data<0?-1:1) {} + CScriptVarInfinity(const CScriptVarInfinity &Copy) : CScriptVarIntegerBase(Copy) {} ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarInfinity(); + virtual CScriptVarPtr clone(); + virtual int isInfinity(); // { return data; } + virtual std::string getString(); // {return data<0?"-Infinity":"Infinity";} + friend define_newScriptVar_Fnc(Infinity, CTinyJS* Context, Infinity); +}; +inline define_newScriptVar_Fnc(Infinity, CTinyJS* Context, Infinity Obj) { return new CScriptVarInfinity(Context, Obj.sig); } + + +////////////////////////////////////////////////////////////////////////// CScriptVarDouble + +define_ScriptVarPtr_Type(Double); +class CScriptVarDouble : public CScriptVar { +protected: + CScriptVarDouble(CTinyJS *Context, double Data); + CScriptVarDouble(const CScriptVarDouble &Copy) : CScriptVar(Copy), data(Copy.data) {} ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarDouble(); + virtual CScriptVarPtr clone(); + virtual bool isNumber(); // { return true; } + virtual bool isDouble(); // { return true; } + virtual int getInt(); // {return (int)data; } + virtual bool getBool(); // {return data!=0.0;} + virtual double getDouble(); // {return data;} + virtual std::string getString(); // {return float2string(data);} + virtual std::string getVarType(); // { return "number"; } +private: + double data; + friend define_newScriptVar_Fnc(Double, CTinyJS* Context, double); +}; +inline define_newScriptVar_Fnc(Double, CTinyJS* Context, double Obj) { return new CScriptVarDouble(Context, Obj); } + + +////////////////////////////////////////////////////////////////////////// CScriptVarFunction + +define_ScriptVarPtr_Type(Function); +class CScriptVarFunction : public CScriptVar { +protected: + CScriptVarFunction(CTinyJS *Context, CScriptTokenDataFnc *Data); + CScriptVarFunction(const CScriptVarFunction &Copy) : CScriptVar(Copy), data(Copy.data) { data->ref(); } ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarFunction(); + virtual CScriptVarPtr clone(); + virtual bool isFunction(); // { return true; } + + virtual std::string getString(); // {return "[ Function ]";} + virtual std::string getVarType(); // { return "function"; } + virtual std::string getParsableString(const std::string &indentString, const std::string &indent); + virtual CScriptTokenDataFnc *getFunctionData(); + void setFunctionData(CScriptTokenDataFnc *Data); +private: + CScriptTokenDataFnc *data; + std::string getParsableBlockString(TOKEN_VECT::iterator &it, TOKEN_VECT::iterator end, const std::string indentString, const std::string indent); + + + friend define_newScriptVar_Fnc(Function, CTinyJS* Context, CScriptTokenDataFnc *); +}; +inline define_newScriptVar_Fnc(Function, CTinyJS* Context, CScriptTokenDataFnc *Obj) { return new CScriptVarFunction(Context, Obj); } + + +////////////////////////////////////////////////////////////////////////// CScriptVarFunctionNative + +define_ScriptVarPtr_Type(FunctionNative); +class CScriptVarFunctionNative : public CScriptVarFunction { +protected: + CScriptVarFunctionNative(CTinyJS *Context, void *Userdata) : CScriptVarFunction(Context, 0), jsUserData(Userdata) { } + CScriptVarFunctionNative(const CScriptVarFunctionNative &Copy) : CScriptVarFunction(Copy), jsUserData(Copy.jsUserData) { } ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarFunctionNative(); + virtual CScriptVarPtr clone()=0; + virtual bool isNative(); // { return true; } + + virtual std::string getString(); // {return "[ Function Native ]";} + + virtual void callFunction(const CFunctionsScopePtr &c)=0;// { jsCallback(c, jsCallbackUserData); } +protected: + void *jsUserData; ///< user data passed as second argument to native functions +}; + + +////////////////////////////////////////////////////////////////////////// CScriptVarFunctionNativeCallback + + +define_ScriptVarPtr_Type(FunctionNativeCallback); +class CScriptVarFunctionNativeCallback : public CScriptVarFunctionNative { +protected: + CScriptVarFunctionNativeCallback(CTinyJS *Context, JSCallback Callback, void *Userdata) : CScriptVarFunctionNative(Context, Userdata), jsCallback(Callback) { } + CScriptVarFunctionNativeCallback(const CScriptVarFunctionNativeCallback &Copy) : CScriptVarFunctionNative(Copy), jsCallback(Copy.jsCallback) { } ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarFunctionNativeCallback(); + virtual CScriptVarPtr clone(); + virtual void callFunction(const CFunctionsScopePtr &c); +private: + JSCallback jsCallback; ///< Callback for native functions + friend define_newScriptVar_Fnc(FunctionNativeCallback, CTinyJS* Context, JSCallback Callback, void*); +}; +inline define_newScriptVar_Fnc(FunctionNativeCallback, CTinyJS* Context, JSCallback Callback, void* Userdata) { return new CScriptVarFunctionNativeCallback(Context, Callback, Userdata); } + + +////////////////////////////////////////////////////////////////////////// CScriptVarFunctionNativeClass + +template +class CScriptVarFunctionNativeClass : public CScriptVarFunctionNative { +protected: + CScriptVarFunctionNativeClass(CTinyJS *Context, native *ClassPtr, void (native::*ClassFnc)(const CFunctionsScopePtr &, void *), void *Userdata) : CScriptVarFunctionNative(Context, Userdata), classPtr(ClassPtr), classFnc(ClassFnc) { } + CScriptVarFunctionNativeClass(const CScriptVarFunctionNativeClass &Copy) : CScriptVarFunctionNative(Copy), classPtr(Copy.classPtr), classFnc(Copy.classFnc) { } ///< Copy protected -> use clone for public +public: + virtual CScriptVarPtr clone() { return new CScriptVarFunctionNativeClass(*this); } + + virtual void callFunction(const CFunctionsScopePtr &c) { (classPtr->*classFnc)(c, jsUserData); } +private: native *classPtr; - void (native::*classFnc)(CScriptVar *v, void *userdata); + void (native::*classFnc)(const CFunctionsScopePtr &c, void *userdata); + template + friend CScriptVarFunctionNativePtr newScriptVar(CTinyJS* Context, native2 *ClassPtr, void (native2::*ClassFnc)(const CFunctionsScopePtr &, void *), void *Userdata); +}; +template +inline CScriptVarFunctionNativePtr newScriptVar(CTinyJS* Context, native *ClassPtr, void (native::*ClassFnc)(const CFunctionsScopePtr &, void *), void *Userdata) { return new CScriptVarFunctionNativeClass(Context, ClassPtr, ClassFnc, Userdata); } + + +////////////////////////////////////////////////////////////////////////// CScriptVarScope + +define_dummy_t(Scope); +define_ScriptVarPtr_Type(Scope); +class CScriptVarScope : public CScriptVarObject { +protected: // only derived classes or friends can be created + CScriptVarScope(CTinyJS *Context) // constructor for rootScope + : CScriptVarObject(Context) {} + virtual CScriptVarPtr clone(); + virtual bool isObject(); // { return false; } +public: + virtual ~CScriptVarScope(); + virtual CScriptVarPtr scopeVar(); ///< to create var like: var a = ... + virtual CScriptVarPtr scopeLet(); ///< to create var like: let a = ... + virtual CScriptVarLink *findInScopes(const std::string &childName); + virtual CScriptVarScopePtr getParent(); + friend define_newScriptVar_Fnc(Scope, CTinyJS* Context, Scope_t); +}; +inline define_newScriptVar_Fnc(Scope, CTinyJS* Context, Scope_t) { return new CScriptVarScope(Context); } + + +////////////////////////////////////////////////////////////////////////// CScriptVarScopeFnc + +define_dummy_t(ScopeFnc); +define_ScriptVarPtr_Type(ScopeFnc); +class CScriptVarScopeFnc : public CScriptVarScope { +protected: // only derived classes or friends can be created + CScriptVarScopeFnc(CTinyJS *Context, const CScriptVarScopePtr &Closure) // constructor for FncScope + : CScriptVarScope(Context), closure(Closure ? addChild(TINYJS_FUNCTION_CLOSURE_VAR, Closure, 0) : 0) {} +public: + virtual ~CScriptVarScopeFnc(); + virtual CScriptVarLink *findInScopes(const std::string &childName); +protected: + CScriptVarLink *closure; + friend define_newScriptVar_Fnc(ScopeFnc, CTinyJS* Context, ScopeFnc_t, const CScriptVarScopePtr &Closure); +}; +inline define_newScriptVar_Fnc(ScopeFnc, CTinyJS* Context, ScopeFnc_t, const CScriptVarScopePtr &Closure) { return new CScriptVarScopeFnc(Context, Closure); } + + +////////////////////////////////////////////////////////////////////////// CScriptVarScopeLet + +define_dummy_t(ScopeLet); +define_ScriptVarPtr_Type(ScopeLet); +class CScriptVarScopeLet : public CScriptVarScope { +protected: // only derived classes or friends can be created + CScriptVarScopeLet(const CScriptVarScopePtr &Parent); // constructor for LetScope +// : CScriptVarScope(Parent->getContext()), parent( context->getRoot() != Parent ? addChild(TINYJS_SCOPE_PARENT_VAR, Parent, 0) : 0) {} +public: + virtual ~CScriptVarScopeLet(); + virtual CScriptVarLink *findInScopes(const std::string &childName); + virtual CScriptVarPtr scopeVar(); ///< to create var like: var a = ... + virtual CScriptVarScopePtr getParent(); +protected: + CScriptVarLink *parent; + friend define_newScriptVar_Fnc(ScopeLet, CTinyJS* Context, ScopeLet_t, const CScriptVarScopePtr &Parent); }; +inline define_newScriptVar_Fnc(ScopeLet, CTinyJS* , ScopeLet_t, const CScriptVarScopePtr &Parent) { return new CScriptVarScopeLet(Parent); } + -class CScriptVarSmartLink +////////////////////////////////////////////////////////////////////////// CScriptVarScopeWith + +define_dummy_t(ScopeWith); +define_ScriptVarPtr_Type(ScopeWith); +class CScriptVarScopeWith : public CScriptVarScopeLet { +protected: + CScriptVarScopeWith(const CScriptVarScopePtr &Parent, const CScriptVarPtr &With) + : CScriptVarScopeLet(Parent), with(addChild(TINYJS_SCOPE_WITH_VAR, With, 0)) {} + +public: + virtual ~CScriptVarScopeWith(); + virtual CScriptVarPtr scopeLet(); ///< to create var like: let a = ... + virtual CScriptVarLink *findInScopes(const std::string &childName); +private: + CScriptVarLink *with; + friend define_newScriptVar_Fnc(ScopeWith, CTinyJS* Context, ScopeWith_t, const CScriptVarScopePtr &Parent, const CScriptVarPtr &With); +}; +inline define_newScriptVar_Fnc(ScopeWith, CTinyJS* , ScopeWith_t, const CScriptVarScopePtr &Parent, const CScriptVarPtr &With) { return new CScriptVarScopeWith(Parent, With); } + +////////////////////////////////////////////////////////////////////////// +template +inline CScriptVarPtr CScriptVar::newScriptVar(T t) { return ::newScriptVar(context, t); } +////////////////////////////////////////////////////////////////////////// +// ----------------------------------------------------------------------------------- CSCRIPTVARSMARTLINK +class CScriptVarSmartLink : public fixed_size_object { public: - CScriptVarSmartLink (); - CScriptVarSmartLink (CScriptVarLink *Link); - explicit CScriptVarSmartLink (CScriptVar *Var); - ~ CScriptVarSmartLink (); - // Copy - CScriptVarSmartLink (const CScriptVarSmartLink &Link); + CScriptVarSmartLink() : link(0){} + CScriptVarSmartLink(CScriptVarLink *Link) : link(Link) {} + explicit CScriptVarSmartLink (CScriptVarPtr Var) : link(0) { *this = Var; } + ~ CScriptVarSmartLink() { if(link && !link->IsOwned()) delete link; } + /// Copy - when copying a SmartLink to an other then the right hand side will lost your link + CScriptVarSmartLink (const CScriptVarSmartLink &Link) : link(0) { *this = Link; } CScriptVarSmartLink &operator = (const CScriptVarSmartLink &Link); + ///- CScriptVarSmartLink &operator = (CScriptVarLink *Link); - CScriptVarSmartLink &operator = (CScriptVar *Var); - CScriptVarLink *operator ->() { return link; } + CScriptVarSmartLink &operator = (CScriptVarPtr Var); + CScriptVarLink *operator ->() const { return link; } CScriptVarLink &operator *() { return *link; } CScriptVarSmartLink &operator << (CScriptVarSmartLink &Link); - operator bool() { return link != 0; } + operator bool() const { return link != 0; } // operator CScriptVarLink *() { return link; } CScriptVarLink *&getLink() { return link; }; private: CScriptVarLink *link; }; +template +CScriptVarPointer::CScriptVarPointer(const CScriptVarSmartLink &Link) : var(0) { if(Link) { var = dynamic_cast(Link->operator->()); ASSERT(var); } if(var) var->ref(); } +// ----------------------------------------------------------------------------------- CTINYJS class CTinyJS { public: - CTinyJS(bool TwoPass=true); + CTinyJS(); ~CTinyJS(); - void execute(const std::string &code); + void execute(CScriptTokenizer &Tokenizer); + void execute(const char *Code, const std::string &File="", int Line=0, int Column=0); + void execute(const std::string &Code, const std::string &File="", int Line=0, int Column=0); + /** Evaluate the given code and return a link to a javascript object, + * useful for (dangerous) JSON parsing. If nothing to return, will return + * 'undefined' variable type. CScriptVarLink is returned as this will + * automatically unref the result as it goes out of scope. If you want to + * keep it, you must use ref() and unref() */ + CScriptVarLink evaluateComplex(CScriptTokenizer &Tokenizer); + /** Evaluate the given code and return a link to a javascript object, + * useful for (dangerous) JSON parsing. If nothing to return, will return + * 'undefined' variable type. CScriptVarLink is returned as this will + * automatically unref the result as it goes out of scope. If you want to + * keep it, you must use ref() and unref() */ + CScriptVarLink evaluateComplex(const char *code, const std::string &File="", int Line=0, int Column=0); /** Evaluate the given code and return a link to a javascript object, * useful for (dangerous) JSON parsing. If nothing to return, will return * 'undefined' variable type. CScriptVarLink is returned as this will * automatically unref the result as it goes out of scope. If you want to * keep it, you must use ref() and unref() */ - CScriptVarLink evaluateComplex(const std::string &code); + CScriptVarLink evaluateComplex(const std::string &code, const std::string &File="", int Line=0, int Column=0); + /** Evaluate the given code and return a string. If nothing to return, will return + * 'undefined' */ + std::string evaluate(CScriptTokenizer &Tokenizer); /** Evaluate the given code and return a string. If nothing to return, will return * 'undefined' */ - std::string evaluate(const std::string &code); + std::string evaluate(const char *code, const std::string &File="", int Line=0, int Column=0); + /** Evaluate the given code and return a string. If nothing to return, will return + * 'undefined' */ + std::string evaluate(const std::string &code, const std::string &File="", int Line=0, int Column=0); /// add a native function to be called from TinyJS /** example: \code - void scRandInt(CScriptVar *c, void *userdata) { ... } + void scRandInt(const CFunctionsScopePtr &c, void *userdata) { ... } tinyJS->addNative("function randInt(min, max)", scRandInt, 0); \endcode or \code - void scSubstring(CScriptVar *c, void *userdata) { ... } + void scSubstring(const CFunctionsScopePtr &c, void *userdata) { ... } tinyJS->addNative("function String.substring(lo, hi)", scSubstring, 0); \endcode or @@ -449,72 +1180,155 @@ class CTinyJS { class Class { public: - void scSubstring(CScriptVar *c, void *userdata) { ... } + void scSubstring(const CFunctionsScopePtr &c, void *userdata) { ... } }; Class Instanz; tinyJS->addNative("function String.substring(lo, hi)", &Instanz, &Class::*scSubstring, 0); \endcode */ - void addNative(const std::string &funcDesc, JSCallback ptr, void *userdata=0); - void addNative(const std::string &funcDesc, NativeFncBase *ptr, void *userdata=0); + + CScriptVarFunctionNativePtr addNative(const std::string &funcDesc, JSCallback ptr, void *userdata=0); template - void addNative(const std::string &funcDesc, C *class_ptr, void(C::*class_fnc)(CScriptVar *, void *), void *userdata=0) + CScriptVarFunctionNativePtr addNative(const std::string &funcDesc, C *class_ptr, void(C::*class_fnc)(const CFunctionsScopePtr &, void *), void *userdata=0) { - addNative(funcDesc, new NativeFnc(class_ptr, class_fnc), userdata); + return addNative(funcDesc, ::newScriptVar(this, class_ptr, class_fnc, userdata)); } - /// Get the value of the given variable, or return 0 - const std::string *getVariable(const std::string &path); - /// Send all variables to stdout void trace(); - CScriptVar *root; /// root of symbol table + const CScriptVarScopePtr &getRoot() { return root; }; /// gets the root of symbol table + // CScriptVar *root; /// root of symbol table + + template + CScriptVarPtr newScriptVar(T t) { return ::newScriptVar(this, t); } private: - bool twoPass; - int funcOffset; - CScriptLex *l; /// current lexer + static bool noexecute; + CScriptTokenizer *t; /// current tokenizer int runtimeFlags; - std::vector scopes; /// stack of scopes when parsing - CScriptVar *stringClass; /// Built in string class - CScriptVar *objectClass; /// Built in object class - CScriptVar *arrayClass; /// Built in array class - CScriptVar *exceptionVar; /// containing the exception var by (runtimeFlags&RUNTIME_THROW) == true; - void CheckRightHandVar(bool &execute, CScriptVarSmartLink &link, int pos=-1) + std::vector loop_labels; +// std::vector _scopes; /// stack of scopes when parsing + std::vector_scopes; + CScriptVarScopePtr root; + + const CScriptVarScopePtr &scope() { return _scopes.back(); } +/* + const CScriptVarScopePtr &push_FncScope(const CScriptVarScopePtr &Scope) { + _scopes.push_back(Scope); + return scope(); + } + + const CScriptVarScopePtr &push_newLetScope() { + return _scopes.back() = ::newScriptVar(this, ScopeLet, _scopes.back()); + } + const CScriptVarScopePtr &push_newWithScope(const CScriptVarPtr &With) { + return _scopes.back() = ::newScriptVar(this, ScopeWith, _scopes.back(), With); + } +*/ +/* + void pop_Scope() { + CScriptVarScopePtr parent = _scopes.back()->getParent(); + if(parent) _scopes.back() = parent; else _scopes.pop_back() ; + } +*/ + class CScopeControl { + private: + CScopeControl(const CScopeControl& Copy); // no copy + CScopeControl& operator =(const CScopeControl& Copy); + public: + CScopeControl(CTinyJS* Context) : context(Context), count(0) {} + ~CScopeControl() { while(count--) {CScriptVarScopePtr parent = context->_scopes.back()->getParent(); if(parent) context->_scopes.back() = parent; else context->_scopes.pop_back() ;} } + void addFncScope(const CScriptVarScopePtr &Scope) { context->_scopes.push_back(Scope); count++; } + void addLetScope() { context->_scopes.back() = ::newScriptVar(context, ScopeLet, context->_scopes.back()); count++; } + void addWithScope(const CScriptVarPtr &With) { context->_scopes.back() = ::newScriptVar(context, ScopeWith, context->_scopes.back(), With); count++; } + private: + CTinyJS *context; + int count; + }; + friend class CScopeControl; +public: + CScriptVarPtr objectPrototype; /// Built in object class + CScriptVarPtr arrayPrototype; /// Built in array class + CScriptVarPtr stringPrototype; /// Built in string class + CScriptVarPtr numberPrototype; /// Built in string class + CScriptVarPtr functionPrototype; /// Built in function class +private: + CScriptVarPtr exceptionVar; /// containing the exception var by (runtimeFlags&RUNTIME_THROW) == true; + + void CheckRightHandVar(bool &execute, CScriptVarSmartLink &link) { - if(execute && link && !link->owned && !link->owner && link->name.length()>0) - throwError(execute, link->name + " is not defined", pos); + if(execute && link && !link->IsOwned() && !link->owner && link->name.length()>0) + throwError(execute, link->name + " is not defined", t->getPrevPos()); } + void CheckRightHandVar(bool &execute, CScriptVarSmartLink &link, CScriptTokenizer::ScriptTokenPosition &Pos) + { + if(execute && link && !link->IsOwned() && !link->owner && link->name.length()>0) + throwError(execute, link->name + " is not defined", Pos); + } + + // function call +public: +// CScriptVarSmartLink callFunction(CScriptVarSmartLink &Function, std::vector &Arguments, const CScriptVarPtr& This, bool &execute); + CScriptVarPtr callFunction(const CScriptVarPtr &Function, std::vector &Arguments, const CScriptVarPtr& This, bool &execute); +private: + // + CScriptVarSmartLink getValue(CScriptVarSmartLink Var, bool execute); + CScriptVarSmartLink setValue(CScriptVarSmartLink Var); // parsing - in order of precedence - CScriptVarSmartLink factor(bool &execute); - CScriptVarSmartLink unary(bool &execute); - CScriptVarSmartLink term(bool &execute); - CScriptVarSmartLink expression(bool &execute); - CScriptVarSmartLink binary_shift(bool &execute); - CScriptVarSmartLink relation(bool &execute, int set=LEX_EQUAL, int set_n='<'); - CScriptVarSmartLink logic_binary(bool &execute, int op='|', int op_n1='^', int op_n2='&'); - CScriptVarSmartLink logic(bool &execute, int op=LEX_OROR, int op_n=LEX_ANDAND); - CScriptVarSmartLink condition(bool &execute); - CScriptVarSmartLink assignment(bool &execute); - CScriptVarSmartLink base(bool &execute); - void block(bool &execute); - CScriptVarSmartLink statement(bool &execute); + CScriptVarSmartLink execute_literals(bool &execute); + CScriptVarSmartLink execute_member(CScriptVarSmartLink &parent, bool &execute); + CScriptVarSmartLink execute_function_call(bool &execute); + CScriptVarSmartLink execute_unary(bool &execute); + CScriptVarSmartLink execute_term(bool &execute); + CScriptVarSmartLink execute_expression(bool &execute); + CScriptVarSmartLink execute_binary_shift(bool &execute); + CScriptVarSmartLink execute_relation(bool &execute, int set=LEX_EQUAL, int set_n='<'); + CScriptVarSmartLink execute_binary_logic(bool &execute, int op='|', int op_n1='^', int op_n2='&'); + CScriptVarSmartLink execute_logic(bool &execute, int op=LEX_OROR, int op_n=LEX_ANDAND); + CScriptVarSmartLink execute_condition(bool &execute); + CScriptVarSmartLink execute_assignment(bool &execute); + CScriptVarSmartLink execute_base(bool &execute); + void execute_block(bool &execute); + CScriptVarSmartLink execute_statement(bool &execute); // parsing utility functions - CScriptVarSmartLink parseFunctionDefinition(); - void parseFunctionArguments(CScriptVar *funcVar); + CScriptVarSmartLink parseFunctionDefinition(CScriptToken &FncToken); +// CScriptVarSmartLink parseFunctionDefinition(); +// void parseFunctionArguments(CScriptVar *funcVar); + CScriptVarSmartLink parseFunctionsBodyFromString(const std::string &Parameter, const std::string &FncBody); CScriptVarLink *findInScopes(const std::string &childName); ///< Finds a child, looking recursively up the scopes + /// Get all Keynames of en given object (optionial look up the prototype-chain) + void keys(STRING_VECTOR_t &Keys, CScriptVarPtr object, bool WithPrototypeChain); /// Look up in any parent classes of the given object - CScriptVarLink *findInParentClasses(CScriptVar *object, const std::string &name); - CScriptVar *addNative(const std::string &funcDesc); + CScriptVarLink *findInPrototypeChain(CScriptVarPtr object, const std::string &name); + CScriptVarFunctionNativePtr addNative(const std::string &funcDesc, CScriptVarFunctionNativePtr Var); /// throws an Error - void throwError(bool &execute, const std::string &message, int pos=-1); + void throwError(bool &execute, const std::string &message); + void throwError(bool &execute, const std::string &message, CScriptTokenizer::ScriptTokenPosition &Pos); /// native Functions - void scEval(CScriptVar *c, void *data); + void native_Object(const CFunctionsScopePtr &c, void *data); + void native_String(const CFunctionsScopePtr &c, void *data); + void native_Array(const CFunctionsScopePtr &c, void *data); + + + void native_Eval(const CFunctionsScopePtr &c, void *data); + void native_JSON_parse(const CFunctionsScopePtr &c, void *data); + void native_Object_hasOwnProperty(const CFunctionsScopePtr &c, void *data); + void native_Function(const CFunctionsScopePtr &c, void *data); + + void native_Function_call(const CFunctionsScopePtr &c, void *data); + void native_Function_apply(const CFunctionsScopePtr &c, void *data); + + + int uniqueID; +public: + int getUniqueID() { return ++uniqueID; } + CScriptVar *first; + void ClearLostVars(const CScriptVarPtr &extra=CScriptVarPtr()); }; #endif diff --git a/TinyJS_Functions.cpp b/TinyJS_Functions.cpp index ad9eeba..402232c 100755 --- a/TinyJS_Functions.cpp +++ b/TinyJS_Functions.cpp @@ -23,144 +23,153 @@ * Boston, MA 02111-1307, USA. */ -#ifdef _MSC_VER -# include "targetver.h" -# include -#endif #include #include #include +#include #include "TinyJS_Functions.h" -#ifdef _DEBUG -# ifdef _MSC_VER -# define new DEBUG_NEW -# endif -#endif - -#ifdef __GNUC__ -# define UNUSED(x) __attribute__((__unused__)) -#elif defined(_MSC_VER) -# ifndef UNUSED -# define UNUSED(x) x -# pragma warning( disable : 4100 ) /* unreferenced formal parameter */ -# endif -#else -# define UNUSED(x) x -#endif - - using namespace std; // ----------------------------------------------- Actual Functions -void scTrace(CScriptVar *c, void * userdata) { +static void scTrace(const CFunctionsScopePtr &c, void * userdata) { CTinyJS *js = (CTinyJS*)userdata; if(c->getParameterLength()) c->getParameter(0)->trace(); else - js->root->trace("root"); + js->getRoot()->trace("root"); } -void scObjectDump(CScriptVar *c, void *) { +static void scObjectDump(const CFunctionsScopePtr &c, void *) { c->getParameter("this")->trace("> "); } -void scObjectClone(CScriptVar *c, void *) { - CScriptVar *obj = c->getParameter("this"); - c->getReturnVar()->copyValue(obj); +static void scObjectClone(const CFunctionsScopePtr &c, void *) { + CScriptVarPtr obj = c->getParameter("this"); + c->setReturnVar(obj->clone()); } -void scMathRand(CScriptVar *c, void *) { - c->getReturnVar()->setDouble((double)rand()/RAND_MAX); -} - -void scMathRandInt(CScriptVar *c, void *) { - int min = c->getParameter("min")->getInt(); - int max = c->getParameter("max")->getInt(); - int val = min + (int)((long)rand()*(1+max-min)/RAND_MAX); - if (val>max) val=max; - c->getReturnVar()->setInt(val); -} - -void scCharToInt(CScriptVar *c, void *) { +static void scCharToInt(const CFunctionsScopePtr &c, void *) { string str = c->getParameter("ch")->getString();; int val = 0; if (str.length()>0) val = (int)str.c_str()[0]; - c->getReturnVar()->setInt(val); -} - -void scStringIndexOf(CScriptVar *c, void *) { - string str = c->getParameter("this")->getString(); - string search = c->getParameter("search")->getString(); - size_t p = str.find(search); - int val = (p==string::npos) ? -1 : p; - c->getReturnVar()->setInt(val); + c->setReturnVar(c->newScriptVar(val)); } -void scStringSubstring(CScriptVar *c, void *) { - string str = c->getParameter("this")->getString(); - int lo = c->getParameter("lo")->getInt(); - int hi = c->getParameter("hi")->getInt(); - int l = hi-lo; - if (l>0 && lo>=0 && lo+l<=(int)str.length()) - c->getReturnVar()->setString(str.substr(lo, l)); - else - c->getReturnVar()->setString(""); +static void scStringFromCharCode(const CFunctionsScopePtr &c, void *) { + char str[2]; + str[0] = c->getParameter("char")->getInt(); + str[1] = 0; + c->setReturnVar(c->newScriptVar(str)); } -void scStringCharAt(CScriptVar *c, void *) { - string str = c->getParameter("this")->getString(); - int p = c->getParameter("pos")->getInt(); - if (p>=0 && p<(int)str.length()) - c->getReturnVar()->setString(str.substr(p, 1)); - else - c->getReturnVar()->setString(""); -} - -void scIntegerParseInt(CScriptVar *c, void *) { +static void scIntegerParseInt(const CFunctionsScopePtr &c, void *) { string str = c->getParameter("str")->getString(); int val = strtol(str.c_str(),0,0); - c->getReturnVar()->setInt(val); + c->setReturnVar(c->newScriptVar(val)); } -void scIntegerValueOf(CScriptVar *c, void *) { +static void scIntegerValueOf(const CFunctionsScopePtr &c, void *) { string str = c->getParameter("str")->getString(); int val = 0; if (str.length()==1) - val = str[0]; - c->getReturnVar()->setInt(val); + val = str.operator[](0); + c->setReturnVar(c->newScriptVar(val)); +} + +static void scJSONStringify(const CFunctionsScopePtr &c, void *) { + string indent = " ", indentString; + c->setReturnVar(c->newScriptVar(c->getParameter("obj")->getParsableString(indentString, indent))); +} + +static void scArrayContains(const CFunctionsScopePtr &c, void *data) { + CScriptVarPtr obj = c->getParameter("obj"); + CScriptVarPtr arr = c->getParameter("this"); + + int l = arr->getArrayLength(); + CScriptVarSmartLink equal; + for (int i=0;imathsOp(arr->getArrayIndex(i), LEX_EQUAL); + if((*equal)->getBool()) { + c->setReturnVar(c->newScriptVar(true)); + return; + } + } + c->setReturnVar(c->newScriptVar(false)); } -void scJSONStringify(CScriptVar *c, void *) { - std::ostringstream result; - c->getParameter("obj")->getJSON(result); - c->getReturnVar()->setString(result.str()); +static void scArrayRemove(const CFunctionsScopePtr &c, void *data) { + CScriptVarPtr obj = c->getParameter("obj"); + CScriptVarPtr arr = c->getParameter("this"); + int i; + vector removedIndices; + + int l = arr->getArrayLength(); + CScriptVarSmartLink equal; + for (i=0;imathsOp(arr->getArrayIndex(i), LEX_EQUAL); + if((*equal)->getBool()) { + removedIndices.push_back(i); + } + } + if(removedIndices.size()) { + vector::iterator remove_it = removedIndices.begin(); + int next_remove = *remove_it; + int next_insert = *remove_it++; + for (i=next_remove;ifindChild(int2string(i)); + if(i == next_remove) { + if(link) arr->removeLink(link); + if(remove_it != removedIndices.end()) + next_remove = *remove_it++; + } else { + if(link) { + arr->setArrayIndex(next_insert++, link); + arr->removeLink(link); + } + } + } + } } -void scEval(CScriptVar *c, void *data) { - CTinyJS *tinyJS = (CTinyJS *)data; - std::string str = c->getParameter("jsCode")->getString(); - c->setReturnVar(tinyJS->evaluateComplex(str).var); +static void scArrayJoin(const CFunctionsScopePtr &c, void *data) { + string sep = c->getParameter("separator")->getString(); + CScriptVarPtr arr = c->getParameter("this"); + + ostringstream sstr; + int l = arr->getArrayLength(); + for (int i=0;i0) sstr << sep; + sstr << arr->getArrayIndex(i)->getString(); + } + + c->setReturnVar(c->newScriptVar(sstr.str())); } // ----------------------------------------------- Register Functions void registerFunctions(CTinyJS *tinyJS) { -// tinyJS->addNative("function eval(jsCode)", scEval, tinyJS); // execute the given string and return the result tinyJS->addNative("function trace()", scTrace, tinyJS); - tinyJS->addNative("function Object.dump()", scObjectDump, 0); - tinyJS->addNative("function Object.clone()", scObjectClone, 0); - tinyJS->addNative("function Math.rand()", scMathRand, 0); - tinyJS->addNative("function Math.randInt(min, max)", scMathRandInt, 0); - tinyJS->addNative("function charToInt(ch)", scCharToInt, 0); // convert a character to an int - get its value - tinyJS->addNative("function String.indexOf(search)", scStringIndexOf, 0); // find the position of a string in a string, -1 if not - tinyJS->addNative("function String.substring(lo,hi)", scStringSubstring, 0); - tinyJS->addNative("function String.charAt(pos)", scStringCharAt, 0); + tinyJS->addNative("function Object.prototype.dump()", scObjectDump, 0); + tinyJS->addNative("function Object.prototype.clone()", scObjectClone, 0); + +// tinyJS->addNative("function charToInt(ch)", scCharToInt, 0); // convert a character to an int - get its value +// tinyJS->addNative("function String.prototype.indexOf(search)", scStringIndexOf, 0); // find the position of a string in a string, -1 if not +// tinyJS->addNative("function String.prototype.substring(lo,hi)", scStringSubstring, 0); +// tinyJS->addNative("function String.prototype.charAt(pos)", scStringCharAt, 0); +// tinyJS->addNative("function String.prototype.charCodeAt(pos)", scStringCharCodeAt, 0); +// tinyJS->addNative("function String.prototype.fromCharCode(char)", scStringFromCharCode, 0); +// tinyJS->addNative("function String.prototype.split(separator,limit)", scStringSplit, 0); + tinyJS->addNative("function Integer.parseInt(str)", scIntegerParseInt, 0); // string to int tinyJS->addNative("function Integer.valueOf(str)", scIntegerValueOf, 0); // value of a single character tinyJS->addNative("function JSON.stringify(obj, replacer)", scJSONStringify, 0); // convert to JSON. replacer is ignored at the moment // JSON.parse is left out as you can (unsafely!) use eval instead + tinyJS->addNative("function Array.prototype.contains(obj)", scArrayContains, 0); + tinyJS->addNative("function Array.prototype.remove(obj)", scArrayRemove, 0); + tinyJS->addNative("function Array.prototype.join(separator)", scArrayJoin, 0); } diff --git a/TinyJS_MathFunctions.cpp b/TinyJS_MathFunctions.cpp index ea29cd8..12c1f96 100644 --- a/TinyJS_MathFunctions.cpp +++ b/TinyJS_MathFunctions.cpp @@ -10,255 +10,363 @@ #include #include #include +#include #include "TinyJS_MathFunctions.h" using namespace std; #define k_E exp(1.0) #define k_PI 3.1415926535897932384626433832795 +#define k_LN2 log((double)2) +#define k_LN10 log((double)10) +#define k_LOG2E (log(k_E)/log((double)2)) +#define k_LOG10E log10(k_E) +#define k_SQRT1_2 sqrt((double)0.5) +#define k_SQRT2 sqrt((double)2) #define F_ABS(a) ((a)>=0 ? (a) : (-(a))) #define F_MIN(a,b) ((a)>(b) ? (b) : (a)) #define F_MAX(a,b) ((a)>(b) ? (a) : (b)) #define F_SGN(a) ((a)>0 ? 1 : ((a)<0 ? -1 : 0 )) #define F_RNG(a,min,max) ((a)<(min) ? min : ((a)>(max) ? max : a )) -#define F_ROUND(a) ((a)>0 ? (int) ((a)+0.5) : (int) ((a)-0.5) ) //CScriptVar shortcut macro #define scIsInt(a) ( c->getParameter(a)->isInt() ) #define scIsDouble(a) ( c->getParameter(a)->isDouble() ) #define scGetInt(a) ( c->getParameter(a)->getInt() ) #define scGetDouble(a) ( c->getParameter(a)->getDouble() ) -#define scReturnInt(a) ( c->getReturnVar()->setInt(a) ) -#define scReturnDouble(a) ( c->getReturnVar()->setDouble(a) ) +#define scReturnInt(a) ( c->setReturnVar(c->newScriptVar((int)a)) ) +#define scReturnDouble(a) ( c->setReturnVar(c->newScriptVar((double)a)) ) +#define scReturnNaN() ( c->setReturnVar(c->newScriptVar(NaN)) ) #ifdef _MSC_VER namespace { - double asinh( const double &value ) - { - double returned; + double asinh( const double &value ) + { + double returned; - if(value>0) - returned = log(value + sqrt(value * value + 1)); - else - returned = -log(-value + sqrt(value * value + 1)); + if(value>0) + returned = log(value + sqrt(value * value + 1)); + else + returned = -log(-value + sqrt(value * value + 1)); - return(returned); - } + return(returned); + } - double acosh( const double &value ) - { - double returned; + double acosh( const double &value ) + { + double returned; - if(value>0) - returned = log(value + sqrt(value * value - 1)); - else - returned = -log(-value + sqrt(value * value - 1)); + if(value>0) + returned = log(value + sqrt(value * value - 1)); + else + returned = -log(-value + sqrt(value * value - 1)); - return(returned); - } + return(returned); + } } #endif +#define GET_PARAMETER_AS_NUMERIC_VAR(v,n) CScriptVarSmartLink v = c->getParameter(n)->getNumericVar() +#define RETURN_NAN_IS_NAN(v) do{ if((*v)->isNaN()) { c->setReturnVar(v); return; } }while(0) +#define RETURN_NAN_IS_NAN_OR_INFINITY(v) do{ if((*v)->isNaN() || (*v)->isInfinity()) { c->setReturnVar(v); return; } }while(0) +#define RETURN_INFINITY_IS_INFINITY(v) do{ if((*v)->isInfinity()) { c->setReturnVar(v); return; } }while(0) + +#define GET_DOUBLE(v) (*v)->getDouble() + //Math.abs(x) - returns absolute of given value -void scMathAbs(CScriptVar *c, void *userdata) { - if ( scIsInt("a") ) { - scReturnInt( F_ABS( scGetInt("a") ) ); - } else if ( scIsDouble("a") ) { - scReturnDouble( F_ABS( scGetDouble("a") ) ); - } +static void scMathAbs(const CFunctionsScopePtr &c, void *userdata) { + GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN(a); + if((*a)->getInt() < 0) { + CScriptVarPtr zero = c->newScriptVar(0); + c->setReturnVar(zero->mathsOp(a, '-')); + } else + c->setReturnVar(a); } //Math.round(a) - returns nearest round of given value -void scMathRound(CScriptVar *c, void *userdata) { - if ( scIsInt("a") ) { - scReturnInt( F_ROUND( scGetInt("a") ) ); - } else if ( scIsDouble("a") ) { - scReturnDouble( F_ROUND( scGetDouble("a") ) ); - } +static void scMathRound(const CFunctionsScopePtr &c, void *userdata) { + GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN(a); RETURN_INFINITY_IS_INFINITY(a); + scReturnInt( (int)floor( GET_DOUBLE(a)+0.5 ) ); +} + +//Math.ceil(a) - returns nearest round of given value +static void scMathCeil(const CFunctionsScopePtr &c, void *userdata) { + GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN(a); RETURN_INFINITY_IS_INFINITY(a); + scReturnInt( (int)ceil( GET_DOUBLE(a) ) ); +} + +//Math.floor(a) - returns nearest round of given value +static void scMathFloor(const CFunctionsScopePtr &c, void *userdata) { + GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN(a); RETURN_INFINITY_IS_INFINITY(a); + scReturnInt( (int)floor( GET_DOUBLE(a) ) ); } //Math.min(a,b) - returns minimum of two given values -void scMathMin(CScriptVar *c, void *userdata) { - if ( (scIsInt("a")) && (scIsInt("b")) ) { - scReturnInt( F_MIN( scGetInt("a"), scGetInt("b") ) ); - } else { - scReturnDouble( F_MIN( scGetDouble("a"), scGetDouble("b") ) ); - } +static void scMathMin(const CFunctionsScopePtr &c, void *userdata) { + int length = c->getParameterLength(); + CScriptVarSmartLink ret(c->newScriptVar(InfinityPositive)); + for(int i=0; iisInfinity() < 0) { c->setReturnVar(a); return; } + CScriptVarPtr result = (*a)->mathsOp(ret, '<'); + if(result->getBool()) + ret = a; + } + c->setReturnVar(ret); } //Math.max(a,b) - returns maximum of two given values -void scMathMax(CScriptVar *c, void *userdata) { - if ( (scIsInt("a")) && (scIsInt("b")) ) { - scReturnInt( F_MAX( scGetInt("a"), scGetInt("b") ) ); - } else { - scReturnDouble( F_MAX( scGetDouble("a"), scGetDouble("b") ) ); - } +static void scMathMax(const CFunctionsScopePtr &c, void *userdata) { + int length = c->getParameterLength(); + CScriptVarSmartLink ret((CScriptVarPtr)newScriptVar(c->getContext(), Infinity(-1))); + for(int i=0; iisInfinity() > 0) { c->setReturnVar(a); return; } + CScriptVarPtr result = (*a)->mathsOp(ret, '>'); + if(result->getBool()) + ret = a; + } + c->setReturnVar(ret); } //Math.range(x,a,b) - returns value limited between two given values -void scMathRange(CScriptVar *c, void *userdata) { - if ( (scIsInt("x")) ) { - scReturnInt( F_RNG( scGetInt("x"), scGetInt("a"), scGetInt("b") ) ); - } else { - scReturnDouble( F_RNG( scGetDouble("x"), scGetDouble("a"), scGetDouble("b") ) ); - } +static void scMathRange(const CFunctionsScopePtr &c, void *userdata) { + GET_PARAMETER_AS_NUMERIC_VAR(x,"x"); RETURN_NAN_IS_NAN(x); + GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN(a); + GET_PARAMETER_AS_NUMERIC_VAR(b,"b"); RETURN_NAN_IS_NAN(b); + + CScriptVarPtr check; + bool check_bool; + + check = (*a)->mathsOp(b, LEX_LEQUAL); + check_bool = check->getBool(); + if(!check_bool) { scReturnNaN(); return; } + + check = (*x)->mathsOp(a, '<'); + check_bool = check->getBool(); + if(check_bool) { c->setReturnVar(a); return; } + + check = (*x)->mathsOp(b, '>'); + check_bool = check->getBool(); + c->setReturnVar((check_bool ? b:x)); } //Math.sign(a) - returns sign of given value (-1==negative,0=zero,1=positive) -void scMathSign(CScriptVar *c, void *userdata) { - if ( scIsInt("a") ) { - scReturnInt( F_SGN( scGetInt("a") ) ); - } else if ( scIsDouble("a") ) { - scReturnDouble( F_SGN( scGetDouble("a") ) ); - } +static void scMathSign(const CFunctionsScopePtr &c, void *userdata) { + GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN(a); + double a_d = GET_DOUBLE(a); + scReturnInt(a_d>0.0?1:a_d<0.0?-1:0); } - -//Math.PI() - returns PI value -void scMathPI(CScriptVar *c, void *userdata) { - scReturnDouble(k_PI); +static void init_rand(){ + static int inited=0; + if(!inited) { + inited = 1; + srand((unsigned int)time(NULL)); + } +} +static void scMathRandom(const CFunctionsScopePtr &c, void *) { + init_rand(); + scReturnDouble((double)rand()/RAND_MAX); } //Math.toDegrees(a) - returns degree value of a given angle in radians -void scMathToDegrees(CScriptVar *c, void *userdata) { - scReturnDouble( (180.0/k_PI)*( scGetDouble("a") ) ); +static void scMathToDegrees(const CFunctionsScopePtr &c, void *userdata) { + GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN(a); RETURN_INFINITY_IS_INFINITY(a); + scReturnDouble( (180.0/k_PI)*( GET_DOUBLE(a) ) ); } //Math.toRadians(a) - returns radians value of a given angle in degrees -void scMathToRadians(CScriptVar *c, void *userdata) { - scReturnDouble( (k_PI/180.0)*( scGetDouble("a") ) ); +static void scMathToRadians(const CFunctionsScopePtr &c, void *userdata) { + GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN(a); RETURN_INFINITY_IS_INFINITY(a); + scReturnDouble( (k_PI/180.0)*( GET_DOUBLE(a) ) ); } //Math.sin(a) - returns trig. sine of given angle in radians -void scMathSin(CScriptVar *c, void *userdata) { - scReturnDouble( sin( scGetDouble("a") ) ); +static void scMathSin(const CFunctionsScopePtr &c, void *userdata) { + GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN_OR_INFINITY(a); + scReturnDouble( sin( GET_DOUBLE(a) ) ); } //Math.asin(a) - returns trig. arcsine of given angle in radians -void scMathASin(CScriptVar *c, void *userdata) { - scReturnDouble( asin( scGetDouble("a") ) ); +static void scMathASin(const CFunctionsScopePtr &c, void *userdata) { + GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN_OR_INFINITY(a); + scReturnDouble( asin( GET_DOUBLE(a) ) ); } //Math.cos(a) - returns trig. cosine of given angle in radians -void scMathCos(CScriptVar *c, void *userdata) { - scReturnDouble( cos( scGetDouble("a") ) ); +static void scMathCos(const CFunctionsScopePtr &c, void *userdata) { + GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN_OR_INFINITY(a); + scReturnDouble( cos( GET_DOUBLE(a) ) ); } //Math.acos(a) - returns trig. arccosine of given angle in radians -void scMathACos(CScriptVar *c, void *userdata) { - scReturnDouble( acos( scGetDouble("a") ) ); +static void scMathACos(const CFunctionsScopePtr &c, void *userdata) { + GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN_OR_INFINITY(a); + scReturnDouble( acos( GET_DOUBLE(a) ) ); } //Math.tan(a) - returns trig. tangent of given angle in radians -void scMathTan(CScriptVar *c, void *userdata) { - scReturnDouble( tan( scGetDouble("a") ) ); +static void scMathTan(const CFunctionsScopePtr &c, void *userdata) { + GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN_OR_INFINITY(a); + scReturnDouble( tan( GET_DOUBLE(a) ) ); } //Math.atan(a) - returns trig. arctangent of given angle in radians -void scMathATan(CScriptVar *c, void *userdata) { - scReturnDouble( atan( scGetDouble("a") ) ); +static void scMathATan(const CFunctionsScopePtr &c, void *userdata) { + GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN_OR_INFINITY(a); + scReturnDouble( atan( GET_DOUBLE(a) ) ); } //Math.sinh(a) - returns trig. hyperbolic sine of given angle in radians -void scMathSinh(CScriptVar *c, void *userdata) { - scReturnDouble( sinh( scGetDouble("a") ) ); +static void scMathSinh(const CFunctionsScopePtr &c, void *userdata) { + GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN_OR_INFINITY(a); + scReturnDouble( sinh( GET_DOUBLE(a) ) ); } //Math.asinh(a) - returns trig. hyperbolic arcsine of given angle in radians -void scMathASinh(CScriptVar *c, void *userdata) { - scReturnDouble( asinh( scGetDouble("a") ) ); +static void scMathASinh(const CFunctionsScopePtr &c, void *userdata) { + GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN_OR_INFINITY(a); + scReturnDouble( asinh( GET_DOUBLE(a) ) ); } //Math.cosh(a) - returns trig. hyperbolic cosine of given angle in radians -void scMathCosh(CScriptVar *c, void *userdata) { - scReturnDouble( cosh( scGetDouble("a") ) ); +static void scMathCosh(const CFunctionsScopePtr &c, void *userdata) { + GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN_OR_INFINITY(a); + scReturnDouble( cosh( GET_DOUBLE(a) ) ); } //Math.acosh(a) - returns trig. hyperbolic arccosine of given angle in radians -void scMathACosh(CScriptVar *c, void *userdata) { - scReturnDouble( acosh( scGetDouble("a") ) ); +static void scMathACosh(const CFunctionsScopePtr &c, void *userdata) { + GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN_OR_INFINITY(a); + scReturnDouble( acosh( GET_DOUBLE(a) ) ); } //Math.tanh(a) - returns trig. hyperbolic tangent of given angle in radians -void scMathTanh(CScriptVar *c, void *userdata) { - scReturnDouble( tanh( scGetDouble("a") ) ); +static void scMathTanh(const CFunctionsScopePtr &c, void *userdata) { + GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN_OR_INFINITY(a); + scReturnDouble( tanh( GET_DOUBLE(a) ) ); } //Math.atan(a) - returns trig. hyperbolic arctangent of given angle in radians -void scMathATanh(CScriptVar *c, void *userdata) { - scReturnDouble( atan( scGetDouble("a") ) ); -} - -//Math.E() - returns E Neplero value -void scMathE(CScriptVar *c, void *userdata) { - scReturnDouble(k_E); +static void scMathATanh(const CFunctionsScopePtr &c, void *userdata) { + GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN_OR_INFINITY(a); + scReturnDouble( atan( GET_DOUBLE(a) ) ); } //Math.log(a) - returns natural logaritm (base E) of given value -void scMathLog(CScriptVar *c, void *userdata) { - scReturnDouble( log( scGetDouble("a") ) ); +static void scMathLog(const CFunctionsScopePtr &c, void *userdata) { + GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN(a); + int a_i = (*a)->isInfinity(); + double a_d = (*a)->getDouble(); + if(a_i>0) { c->setReturnVar(c->newScriptVar(InfinityPositive)); return; } + else if(a_i<0 || a_d<0.0) { scReturnNaN(); return; } + scReturnDouble( log( a_d ) ); } //Math.log10(a) - returns logaritm(base 10) of given value -void scMathLog10(CScriptVar *c, void *userdata) { - scReturnDouble( log10( scGetDouble("a") ) ); +static void scMathLog10(const CFunctionsScopePtr &c, void *userdata) { + GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN(a); + int a_i = (*a)->isInfinity(); + double a_d = (*a)->getDouble(); + if(a_i>0) { c->setReturnVar(c->newScriptVar(InfinityPositive)); return; } + else if(a_i<0 || a_d<0.0) { c->setReturnVar(c->newScriptVar(NaN)); return; } + scReturnDouble( log10( a_d ) ); } //Math.exp(a) - returns e raised to the power of a given number -void scMathExp(CScriptVar *c, void *userdata) { - scReturnDouble( exp( scGetDouble("a") ) ); +static void scMathExp(const CFunctionsScopePtr &c, void *userdata) { + GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN(a); + int a_i = (*a)->isInfinity(); + if(a_i>0) { c->setReturnVar(c->newScriptVar(InfinityPositive)); return; } + else if(a_i<0) { c->setReturnVar(c->newScriptVar(0)); return; } + scReturnDouble( exp( GET_DOUBLE(a) ) ); } //Math.pow(a,b) - returns the result of a number raised to a power (a)^(b) -void scMathPow(CScriptVar *c, void *userdata) { - scReturnDouble( pow( scGetDouble("a"), scGetDouble("b") ) ); +static void scMathPow(const CFunctionsScopePtr &c, void *userdata) { + GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN(a); + GET_PARAMETER_AS_NUMERIC_VAR(b,"b"); RETURN_NAN_IS_NAN(b); + int a_i = (*a)->isInfinity(), b_i = (*b)->isInfinity(); + double a_d = (*a)->getDouble(), b_d = (*b)->getDouble(); + if(b_i>0) { + if(a_i || a_d>1.0 || a_d<-1.0) { c->setReturnVar(c->newScriptVar(InfinityPositive)); return; } + else if(a_i==0 && (a_d==1.0 || a_d==-1.0)) { c->setReturnVar(c->newScriptVar(1)); return; } + if(a_i==0 && a_d<1.0 && a_d>-1.0) { c->setReturnVar(c->newScriptVar(0)); return; } + } else if(b_i<0) { c->setReturnVar(c->newScriptVar(0)); return; } + else if(b_d == 0.0) { c->setReturnVar(c->newScriptVar(1)); return; } + + if(a_i) a_d = a_i; + double result = pow(a_d, b_d); + if(a_i) { c->setReturnVar(c->newScriptVar(Infinity(result>=0.0?1:-1))); return; } + scReturnDouble( result ); } //Math.sqr(a) - returns square of given value -void scMathSqr(CScriptVar *c, void *userdata) { - scReturnDouble( ( scGetDouble("a") * scGetDouble("a") ) ); +static void scMathSqr(const CFunctionsScopePtr &c, void *userdata) { + GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN(a); RETURN_INFINITY_IS_INFINITY(a); + scReturnDouble( ( GET_DOUBLE(a) * GET_DOUBLE(a) ) ); } //Math.sqrt(a) - returns square root of given value -void scMathSqrt(CScriptVar *c, void *userdata) { - scReturnDouble( sqrt( scGetDouble("a") ) ); +static void scMathSqrt(const CFunctionsScopePtr &c, void *userdata) { + GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN(a); RETURN_INFINITY_IS_INFINITY(a); + scReturnDouble( sqrt( GET_DOUBLE(a) ) ); } // ----------------------------------------------- Register Functions void registerMathFunctions(CTinyJS *tinyJS) { - - // --- Math and Trigonometry functions --- - tinyJS->addNative("function Math.abs(a)", scMathAbs, 0); - tinyJS->addNative("function Math.round(a)", scMathRound, 0); - tinyJS->addNative("function Math.min(a,b)", scMathMin, 0); - tinyJS->addNative("function Math.max(a,b)", scMathMax, 0); - tinyJS->addNative("function Math.range(x,a,b)", scMathRange, 0); - tinyJS->addNative("function Math.sign(a)", scMathSign, 0); - - tinyJS->addNative("function Math.PI()", scMathPI, 0); - tinyJS->addNative("function Math.toDegrees(a)", scMathToDegrees, 0); - tinyJS->addNative("function Math.toRadians(a)", scMathToRadians, 0); - tinyJS->addNative("function Math.sin(a)", scMathSin, 0); - tinyJS->addNative("function Math.asin(a)", scMathASin, 0); - tinyJS->addNative("function Math.cos(a)", scMathCos, 0); - tinyJS->addNative("function Math.acos(a)", scMathACos, 0); - tinyJS->addNative("function Math.tan(a)", scMathTan, 0); - tinyJS->addNative("function Math.atan(a)", scMathATan, 0); - tinyJS->addNative("function Math.sinh(a)", scMathSinh, 0); - tinyJS->addNative("function Math.asinh(a)", scMathASinh, 0); - tinyJS->addNative("function Math.cosh(a)", scMathCosh, 0); - tinyJS->addNative("function Math.acosh(a)", scMathACosh, 0); - tinyJS->addNative("function Math.tanh(a)", scMathTanh, 0); - tinyJS->addNative("function Math.atanh(a)", scMathATanh, 0); - - tinyJS->addNative("function Math.E()", scMathE, 0); - tinyJS->addNative("function Math.log(a)", scMathLog, 0); - tinyJS->addNative("function Math.log10(a)", scMathLog10, 0); - tinyJS->addNative("function Math.exp(a)", scMathExp, 0); - tinyJS->addNative("function Math.pow(a,b)", scMathPow, 0); - - tinyJS->addNative("function Math.sqr(a)", scMathSqr, 0); - tinyJS->addNative("function Math.sqrt(a)", scMathSqrt, 0); + + CScriptVarLink *Math = tinyJS->getRoot()->addChild("Math", tinyJS->newScriptVar(Object)); + + // --- Math and Trigonometry functions --- + tinyJS->addNative("function Math.abs(a)", scMathAbs, 0); + tinyJS->addNative("function Math.round(a)", scMathRound, 0); + tinyJS->addNative("function Math.ceil(a)", scMathCeil, 0); + tinyJS->addNative("function Math.floor(a)", scMathFloor, 0); + tinyJS->addNative("function Math.min()", scMathMin, 0); + tinyJS->addNative("function Math.max()", scMathMax, 0); + tinyJS->addNative("function Math.range(x,a,b)", scMathRange, 0); + tinyJS->addNative("function Math.sign(a)", scMathSign, 0); + tinyJS->addNative("function Math.random(a)", scMathRandom, 0); + + +// atan2, ceil, floor, random, round, + + (*Math)->addChild("LN2", tinyJS->newScriptVar(k_LN2), SCRIPTVARLINK_ENUMERABLE); + (*Math)->addChild("LN10", tinyJS->newScriptVar(k_LN10), SCRIPTVARLINK_ENUMERABLE); + (*Math)->addChild("LOG2E", tinyJS->newScriptVar(k_LOG2E), SCRIPTVARLINK_ENUMERABLE); + (*Math)->addChild("LOG10E", tinyJS->newScriptVar(k_LOG10E), SCRIPTVARLINK_ENUMERABLE); + (*Math)->addChild("SQRT1_2", tinyJS->newScriptVar(k_SQRT1_2), SCRIPTVARLINK_ENUMERABLE); + (*Math)->addChild("SQRT2", tinyJS->newScriptVar(k_SQRT2), SCRIPTVARLINK_ENUMERABLE); + (*Math)->addChild("PI", tinyJS->newScriptVar(k_PI), SCRIPTVARLINK_ENUMERABLE); +// tinyJS->addNative("function Math.PI()", scMathPI, 0); + tinyJS->addNative("function Math.toDegrees(a)", scMathToDegrees, 0); + tinyJS->addNative("function Math.toRadians(a)", scMathToRadians, 0); + tinyJS->addNative("function Math.sin(a)", scMathSin, 0); + tinyJS->addNative("function Math.asin(a)", scMathASin, 0); + tinyJS->addNative("function Math.cos(a)", scMathCos, 0); + tinyJS->addNative("function Math.acos(a)", scMathACos, 0); + tinyJS->addNative("function Math.tan(a)", scMathTan, 0); + tinyJS->addNative("function Math.atan(a)", scMathATan, 0); + tinyJS->addNative("function Math.sinh(a)", scMathSinh, 0); + tinyJS->addNative("function Math.asinh(a)", scMathASinh, 0); + tinyJS->addNative("function Math.cosh(a)", scMathCosh, 0); + tinyJS->addNative("function Math.acosh(a)", scMathACosh, 0); + tinyJS->addNative("function Math.tanh(a)", scMathTanh, 0); + tinyJS->addNative("function Math.atanh(a)", scMathATanh, 0); + + (*Math)->addChild("E", tinyJS->newScriptVar(k_E), SCRIPTVARLINK_ENUMERABLE); +// tinyJS->addNative("function Math.E()", scMathE, 0); + tinyJS->addNative("function Math.log(a)", scMathLog, 0); + tinyJS->addNative("function Math.log10(a)", scMathLog10, 0); + tinyJS->addNative("function Math.exp(a)", scMathExp, 0); + tinyJS->addNative("function Math.pow(a,b)", scMathPow, 0); + + tinyJS->addNative("function Math.sqr(a)", scMathSqr, 0); + tinyJS->addNative("function Math.sqrt(a)", scMathSqrt, 0); } diff --git a/TinyJS_StringFunctions.cpp b/TinyJS_StringFunctions.cpp new file mode 100644 index 0000000..40acc85 --- /dev/null +++ b/TinyJS_StringFunctions.cpp @@ -0,0 +1,236 @@ +/* + * TinyJS + * + * A single-file Javascript-alike engine + * + * Authored By Gordon Williams + * + * Copyright (C) 2009 Pur3 Ltd + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +/* + * 42TinyJS + * + * A fork of TinyJS + * + * Copyright (C) 2010 ardisoft + * + * Authored By Armin Diedering + * + */ + +#include +#include "TinyJS.h" + +using namespace std; +// ----------------------------------------------- Actual Functions + +static void scStringCharAt(const CFunctionsScopePtr &c, void *) { + string str = c->getParameter("this")->getString(); + int p = c->getParameter("pos")->getInt(); + if (p>=0 && p<(int)str.length()) + c->setReturnVar(c->newScriptVar(str.substr(p, 1))); + else + c->setReturnVar(c->newScriptVar("")); +} + +static void scStringCharCodeAt(const CFunctionsScopePtr &c, void *) { + string str = c->getParameter("this")->getString(); + int p = c->getParameter("pos")->getInt(); + if (p>=0 && p<(int)str.length()) + c->setReturnVar(c->newScriptVar(str.at(p))); + else + c->setReturnVar(c->newScriptVar(0)); +} + +static void scStringConcat(const CFunctionsScopePtr &c, void *userdata) { + int length = c->getParameterLength(); + string str = c->getParameter("this")->getString(); + for(int i=(int)userdata; igetParameter(i)->getString()); + c->setReturnVar(c->newScriptVar(str)); +} + +static void scStringIndexOf(const CFunctionsScopePtr &c, void *userdata) { + string str = c->getParameter("this")->getString(); + string search = c->getParameter("search")->getString(); + size_t p = (userdata==0) ? str.find(search) : str.rfind(search); + int val = (p==string::npos) ? -1 : p; + c->setReturnVar(c->newScriptVar(val)); +} + +static void scStringSlice(const CFunctionsScopePtr &c, void *userdata) { + string str = c->getParameter("this")->getString(); + int length = c->getParameterLength()-((int)userdata & 1); + bool slice = ((int)userdata & 2) == 0; + int start = c->getParameter("start")->getInt(); + int end = (int)str.size(); + if(slice && start<0) start = str.size()+start; + if(length>1) { + end = c->getParameter("end")->getInt(); + if(slice && end<0) end = str.size()+end; + } + if(!slice && end < start) { end^=start; start^=end; end^=start; } + if(start<0) start = 0; + if(start>=(int)str.size()) + c->setReturnVar(c->newScriptVar("")); + else if(end <= start) + c->setReturnVar(c->newScriptVar("")); + else + c->setReturnVar(c->newScriptVar(str.substr(start, end-start))); +} + +static void scStringSplit(const CFunctionsScopePtr &c, void *) { + string str = c->getParameter("this")->getString(); + CScriptVarPtr sep_var = c->getParameter("separator"); + CScriptVarPtr limit_var = c->getParameter("limit"); + int limit = limit_var->isUndefined() ? 0x7fffffff : limit_var->getInt(); + + CScriptVarPtr result(newScriptVar(c->getContext(), Array)); + c->setReturnVar(result); + if(limit == 0 || !str.size()) + return; + else if(sep_var->isUndefined()) { + result->setArrayIndex(0, c->newScriptVar(str)); + return; + } + string sep = sep_var->getString(); + if(sep.size() == 0) { + for(int i=0; isetArrayIndex(i, c->newScriptVar(str.substr(i,1))); + return; + } + int length = 0; + string::size_type pos = 0, last_pos=0; + do { + pos = str.find(sep, last_pos); + result->setArrayIndex(length++, c->newScriptVar(str.substr(last_pos ,pos-last_pos))); + if(length == limit || pos == string::npos) break; + last_pos = pos+sep.size(); + } while (last_pos < str.size()); +} + +static void scStringSubstr(const CFunctionsScopePtr &c, void *userdata) { + string str = c->getParameter("this")->getString(); + int length = c->getParameterLength()-(int)userdata; + int start = c->getParameter("start")->getInt(); + if(start<0 || start>=(int)str.size()) + c->setReturnVar(c->newScriptVar("")); + else if(length>1) { + int length = c->getParameter("length")->getInt(); + c->setReturnVar(c->newScriptVar(str.substr(start, length))); + } else + c->setReturnVar(c->newScriptVar(str.substr(start))); +} + +static void scStringToLowerCase(const CFunctionsScopePtr &c, void *) { + string str = c->getParameter("this")->getString(); + transform(str.begin(), str.end(), str.begin(), ::tolower); + c->setReturnVar(c->newScriptVar(str)); +} + +static void scStringToUpperCase(const CFunctionsScopePtr &c, void *) { + string str = c->getParameter("this")->getString(); + transform(str.begin(), str.end(), str.begin(), ::toupper); + c->setReturnVar(c->newScriptVar(str)); +} + +static void scStringTrim(const CFunctionsScopePtr &c, void *userdata) { + string str = c->getParameter("this")->getString(); + string::size_type start = 0; + string::size_type end = string::npos; + if((((int)userdata) & 2) == 0) { + start = str.find_first_not_of(" \t\r\n"); + if(start == string::npos) start = 0; + } + if((((int)userdata) & 1) == 0) { + end = str.find_last_not_of(" \t\r\n"); + if(end != string::npos) end = 1+end-start; + } + c->setReturnVar(c->newScriptVar(str.substr(start, end))); +} + + + +static void scCharToInt(const CFunctionsScopePtr &c, void *) { + string str = c->getParameter("ch")->getString();; + int val = 0; + if (str.length()>0) + val = (int)str.c_str()[0]; + c->setReturnVar(c->newScriptVar(val)); +} + + +static void scStringFromCharCode(const CFunctionsScopePtr &c, void *) { + char str[2]; + str[0] = c->getParameter("char")->getInt(); + str[1] = 0; + c->setReturnVar(c->newScriptVar(str)); +} + + +// ----------------------------------------------- Register Functions +void registerStringFunctions(CTinyJS *tinyJS) { + CScriptVarPtr fnc; + // charAt + tinyJS->addNative("function String.prototype.charAt(pos)", scStringCharAt, 0); + tinyJS->addNative("function String.charAt(this,pos)", scStringCharAt, 0); + // charCodeAt + tinyJS->addNative("function String.prototype.charCodeAt(pos)", scStringCharCodeAt, 0); + tinyJS->addNative("function String.charCodeAt(this,pos)", scStringCharCodeAt, 0); + // concat + tinyJS->addNative("function String.prototype.concat()", scStringConcat, 0); + tinyJS->addNative("function String.concat(this)", scStringConcat, (void*)1); + // indexOf + tinyJS->addNative("function String.prototype.indexOf(search)", scStringIndexOf, 0); // find the position of a string in a string, -1 if not + tinyJS->addNative("function String.indexOf(this,search)", scStringIndexOf, 0); // find the position of a string in a string, -1 if not + // lastIndexOf + tinyJS->addNative("function String.prototype.lastIndexOf(search)", scStringIndexOf, (void*)-1); // find the last position of a string in a string, -1 if not + tinyJS->addNative("function String.lastIndexOf(this,search)", scStringIndexOf, (void*)-1); // find the last position of a string in a string, -1 if not + // slice + tinyJS->addNative("function String.prototype.slice(start,end)", scStringSlice, 0); // find the last position of a string in a string, -1 if not + tinyJS->addNative("function String.slice(this,start,end)", scStringSlice, (void*)1); // find the last position of a string in a string, -1 if not + // split + tinyJS->addNative("function String.prototype.split(separator,limit)", scStringSplit, 0); + tinyJS->addNative("function String.split(this,separator,limit)", scStringSplit, 0); + // substr + tinyJS->addNative("function String.prototype.substr(start,length)", scStringSubstr, 0); + tinyJS->addNative("function String.substr(this,start,length)", scStringSubstr, (void*)1); + // substring + tinyJS->addNative("function String.prototype.substring(start,end)", scStringSlice, (void*)2); + tinyJS->addNative("function String.substring(this,start,end)", scStringSlice, (void*)3); + // toLowerCase + tinyJS->addNative("function String.prototype.toLowerCase()", scStringToLowerCase, 0); + tinyJS->addNative("function String.toLowerCase(this)", scStringToLowerCase, 0); + // toUpperCase + tinyJS->addNative("function String.prototype.toUpperCase()", scStringToUpperCase, 0); + tinyJS->addNative("function String.toUpperCase(this)", scStringToUpperCase, 0); + // trim + tinyJS->addNative("function String.prototype.trim()", scStringTrim, 0); + tinyJS->addNative("function String.trim(this)", scStringTrim, 0); + // trimLeft + tinyJS->addNative("function String.prototype.trimLeft()", scStringTrim, (void*)1); + tinyJS->addNative("function String.trimLeft(this)", scStringTrim, (void*)1); + // trimRight + tinyJS->addNative("function String.prototype.trimRight()", scStringTrim, (void*)2); + tinyJS->addNative("function String.trimRight(this)", scStringTrim, (void*)2); + + tinyJS->addNative("function charToInt(ch)", scCharToInt, 0); // convert a character to an int - get its value + + tinyJS->addNative("function String.prototype.fromCharCode(char)", scStringFromCharCode, 0); +} + diff --git a/TinyJS_StringFunctions.h b/TinyJS_StringFunctions.h new file mode 100644 index 0000000..ccafc94 --- /dev/null +++ b/TinyJS_StringFunctions.h @@ -0,0 +1,34 @@ +/* + * TinyJS + * + * A single-file Javascript-alike engine + * + * Authored By Gordon Williams + * + * Copyright (C) 2009 Pur3 Ltd + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef TINYJS_STRINGFUNCTIONS_H +#define TINYJS_STRINGFUNCTIONS_H + +#include "TinyJS.h" + +/// Register useful functions with the TinyJS interpreter +extern void registerStringFunctions(CTinyJS *tinyJS); + +#endif diff --git a/lib-tiny-js.vcxproj b/lib-tiny-js.vcxproj index 0a811a2..6526bd8 100644 --- a/lib-tiny-js.vcxproj +++ b/lib-tiny-js.vcxproj @@ -41,14 +41,14 @@ <_ProjectFileVersion>10.0.30319.1 $(SolutionDir)$(Configuration)\ - $(ProjectName)\$(Configuration)\ + Build\$(ProjectName)\$(Configuration)\ $(SolutionDir)$(Configuration)\ - $(ProjectName)\$(Configuration)\ + Build\$(ProjectName)\$(Configuration)\ Disabled - WIN32;_DEBUG;_AFXDLL;%(PreprocessorDefinitions) + WIN32;_DEBUG;%(PreprocessorDefinitions) true EnableFastChecks MultiThreadedDebugDLL @@ -60,7 +60,7 @@ Full true - WIN32;NDEBUG;_AFXDLL;%(PreprocessorDefinitions) + WIN32;NDEBUG;%(PreprocessorDefinitions) MultiThreadedDLL true Level3 @@ -68,15 +68,21 @@ + + + + + + diff --git a/lib-tiny-js.vcxproj.filters b/lib-tiny-js.vcxproj.filters index 189584d..c36d8dd 100644 --- a/lib-tiny-js.vcxproj.filters +++ b/lib-tiny-js.vcxproj.filters @@ -24,6 +24,12 @@ Quelldateien + + Quelldateien + + + Quelldateien + @@ -38,5 +44,17 @@ Headerdateien + + Headerdateien + + + Headerdateien + + + Headerdateien + + + Headerdateien + \ No newline at end of file diff --git a/pool_allocator.cpp b/pool_allocator.cpp new file mode 100644 index 0000000..5d2bc56 --- /dev/null +++ b/pool_allocator.cpp @@ -0,0 +1,134 @@ +#include "pool_allocator.h" +#include +struct block { + block* next; +}; +struct block_head { + block_head* next; +}; + +static void set_next(void* p, void* next) { + static_cast(p)->next = static_cast(next); +} + +static void* get_next(void* p) { + return static_cast(p)->next; +} + +fixed_size_allocator::fixed_size_allocator( size_t numObjects, size_t objectSize, const char *for_class ) +{ +/// fprintf(stderr, "allocator(%d) created\n", object_size); + num_objects = numObjects; + object_size = objectSize >= sizeof(block) ? objectSize : sizeof(block); + + head_of_free_list = head = 0; + + //debug + if(for_class) name = for_class; + allocs= + frees= + max = + current= + blocks = 0; +} + +fixed_size_allocator::~fixed_size_allocator() +{ + while(head) { + char *p = (char*)head; + head = head->next; + delete [] p; + } + fprintf(stderr, "allocator [%s](%d) destroyed\n", name.c_str(), object_size); + fprintf(stderr, " allocs:%i, ", allocs); + fprintf(stderr, "frees:%i, ", frees); + fprintf(stderr, "max:%i, ", max); + fprintf(stderr, "blocks:%i\n\n", blocks); +} + +void* fixed_size_allocator::alloc( size_t ) { + allocs++;current++; + if(current>max)max=current; + void* p = head_of_free_list; + if(p) { + head_of_free_list = get_next(p); + } else { + + char* new_block = new char[sizeof(block_head) + num_objects * object_size]; + ((block_head*)new_block)->next = head; + head = (block_head*)new_block; + new_block += sizeof(block_head); + for(std::size_t i = object_size; i < (num_objects - 1) * object_size; i += object_size) { + set_next(&new_block[i], &new_block[i + object_size]); + } + set_next(&new_block[(num_objects - 1) * object_size], 0); + p = new_block; + head_of_free_list = &new_block[object_size]; +/// fprintf(stderr, "pool(%i) new block created\n", object_size); + blocks++; + } +#ifdef _DEBUG + *((int *)p) = 0x31415695; + p=((char*)p)+sizeof(int); +#endif + return p; +} +#include +#ifndef ASSERT +# define ASSERT(X) assert(X) +#endif + +void fixed_size_allocator::free( void* p, size_t ) { + if(p == 0) return; + frees++;current--; +#ifdef _DEBUG + p=((char*)p)-sizeof(int); + ASSERT(*((int *)p) == 0x31415695); +#endif + block* dead_object = static_cast(p); + + dead_object->next = static_cast(head_of_free_list); + head_of_free_list = dead_object; +} +typedef std::map allocator_pool_t; +typedef std::map::iterator allocator_pool_it; + +static class _allocator_pool +{ +public: + _allocator_pool() : last_allocate_allocator(0), last_free_allocator(0) { + last_ok = last_access = 0; + } + ~_allocator_pool() { + for(allocator_pool_it it = allocator_pool.begin(); it!=allocator_pool.end(); it++) + delete it->second; + if(last_access) fprintf(stderr, "last_ok:%i(%i)=%i%%\n", last_ok, last_access, (last_ok*100)/last_access); +// getc(stdin); + } + allocator_pool_t allocator_pool; + fixed_size_allocator *last_allocate_allocator; + fixed_size_allocator *last_free_allocator; + int last_ok; + int last_access; +}allocator_pool; +fixed_size_allocator *fixed_size_allocator::get(size_t size, bool for_alloc, const char *for_class) { +#ifdef _DEBUG + size += sizeof(int); +#endif + allocator_pool.last_access++; + if(allocator_pool.last_allocate_allocator && allocator_pool.last_allocate_allocator->object_size==size) { + allocator_pool.last_ok++; + return allocator_pool.last_allocate_allocator; + } + else if(allocator_pool.last_free_allocator && allocator_pool.last_free_allocator->object_size==size) { + allocator_pool.last_ok++; + return allocator_pool.last_free_allocator; + } + allocator_pool_it it = allocator_pool.allocator_pool.find(size); + if(it != allocator_pool.allocator_pool.end()) + return (for_alloc ? allocator_pool.last_allocate_allocator : allocator_pool.last_free_allocator) = it->second; + else { +/// fprintf(stderr, "new allocator(%i) for %s\n", size, for_class); + return (for_alloc ? allocator_pool.last_allocate_allocator : allocator_pool.last_free_allocator) = allocator_pool.allocator_pool[size] = new fixed_size_allocator(64, size, for_class); + } +} diff --git a/pool_allocator.h b/pool_allocator.h new file mode 100644 index 0000000..fb71ee6 --- /dev/null +++ b/pool_allocator.h @@ -0,0 +1,107 @@ + +#ifndef pool_allocator_h__ +#define pool_allocator_h__ + +#include +#include +#include + +struct block_head; +class fixed_size_allocator { +public: + ~fixed_size_allocator(); + void* alloc(size_t); + void free(void* p, size_t); + static fixed_size_allocator *get(size_t,bool,const char* for_class=0); +private: + fixed_size_allocator(size_t num_objects, size_t object_size, const char* for_class); + fixed_size_allocator(const fixed_size_allocator&); + fixed_size_allocator& operator=(const fixed_size_allocator&); + size_t num_objects; + size_t object_size; + void *head_of_free_list; + block_head *head; + // Debug + std::string name; + int allocs; + int frees; + int current; + int max; + int blocks; +}; +//************************************************************************************** +template +class fixed_size_object { +public: + static void* operator new(size_t size) { + return fixed_size_allocator::get(size, true, typeid(T).name())->alloc(size); + } + static void* operator new(size_t size, void* p) { + return p; + } + static void operator delete(void* p, size_t size) { + fixed_size_allocator::get(size, false)->free(p, size); + } +private: +}; +#if 0 // under construction +template +class block_allocator_stl { +public : + // typedefs + + typedef T value_type; + typedef value_type* pointer; + typedef const value_type* const_pointer; + typedef value_type& reference; + typedef const value_type& const_reference; + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; +public : + // convert an allocator to allocator + + template + struct rebind { + typedef block_allocator_stl other; + }; + + inline explicit block_allocator_stl() {} + inline ~block_allocator_stl() {} + inline block_allocator_stl(block_allocator_stl const&) {} + template + inline block_allocator_stl(block_allocator_stl const&) {} + inline block_allocator_stl &operator=(block_allocator_stl const&) {} + template + inline block_allocator_stl &operator=(block_allocator_stl const&) {} + + // address + + inline pointer address(reference r) { return &r; } + inline const_pointer address(const_reference r) { return &r; } + + // memory allocation + inline pointer allocate(size_type cnt, const void*) { + return reinterpret_cast(fixed_size_allocator::get(cnt * sizeof (T), true, typeid(T).name())->alloc(cnt * sizeof (T))); + // return reinterpret_cast(::operator new(cnt * sizeof (T))); + } + inline void deallocate(pointer p, size_type cnt) { + fixed_size_allocator::get(cnt * sizeof (T), false)->free(p, cnt * sizeof (T)); + // ::operator delete(p); + } + + // size + + inline size_type max_size() const { + return SIZE_MAX / sizeof(T); + } + + inline void construct(pointer _Ptr, value_type& _Val) { + ::new ((void*)_Ptr) value_type(_Val); + } + inline void destroy(pointer _Ptr) { + _Ptr->~value_type(); + } +}; +#endif + +#endif // pool_allocator_h__ \ No newline at end of file diff --git a/run_tests.cpp b/run_tests.cpp index 6703a9e..16aa3c4 100644 --- a/run_tests.cpp +++ b/run_tests.cpp @@ -26,13 +26,13 @@ /* * This is a program to run all the tests in the tests folder... */ -//#define _AFXDLL + #ifdef _DEBUG # ifdef _MSC_VER # ifdef USE_DEBUG_NEW -# include "targetver.h" -# include -# define new DEBUG_NEW +//# include "targetver.h" +//# include +//# define new DEBUG_NEW # endif # else # define DEBUG_MEMORY 1 @@ -42,13 +42,18 @@ #include "TinyJS.h" #include "TinyJS_Functions.h" #include "TinyJS_MathFunctions.h" +#include "TinyJS_StringFunctions.h" #include #include #include #include #include +//#define WITH_TIME_LOGGER +#ifdef WITH_TIME_LOGGER +# include "time_logger.h" +#endif //#define INSANE_MEMORY_DEBUG #ifdef INSANE_MEMORY_DEBUG @@ -195,7 +200,18 @@ void memtracing_kill() { } } #endif // INSANE_MEMORY_DEBUG - +class end { // this is for VisualStudio debugging stuff. It's holds the console open up to ENTER is pressed +public: + end() : active(false) {} + ~end() + { + if(active) { + printf("press Enter (end)"); + getchar(); + } + } + bool active; +} end; bool run_test(const char *filename) { printf("TEST %s ", filename); @@ -220,25 +236,31 @@ bool run_test(const char *filename) { CTinyJS s; registerFunctions(&s); registerMathFunctions(&s); - s.root->addChild("result", new CScriptVar("0",SCRIPTVAR_INTEGER)); + registerStringFunctions(&s); + s.getRoot()->addChild("result", s.newScriptVar(0)); +#ifdef WITH_TIME_LOGGER + TimeLoggerCreate(Test, true, filename); +#endif try { s.execute(buffer); } catch (CScriptException *e) { printf("ERROR: %s\n", e->text.c_str()); delete e; } - bool pass = s.root->getParameter("result")->getBool(); + bool pass = s.getRoot()->getParameter("result")->getBool(); +#ifdef WITH_TIME_LOGGER + TimeLoggerLogprint(Test); +#endif if (pass) printf("PASS\n"); else { char fn[64]; - sprintf(fn, "%s.fail.js", filename); + sprintf(fn, "%s.fail.txt", filename); FILE *f = fopen(fn, "wt"); if (f) { - std::ostringstream symbols; - s.root->getJSON(symbols); - fprintf(f, "%s", symbols.str().c_str()); + std::string symbols = s.getRoot()->getParsableString("", " "); + fprintf(f, "%s", symbols.c_str()); fclose(f); } @@ -256,25 +278,38 @@ int main(int argc, char **argv) #endif printf("TinyJS test runner\n"); printf("USAGE:\n"); - printf(" ./run_tests test.js : run just one test\n"); - printf(" ./run_tests : run all tests\n"); - if (argc==2) { - return !run_test(argv[1]); + printf(" ./run_tests [-k] test.js [test2.js] : run tests\n"); + printf(" ./run_tests [-k] : run all tests\n"); + int arg_num = 1; + bool runs = false; + for(; arg_num Application true - Unicode + MultiByte Application false true - Unicode + MultiByte diff --git a/time_logger.h b/time_logger.h new file mode 100644 index 0000000..653d5e8 --- /dev/null +++ b/time_logger.h @@ -0,0 +1,97 @@ +#ifndef time_logger_h__ +#define time_logger_h__ +#ifdef _DEBUG + +#include +#include +#include +#ifdef _WIN32 +#include +#else +#include +#endif +class TimeLogger { +public: + TimeLogger(const char *Name, bool Started=false, const char *extName=0) + : + name(Name), start_time(gettime()), sum_time(0), calls(0), started(Started) { + if(extName) { + name+="["; + name+=extName; + name+="]"; + } + } + TimeLogger(const char *Name, const char *extName, bool Started=false) + : + name(Name), start_time(gettime()), sum_time(0), calls(0), started(Started) { + if(extName) { + name+="["; + name+=extName; + name+="]"; + } + } + ~TimeLogger() { + printLog(); +// getchar(); + } + void startTimer() { + start_time = gettime(); + started = true; + } + void stopTimer() { + if(!started) return; + sum_time += gettime()-start_time; + calls++; + started = false; + } + void printLog() { + if(started) stopTimer(); + if(calls == 1) + printf("Timer( %s ) = %d,%06d sec \n", + name.c_str(), (int)(sum_time / 1000000LL), (int)(sum_time % 1000000LL)); + else if(calls>1) + printf("Timer( %s ) = %d,%06d sec (called %d times) -> %.d microsec per call\n", + name.c_str(), (int)(sum_time / 1000000LL), (int)(sum_time % 1000000LL), + calls, (int)(sum_time/calls)); + calls = 0; sum_time = 0; + } +private: +// static int64_t frequenzy = 0; + std::string name; + int64_t start_time, sum_time; + uint32_t calls; + bool started; + int64_t gettime() { // set out to time in millisec +#ifdef _WIN32 + static LARGE_INTEGER fr = {0}; + LARGE_INTEGER li; + if(fr.QuadPart == 0) QueryPerformanceFrequency(&fr); + QueryPerformanceCounter(&li); + return (li.QuadPart * 1000000LL) / fr.QuadPart; +#else + return (clock() * 1000000LL) / CLOCKS_PER_SEC; +#endif + }; +}; +class _TimeLoggerHelper { +public: + _TimeLoggerHelper(TimeLogger &Tl) : tl(Tl) { tl.startTimer(); } + ~_TimeLoggerHelper() { tl.stopTimer(); } +private: + TimeLogger &tl; +}; +# define TimeLoggerCreate(a, ...) TimeLogger a##_TimeLogger(#a,##__VA_ARGS__) +# define TimeLoggerStart(a) a##_TimeLogger.startTimer() +# define TimeLoggerStop(a) a##_TimeLogger.stopTimer() +# define TimeLoggerLogprint(a) a##_TimeLogger.printLog() +# define TimeLoggerHelper(a) _TimeLoggerHelper a##_helper(a##_TimeLogger) +#else /* _DEBUG */ +# define TimeLoggerCreate(...) +# define TimeLoggerStart(...) do{}while(0) +# define TimeLoggerStop(...) do{}while(0) +# define TimeLoggerLogprint(a) do{}while(0) +#endif /* _DEBUG */ + + + +#endif // time_logger_h__ \ No newline at end of file From 9808352e96e90a81d6ba8c1387cf8fa02a6b48f9 Mon Sep 17 00:00:00 2001 From: armin Date: Sun, 1 Jan 2012 11:45:47 +0000 Subject: [PATCH 35/43] 42tiny-js: - fixed Number-constructor - remove the old complicated recursions-check-stuff it's already replaced by the last commit - clean up some code-stuff --- Script.vcxproj | 1 - Script.vcxproj.filters | 3 - TinyJS.cpp | 307 +++++++------------------------------- TinyJS.h | 62 ++------ TinyJS_Functions.cpp | 8 - run_tests.vcxproj | 4 - run_tests.vcxproj.filters | 8 - targetver.h | 24 --- 8 files changed, 65 insertions(+), 352 deletions(-) delete mode 100644 targetver.h diff --git a/Script.vcxproj b/Script.vcxproj index 7de64ff..92bb1c2 100644 --- a/Script.vcxproj +++ b/Script.vcxproj @@ -93,7 +93,6 @@ - diff --git a/Script.vcxproj.filters b/Script.vcxproj.filters index 1f6996d..b432d09 100644 --- a/Script.vcxproj.filters +++ b/Script.vcxproj.filters @@ -23,9 +23,6 @@ Headerdateien - - Headerdateien - diff --git a/TinyJS.cpp b/TinyJS.cpp index 7a1c155..0c39f70 100755 --- a/TinyJS.cpp +++ b/TinyJS.cpp @@ -1476,7 +1476,7 @@ CScriptVarLink::~CScriptVarLink() { #if DEBUG_MEMORY mark_deallocated(this); #endif - var->unref(this->owner); + var->unref(); } CScriptVarPtr CScriptVarLink::getValue() { @@ -1504,11 +1504,10 @@ void CScriptVarLink::replaceWith(const CScriptVarPtr &newVar) { ASSERT(this->owner); (*setter)->getContext()->callFunction(setter, Params, owner, execute); } - } else { + } else if(var){ CScriptVar *oldVar = var; var = newVar->ref(); - oldVar->unref(owner); - if(owner) var->recoursionCheck(owner); + oldVar->unref(); } } @@ -1530,9 +1529,6 @@ CScriptVar::CScriptVar(CTinyJS *Context, const CScriptVarPtr &Prototype) : conte context->first = this; prev = 0; refs = 0; - internalRefs = 0; - recursionFlag = 0; - recursionSet = 0; if(Prototype) addChild(TINYJS___PROTO___VAR, Prototype, 0); else if(context->objectPrototype) @@ -1551,9 +1547,6 @@ CScriptVar::CScriptVar(const CScriptVar &Copy) : context(Copy.context), temporar context->first = this; prev = 0; refs = 0; - internalRefs = 0; - recursionFlag = 0; - recursionSet = 0; SCRIPTVAR_CHILDS_cit it; for(it = Copy.Childs.begin(); it!= Copy.Childs.end(); ++it) { addChild((*it)->name, (*it), (*it)->flags); @@ -1705,7 +1698,6 @@ CScriptVarLink *CScriptVar::addChild(const string &childName, const CScriptVarPt if(it == Childs.end() || (*it)->name != childName) { it = Childs.insert(it, link); } - (*link)->recoursionCheck(this); return link; } CScriptVarLink *CScriptVar::addChildNoDup(const string &childName, const CScriptVarPtr &child, int linkFlags /*= SCRIPTVARLINK_DEFAULT*/) { @@ -1722,7 +1714,6 @@ CScriptVarLink *CScriptVar::addChildNoDup(const string &childName, const CScript if(it == Childs.end() || (*it)->name != childName) { it = Childs.insert(it, link); } - (*link)->recoursionCheck(this); } return link; @@ -1974,48 +1965,29 @@ CScriptVarPtr CScriptVar::mathsOp(const CScriptVarPtr &b, int op) { void CScriptVar::trace(const string &name) { string indentStr; - trace(indentStr, name); + int uniqueID = context->getUniqueID(); + trace(indentStr, uniqueID, name); } -void CScriptVar::trace(string &indentStr, const string &name) { +void CScriptVar::trace(string &indentStr, int uniqueID, const string &name) { string indent = " "; - if(recursionSet) { - if(recursionFlag) { - int pos = recursionFlag>>12; - string indent_r = indentStr.substr(0, pos+1) + string(indentStr.length()-(pos+1), '-'); - TRACE("%s'%s' = '%s' %s\n", - indent_r.c_str(), - name.c_str(), - getString().c_str(), - getFlagsAsString().c_str()); - - if((recursionFlag&0xfff) == 1) { - indentStr.replace(pos,1," "); - recursionFlag = 0; - } else - recursionFlag--; - return; - } else if(!recursionSet->recursionPathBase || internalRefs>1) { - indent = "| "; - recursionSet->recursionPathBase = this; - } - recursionFlag = indentStr.length()<<12 | (internalRefs&0xfff) ; - } - TRACE("%s'%s' = '%s' %s\n", + char *extra=""; + if(temporaryID == uniqueID) + extra = " recursion detected"; + TRACE("%s'%s' = '%s' %s%s\n", indentStr.c_str(), name.c_str(), getString().c_str(), - getFlagsAsString().c_str()); - - indentStr+=indent; - for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it) { - if((*it)->IsEnumerable()) - (*(*it))->trace(indentStr, (*it)->name); - } - if(recursionSet) { - recursionSet->recursionPathBase = 0; - recursionFlag = 0; + getFlagsAsString().c_str(), + extra); + if(temporaryID != uniqueID) { + temporaryID = uniqueID; + indentStr+=indent; + for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it) { + if((*it)->IsEnumerable()) + (*(*it))->trace(indentStr, uniqueID, (*it)->name); + } + indentStr = indentStr.substr(0, indentStr.length()-2); } - indentStr = indentStr.substr(0, indentStr.length()-2); } string CScriptVar::getFlagsAsString() { @@ -2073,189 +2045,19 @@ void CScriptVar::getJSON(ostringstream &destination, const string linePrefix) { CScriptVar *CScriptVar::ref() { refs++; - if(recursionSet) recursionSet->sumRefs++; return this; } -RECURSION_SET_VAR *CScriptVar::unrefInternal(){ - RECURSION_SET_VAR *ret=0; - if(internalRefs) { - ASSERT(recursionSet); - internalRefs--; - recursionSet->sumInternalRefs--; - if(internalRefs==0) { - recursionSet->sumRefs -= refs; - recursionSet->recursionSet.erase(this); - if(recursionSet->recursionSet.size()) { - for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it!= Childs.end(); ++it) { - if((*(*it))->recursionSet == recursionSet) - (*(*it))->unrefInternal(); - } - ret = recursionSet; - } else - delete recursionSet; - recursionSet = 0; - } - } - return ret; -} -void CScriptVar::unref(CScriptVar* Owner) { +void CScriptVar::unref() { refs--; ASSERT(refs>=0); // printf("OMFG, we have unreffed too far!\n"); - if(recursionSet) { // this Var is in a Set - recursionSet->sumRefs--; - if(Owner && Owner->recursionSet == recursionSet) { // internal Ref - if(internalRefs==1) { - if(refs) { - RECURSION_SET_VAR *old_set = unrefInternal(); - - if(old_set) { // we have breaked a recursion bu the recursion Set is not destroyed - // we needs a redetection of recursions for all Vars in the Set; - // first we destroy the Set - for(RECURSION_SET::iterator it= old_set->recursionSet.begin(); it!=old_set->recursionSet.end(); ++it) { - (*it)->internalRefs = 0; - (*it)->recursionSet = 0; - } - // now we can redetect recursions - for(RECURSION_SET::iterator it= old_set->recursionSet.begin(); it!=old_set->recursionSet.end(); ++it) { - if((*it)->recursionSet == 0) - (*it)->recoursionCheck(); - } - delete old_set; - } - - } else { - removeAllChildren(); - internalRefs--; - recursionSet->sumInternalRefs--; - recursionSet->recursionSet.erase(this); - if(!recursionSet->recursionSet.size()) - delete recursionSet; - delete this; - } - }// otherwise nothing to do - } - else { //external Ref - ASSERT(refs); // by a Set it is not possible to remove an external Ref as the last ref - if(recursionSet->sumRefs==recursionSet->sumInternalRefs) { - SCRIPTVAR_CHILDS_t copyOfChilds(Childs); - Childs.clear(); - SCRIPTVAR_CHILDS_it it; - for(it = copyOfChilds.begin(); it!= copyOfChilds.end(); ++it) { - delete *it; - } - copyOfChilds.clear(); - } // otherwise nothing to do - } - } - else if (refs==0) { + if (refs==0) delete this; - } } int CScriptVar::getRefs() { return refs; } -RECURSION_VECT::iterator find_last(RECURSION_VECT &Vector, const RECURSION_VECT::value_type &Key) { - for(RECURSION_VECT::reverse_iterator rit=Vector.rbegin(); rit!=Vector.rend(); ++rit) { - if(*rit==Key) return --rit.base(); - } - return Vector.end(); -} -void CScriptVar::recoursionCheck(CScriptVar *Owner) { - return; - if(recursionSet && Owner &&recursionSet == Owner->recursionSet) { // special thing for self-linking - Owner->internalRefs++; - Owner->recursionSet->sumInternalRefs++; - } else { - RECURSION_VECT recursionPath; - recoursionCheck(recursionPath); - } -} -void CScriptVar::recoursionCheck(RECURSION_VECT &recursionPath) -{ - if(recursionFlag || (recursionSet && recursionSet->recursionPathBase)) { // recursion found - create a new set - RECURSION_SET_VAR *new_set; - RECURSION_VECT::iterator it; - - if(recursionSet) { // recursion starts by a Set - new_set = recursionSet; // we use the old Set as new Set - new_set->sumInternalRefs++; // one internal - this->internalRefs++; // new internal reference found - // find the Var of the Set tat is in the Path - // +1, because the Set is already in the new Set - it = find_last(recursionPath, recursionSet->recursionPathBase)+1; - } else { // recursion starts by a Var - new_set = new RECURSION_SET_VAR(this); // create a new Set(marks it as "in the Path", with "this as start Var) - // find the Var in the Path - it = find_last(recursionPath, this); - } - // insert the Path beginning by this Var or the next Var after dis Set - // in the new_set and removes it from the Path - for(; it!= recursionPath.end(); ++it) { - if((*it)->recursionSet) { // insert an existing Set - RECURSION_SET_VAR *old_set = (*it)->recursionSet; - new_set->sumInternalRefs += old_set->sumInternalRefs; // for speed up; we adds the sum of internal refs and the sum of - new_set->sumRefs += old_set->sumRefs; // refs to the new Set here instead rather than separately for each Var - new_set->sumInternalRefs++; // a new Set in a Set is linked - (*it)->internalRefs++; // over an internal reference - // insert all Vars of the old Set in the new Set - for(RECURSION_SET::iterator set_it = old_set->recursionSet.begin(); set_it!= old_set->recursionSet.end(); ++set_it) { - new_set->recursionSet.insert(*set_it); // insert this Var to the Set - (*set_it)->recursionSet = new_set; // the Var is now in a Set - } - delete old_set; - } else { // insert this Var in the new set - new_set->sumInternalRefs++; // a new Var in a Set is linked - (*it)->internalRefs++; // over an internal reference - new_set->sumRefs += (*it)->refs; // adds the Var refs to the Set refs - new_set->recursionSet.insert(*it); // insert this Var to the Set - (*it)->recursionSet = new_set; // the Var is now in a Set - (*it)->recursionFlag = 0; // clear the Var-recursionFlag, because the Var in now in a Set - } - } - // all Vars inserted in the new Set - // remove from Path all Vars from the end up to the start of Set (without the start of Set) - while(recursionPath.back() != new_set->recursionPathBase) - recursionPath.pop_back(); - } else if(recursionSet) { // found a recursionSet - recursionPath.push_back(this); // push "this" in the Path - recursionSet->recursionPathBase = this; // of all Vars in the Set is "this" in the Path - RECURSION_SET_VAR *old_set = recursionSet; - // a recursionSet is like an one&only Var - // goes to all Childs of the Set - RECURSION_SET Set(recursionSet->recursionSet); - for(RECURSION_SET::iterator set_it = Set.begin(); set_it != Set.end(); ++set_it) { - for(SCRIPTVAR_CHILDS_it it = (*set_it)->Childs.begin(); it != (*set_it)->Childs.end(); ++it) { - if((*(*it))->recursionSet != recursionSet) - (*(*it))->recoursionCheck(recursionPath); - } - } - if(old_set == recursionSet) { // old_set == recursionSet means this Set is *not* included in an other Set - recursionSet->recursionPathBase = 0; - recursionPath.pop_back(); - } // otherwise this Set is included in an other Set and is already removed from Path - } else { - recursionPath.push_back(this); // push "this" in the Path - recursionFlag = 1; // marked this Var as "the Var is in the Path" - // goes to all Childs of the Var - for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it) { - (*(*it))->recoursionCheck(recursionPath); - } - if(recursionFlag) { // recursionFlag!=0 means this Var is not included in a Set - recursionFlag = 0; - ASSERT(recursionPath.back() == this); - recursionPath.pop_back(); - } else { // otherwise this Var is included in a Set and is already removed from Path - ASSERT(recursionSet && recursionSet->recursionPathBase); - if(recursionSet->recursionPathBase == this) { // the Var is the Base of the Set - recursionSet->recursionPathBase = 0; - ASSERT(recursionPath.back() == this); - recursionPath.pop_back(); - } - } - } -} void CScriptVar::setTempraryIDrecursive(int ID) { if(temporaryID != ID) { temporaryID = ID; @@ -2687,7 +2489,7 @@ CTinyJS::CTinyJS() { ////////////////////////////////////////////////////////////////////////// // Scopes root = ::newScriptVar(this, Scope); - _scopes.push_back(root); + scopes.push_back(root); ////////////////////////////////////////////////////////////////////////// @@ -2715,7 +2517,7 @@ CTinyJS::CTinyJS() { ////////////////////////////////////////////////////////////////////////// // Number numberPrototype = newScriptVar(Object); - var = addNative("function Number()", this, &CTinyJS::native_String); + var = addNative("function Number()", this, &CTinyJS::native_Number); var->addChild(TINYJS_PROTOTYPE_CLASS, numberPrototype); ////////////////////////////////////////////////////////////////////////// @@ -4250,46 +4052,45 @@ void CTinyJS::native_String(const CFunctionsScopePtr &c, void *data) { else c->setReturnVar(c->newScriptVar(c->getParameter(0)->getString())); } +void CTinyJS::native_Number(const CFunctionsScopePtr &c, void *data) { + if(c->getParameterLength()==0) + c->setReturnVar(c->newScriptVar(0)); + else + c->setReturnVar(c->getParameter(0)->getNumericVar()); +} void CTinyJS::native_Eval(const CFunctionsScopePtr &c, void *data) { string Code = c->getParameter("jsCode")->getString(); - CScriptVarScopePtr scEvalScope = scope(); // save scope - _scopes.pop_back(); // go back to the callers scope + CScriptVarScopePtr scEvalScope = scopes.back(); // save scope + scopes.pop_back(); // go back to the callers scope + CScriptVarSmartLink returnVar; + CScriptTokenizer *oldTokenizer = t; t=0; try { - CScriptVarSmartLink returnVar; - CScriptTokenizer *oldTokenizer = t; t=0; - t = new CScriptTokenizer(Code.c_str(), "eval"); - try { - bool execute = true; - do { - returnVar = execute_statement(execute); - while (t->tk==';') t->match(';'); // skip empty statements - } while (t->tk!=LEX_EOF); - } catch (CScriptException *e) { - delete t; - t = oldTokenizer; - throw e; - } - delete t; - t = oldTokenizer; - - _scopes.push_back(scEvalScope); // restore Scopes; - if(returnVar) - c->setReturnVar(returnVar->getVarPtr()); - - // check of exceptions - int exceptionState = runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE); - runtimeFlags &= ~RUNTIME_LOOP_MASK; - if(exceptionState) throw new CScriptEvalException(exceptionState); - + CScriptTokenizer Tokenizer(Code.c_str(), "eval"); + t = &Tokenizer; + bool execute = true; + do { + returnVar = execute_statement(execute); + while (t->tk==';') t->match(';'); // skip empty statements + } while (t->tk!=LEX_EOF); } catch (CScriptException *e) { - _scopes.push_back(scEvalScope); // restore Scopes; + t = oldTokenizer; + scopes.push_back(scEvalScope); // restore Scopes; throw e; } + t = oldTokenizer; + scopes.push_back(scEvalScope); // restore Scopes; + if(returnVar) + c->setReturnVar(returnVar->getVarPtr()); + + // check of exceptions + int exceptionState = runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE); + runtimeFlags &= ~RUNTIME_LOOP_MASK; + if(exceptionState) throw new CScriptEvalException(exceptionState); } void CTinyJS::native_JSON_parse(const CFunctionsScopePtr &c, void *data) { string Code = "�" + c->getParameter("text")->getString(); - + // "�" is a spezal-token - it's for the tokenizer and means the code begins not in Statement-level CScriptVarSmartLink returnVar; CScriptTokenizer *oldTokenizer = t; t=0; try { @@ -4371,7 +4172,7 @@ void CTinyJS::ClearLostVars(const CScriptVarPtr &extra/*=CScriptVarPtr()*/) { var->ref(); var->removeAllChildren(); p = var->next; - var->unref(0); + var->unref(); } else p = p->next; diff --git a/TinyJS.h b/TinyJS.h index d469fd0..4078018 100755 --- a/TinyJS.h +++ b/TinyJS.h @@ -462,11 +462,11 @@ class CScriptVarPointer { CScriptVarPointer(const CScriptVarLink *Link); CScriptVarPointer(const CScriptVarSmartLink &Link); //: var(dynamic_cast(Var)) { if(Var) ASSERT(var); if(var) var->ref(); } - ~CScriptVarPointer() { if(var) var->unref(0); } + ~CScriptVarPointer() { if(var) var->unref(); } CScriptVarPointer(const CScriptVarPointer &VarPtr) : var(0) { *this = VarPtr; } CScriptVarPointer& operator=(const CScriptVarPointer &VarPtr) { if(var != VarPtr.var) { - if(var) var->unref(0); + if(var) var->unref(); var = VarPtr.var; if(var) var->ref(); } return *this; @@ -533,20 +533,6 @@ class CScriptVarLink : public fixed_size_object template CScriptVarPointer::CScriptVarPointer(const CScriptVarLink *Link) : var(0) { if(Link) { var = dynamic_cast(Link->operator->()); ASSERT(var); } if(var) var->ref(); } -// ----------------------------------------------------------------------------------- RECURSION -typedef std::vector RECURSION_VECT; -typedef std::set RECURSION_SET; -class RECURSION_SET_VAR -{ -public: - RECURSION_SET_VAR(CScriptVar *PathBase) : sumRefs(0), sumInternalRefs(0), recursionPathBase(PathBase) {} - RECURSION_SET recursionSet; ///< contains all Vars in the Set - int sumRefs; ///< Sum of Refs - int sumInternalRefs; ///< Sum of Internal Refs --> sumRefs - sumInternalRefs = sumExternalRefs - CScriptVar *recursionPathBase; ///< temporary used in recoursionCheck() and trace() -}; - - // ----------------------------------------------------------------------------------- CSCRIPTVAR typedef std::vector SCRIPTVAR_CHILDS_t; typedef SCRIPTVAR_CHILDS_t::iterator SCRIPTVAR_CHILDS_it; @@ -629,16 +615,15 @@ class CScriptVar : public fixed_size_object { CScriptVarPtr mathsOp(const CScriptVarPtr &b, int op); ///< do a maths op with another script variable void trace(const std::string &name = ""); ///< Dump out the contents of this using trace - void trace(std::string &indentStr, const std::string &name = ""); ///< Dump out the contents of this using trace + void trace(std::string &indentStr, int uniqueID, const std::string &name = ""); ///< Dump out the contents of this using trace std::string getFlagsAsString(); ///< For debugging - just dump a string version of the flags // void getJSON(std::ostringstream &destination, const std::string linePrefix=""); ///< Write out all the JS code needed to recreate this script variable to the stream (as JSON) SCRIPTVAR_CHILDS_t Childs; /// For memory management/garbage collection - void recoursionCheck(CScriptVar *Owner=0); CScriptVar *ref(); ///< Add reference to this variable - void unref(CScriptVar* Owner); ///< Remove a reference, and delete this variable if required + void unref(); ///< Remove a reference, and delete this variable if required int getRefs(); ///< Get the number of references to this script variable template operator T *(){ T* ret = dynamic_cast(this); ASSERT(ret!=0); return ret; } @@ -650,16 +635,9 @@ class CScriptVar : public fixed_size_object { void setTempraryID(int ID) { temporaryID = ID; } void setTempraryIDrecursive(int ID); int getTempraryID() { return temporaryID; } -private: - RECURSION_SET_VAR *unrefInternal(); protected: CTinyJS *context; int refs; ///< The number of references held to this - used for garbage collection - int internalRefs; - - void recoursionCheck(RECURSION_VECT &recursionPath); - int recursionFlag; - RECURSION_SET_VAR *recursionSet; CScriptVar *prev; public: CScriptVar *next; @@ -1207,40 +1185,21 @@ class CTinyJS { CScriptTokenizer *t; /// current tokenizer int runtimeFlags; std::vector loop_labels; -// std::vector _scopes; /// stack of scopes when parsing - std::vector_scopes; + std::vectorscopes; CScriptVarScopePtr root; - const CScriptVarScopePtr &scope() { return _scopes.back(); } -/* - const CScriptVarScopePtr &push_FncScope(const CScriptVarScopePtr &Scope) { - _scopes.push_back(Scope); - return scope(); - } + const CScriptVarScopePtr &scope() { return scopes.back(); } - const CScriptVarScopePtr &push_newLetScope() { - return _scopes.back() = ::newScriptVar(this, ScopeLet, _scopes.back()); - } - const CScriptVarScopePtr &push_newWithScope(const CScriptVarPtr &With) { - return _scopes.back() = ::newScriptVar(this, ScopeWith, _scopes.back(), With); - } -*/ -/* - void pop_Scope() { - CScriptVarScopePtr parent = _scopes.back()->getParent(); - if(parent) _scopes.back() = parent; else _scopes.pop_back() ; - } -*/ class CScopeControl { private: CScopeControl(const CScopeControl& Copy); // no copy CScopeControl& operator =(const CScopeControl& Copy); public: CScopeControl(CTinyJS* Context) : context(Context), count(0) {} - ~CScopeControl() { while(count--) {CScriptVarScopePtr parent = context->_scopes.back()->getParent(); if(parent) context->_scopes.back() = parent; else context->_scopes.pop_back() ;} } - void addFncScope(const CScriptVarScopePtr &Scope) { context->_scopes.push_back(Scope); count++; } - void addLetScope() { context->_scopes.back() = ::newScriptVar(context, ScopeLet, context->_scopes.back()); count++; } - void addWithScope(const CScriptVarPtr &With) { context->_scopes.back() = ::newScriptVar(context, ScopeWith, context->_scopes.back(), With); count++; } + ~CScopeControl() { while(count--) {CScriptVarScopePtr parent = context->scopes.back()->getParent(); if(parent) context->scopes.back() = parent; else context->scopes.pop_back() ;} } + void addFncScope(const CScriptVarScopePtr &Scope) { context->scopes.push_back(Scope); count++; } + void addLetScope() { context->scopes.back() = ::newScriptVar(context, ScopeLet, context->scopes.back()); count++; } + void addWithScope(const CScriptVarPtr &With) { context->scopes.back() = ::newScriptVar(context, ScopeWith, context->scopes.back(), With); count++; } private: CTinyJS *context; int count; @@ -1312,6 +1271,7 @@ class CTinyJS { void native_Object(const CFunctionsScopePtr &c, void *data); void native_String(const CFunctionsScopePtr &c, void *data); + void native_Number(const CFunctionsScopePtr &c, void *data); void native_Array(const CFunctionsScopePtr &c, void *data); diff --git a/TinyJS_Functions.cpp b/TinyJS_Functions.cpp index 402232c..5fd80d5 100755 --- a/TinyJS_Functions.cpp +++ b/TinyJS_Functions.cpp @@ -156,14 +156,6 @@ void registerFunctions(CTinyJS *tinyJS) { tinyJS->addNative("function Object.prototype.dump()", scObjectDump, 0); tinyJS->addNative("function Object.prototype.clone()", scObjectClone, 0); -// tinyJS->addNative("function charToInt(ch)", scCharToInt, 0); // convert a character to an int - get its value -// tinyJS->addNative("function String.prototype.indexOf(search)", scStringIndexOf, 0); // find the position of a string in a string, -1 if not -// tinyJS->addNative("function String.prototype.substring(lo,hi)", scStringSubstring, 0); -// tinyJS->addNative("function String.prototype.charAt(pos)", scStringCharAt, 0); -// tinyJS->addNative("function String.prototype.charCodeAt(pos)", scStringCharCodeAt, 0); -// tinyJS->addNative("function String.prototype.fromCharCode(char)", scStringFromCharCode, 0); -// tinyJS->addNative("function String.prototype.split(separator,limit)", scStringSplit, 0); - tinyJS->addNative("function Integer.parseInt(str)", scIntegerParseInt, 0); // string to int tinyJS->addNative("function Integer.valueOf(str)", scIntegerValueOf, 0); // value of a single character tinyJS->addNative("function JSON.stringify(obj, replacer)", scJSONStringify, 0); // convert to JSON. replacer is ignored at the moment diff --git a/run_tests.vcxproj b/run_tests.vcxproj index 40b8ddb..e34f207 100644 --- a/run_tests.vcxproj +++ b/run_tests.vcxproj @@ -78,10 +78,6 @@ - - - - diff --git a/run_tests.vcxproj.filters b/run_tests.vcxproj.filters index 87407e0..c2dec68 100644 --- a/run_tests.vcxproj.filters +++ b/run_tests.vcxproj.filters @@ -17,14 +17,6 @@ - - - Headerdateien - - - Headerdateien - - Quelldateien diff --git a/targetver.h b/targetver.h deleted file mode 100644 index d9cc11b..0000000 --- a/targetver.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -// Die folgenden Makros definieren die mindestens erforderliche Plattform. Die mindestens erforderliche Plattform -// ist die fr�heste Windows-, Internet Explorer-Version usw., die �ber die erforderlichen Features zur Ausf�hrung -// Ihrer Anwendung verf�gt. Die Makros aktivieren alle Funktionen, die auf den Plattformversionen bis -// einschlie�lich der angegebenen Version verf�gbar sind. - -// �ndern Sie folgende Definitionen f�r Plattformen, die �lter als die unten angegebenen sind. -// Unter MSDN finden Sie die neuesten Informationen �ber die entsprechenden Werte f�r die unterschiedlichen Plattformen. -#ifndef WINVER // Gibt an, dass Windows Vista die mindestens erforderliche Plattform ist. -#define WINVER 0x0600 // �ndern Sie den entsprechenden Wert, um auf andere Versionen von Windows abzuzielen. -#endif - -#ifndef _WIN32_WINNT // Gibt an, dass Windows Vista die mindestens erforderliche Plattform ist. -#define _WIN32_WINNT 0x0600 // �ndern Sie den entsprechenden Wert, um auf andere Versionen von Windows abzuzielen. -#endif - -#ifndef _WIN32_WINDOWS // Gibt an, dass Windows 98 die mindestens erforderliche Plattform ist. -#define _WIN32_WINDOWS 0x0410 // �ndern Sie den entsprechenden Wert, um auf mindestens Windows Me abzuzielen. -#endif - -#ifndef _WIN32_IE // Gibt an, dass Internet Explorer 7.0 die mindestens erforderliche Plattform ist. -#define _WIN32_IE 0x0700 // �ndern Sie den entsprechenden Wert, um auf andere Versionen von IE abzuzielen. -#endif From 4dcd10cdee03eea713d76977c3349df61363ed31 Mon Sep 17 00:00:00 2001 From: armin Date: Sun, 1 Jan 2012 14:36:21 +0000 Subject: [PATCH 36/43] 42tiny-js: - added Lexer can now detect regular expression literals but using of regular expression are currently unsupported - added function closure test "tests/42tests/test002.js" --- TinyJS.cpp | 111 ++++++++++++++++++++++++--------------- TinyJS.h | 14 +++-- TinyJS_MathFunctions.cpp | 40 +++++++------- 3 files changed, 99 insertions(+), 66 deletions(-) diff --git a/TinyJS.cpp b/TinyJS.cpp index 0c39f70..acf3146 100755 --- a/TinyJS.cpp +++ b/TinyJS.cpp @@ -335,7 +335,7 @@ CScriptLex::CScriptLex(const char *Code, const string &File, int Line, int Colum void CScriptLex::reset(const char *toPos, int Line, const char *LineStart) { dataPos = toPos; tokenStart = data; - tk = 0; + tk = last_tk = 0; tkStr = ""; currentLine = Line; currentLineStart = LineStart; @@ -349,7 +349,10 @@ void CScriptLex::check(int expected_tk) { if (expected_tk==';' && tk==LEX_EOF) return; // ignore last missing ';' if (tk!=expected_tk) { ostringstream errorString; - errorString << "Got " << getTokenStr(tk) << " expected " << getTokenStr(expected_tk); + if(expected_tk == LEX_EOF) + errorString << "Got unexpected " << getTokenStr(tk); + else + errorString << "Got " << getTokenStr(tk) << " expected " << getTokenStr(expected_tk); throw new CScriptException(errorString.str(), currentFile, currentLine, currentColumn()); } } @@ -372,6 +375,7 @@ string CScriptLex::getTokenStr(int token) { case LEX_INT : return "INT"; case LEX_FLOAT : return "FLOAT"; case LEX_STR : return "STRING"; + case LEX_REGEXP : return "REGEXP"; case LEX_EQUAL : return "=="; case LEX_TYPEEQUAL : return "==="; case LEX_NEQUAL : return "!="; @@ -459,10 +463,8 @@ void CScriptLex::getNextCh() { currCh = '\n'; // Mac (only '\r') --> convert '\r' to '\n' } } - +static uint16_t not_allowed_tokens_befor_regexp[] = {LEX_ID, LEX_INT, LEX_FLOAT, LEX_STR, LEX_R_TRUE, LEX_R_FALSE, LEX_R_NULL, LEX_R_UNDEFINED, LEX_R_INFINITY, LEX_R_NAN, ']', ')', '.', LEX_EOF}; void CScriptLex::getNextToken() { - tk = LEX_EOF; - tkStr.clear(); while (currCh && isWhitespace(currCh)) getNextCh(); // newline comments if (currCh=='/' && nextCh=='/') { @@ -479,6 +481,9 @@ void CScriptLex::getNextToken() { getNextToken(); return; } + last_tk = tk; + tk = LEX_EOF; + tkStr.clear(); // record beginning of this token tokenStart = dataPos - (nextCh == LEX_EOF ? (currCh == LEX_EOF ? 0 : 1) : 2); // tokens @@ -682,9 +687,36 @@ void CScriptLex::getNextToken() { } else if (tk=='*' && currCh=='=') { tk = LEX_ASTERISKEQUAL; getNextCh(); - } else if (tk=='/' && currCh=='=') { - tk = LEX_SLASHEQUAL; - getNextCh(); + } else if (tk=='/') { + // check if it's a Regex-Literal + tk = LEX_REGEXP; + for(uint16_t *p = not_allowed_tokens_befor_regexp; *p; p++) { + if(*p==last_tk) { tk = '/'; break; } + } + if(tk == LEX_REGEXP) { + tkStr = "/"; + while (currCh && currCh!='/' && currCh!='\n') { + if (currCh == '\\') { + getNextCh(); + if(currCh != '\n') { // ignore newline after '\' + tkStr.append("\\"); + tkStr.append(1, currCh); + } + } else + tkStr.append(1, currCh); + getNextCh(); + } + if(currCh == '/') { + do { + tkStr.append(1, currCh); + getNextCh(); + } while (currCh && currCh=='g' && currCh=='i' && currCh=='m' && currCh=='y'); + } else + throw new CScriptException("unterminated regular expression literal", currentFile, currentLine, currentColumn()); + } else if(currCh=='=') { + tk = LEX_SLASHEQUAL; + getNextCh(); + } } else if (tk=='%' && currCh=='=') { tk = LEX_PERCENTEQUAL; getNextCh(); @@ -901,7 +933,10 @@ bool CScriptTokenizer::check(int ExpectedToken) { if (ExpectedToken==';' && (currentToken==LEX_EOF || currentToken=='}')) return false; // ignore last missing ';' if (currentToken!=ExpectedToken) { ostringstream errorString; - errorString << "Got " << CScriptLex::getTokenStr(currentToken) << " expected " << CScriptLex::getTokenStr(ExpectedToken); + if(ExpectedToken == LEX_EOF) + errorString << "Got unexpected " << CScriptLex::getTokenStr(currentToken); + else + errorString << "Got " << CScriptLex::getTokenStr(currentToken) << " expected " << CScriptLex::getTokenStr(ExpectedToken); throw new CScriptException(errorString.str(), currentFile, currentLine(), currentColumn()); } return true; @@ -1597,6 +1632,7 @@ string CScriptVar::getString() {return "";} string CScriptVar::getParsableString(const string &indentString, const string &indent) { return getString(); } +/* string CScriptVar::getVarType() { if(this->isBool()) return "boolean"; @@ -1610,35 +1646,9 @@ string CScriptVar::getVarType() { return "undefined"; return "object"; // Object / Array / null } -///< returns an Integer, a Double, an Infinity or a NaN -CScriptVarLink *CScriptVar::getNumericVar() -{ - if(isNumber() || isInfinity()) - return new CScriptVarLink(this); - else if(isNull()) - return new CScriptVarLink(newScriptVar(0)); - else if(isBool()) - return new CScriptVarLink(newScriptVar(getInt())); -/* ToDo - else if(Var->isObject()) - return ToDo call Object.valueOff(); - else if(Var->isArray()) - return ToDo call Object.valueOff(); */ - else if(isString()) { - string the_string = getString(); - double d; - char *endptr;//=NULL; - int i = strtol(the_string.c_str(),&endptr,0); - if(*endptr == '\0') - return new CScriptVarLink(newScriptVar(i)); - if(*endptr=='.' || *endptr=='e' || *endptr=='E') { - d = strtod(the_string.c_str(),&endptr); - if(*endptr == '\0') - return new CScriptVarLink(newScriptVar(d)); - } - } - return new CScriptVarLink(newScriptVar(NaN)); +CScriptVarPtr CScriptVar::getNumericVar() { + return newScriptVar(NaN); } @@ -1898,8 +1908,8 @@ CScriptVarPtr CScriptVar::mathsOp(const CScriptVarPtr &b, int op) { } // gets only an Integer, a Double, in Infinity or a NaN - CScriptVarSmartLink a_l = a->getNumericVar(); - CScriptVarSmartLink b_l = b->getNumericVar(); + CScriptVarPtr a_l = a->getNumericVar(); + CScriptVarPtr b_l = b->getNumericVar(); { CScriptVarPtr a = a_l; CScriptVarPtr b = b_l; @@ -2198,6 +2208,7 @@ CScriptVarPtr CScriptVarNull::clone() { return new CScriptVarNull(*this); } bool CScriptVarNull::isNull() { return true; } string CScriptVarNull::getString() { return "null"; }; string CScriptVarNull::getVarType() { return "null"; } +CScriptVarPtr CScriptVarNull::getNumericVar() { return newScriptVar(0); } ////////////////////////////////////////////////////////////////////////// CScriptVarUndefined @@ -2235,11 +2246,24 @@ int CScriptVarString::getInt() {return strtol(data.c_str(),0,0); } bool CScriptVarString::getBool() {return data.length()!=0;} double CScriptVarString::getDouble() {return strtod(data.c_str(),0);} string CScriptVarString::getString() { return data; } -string CScriptVarString::getVarType() { return "string"; } string CScriptVarString::getParsableString() { return getJSString(data); } +string CScriptVarString::getVarType() { return "string"; } +CScriptVarPtr CScriptVarString::getNumericVar() { + string the_string = getString(); + double d; + char *endptr;//=NULL; + int i = strtol(the_string.c_str(),&endptr,0); + if(*endptr == '\0') + return newScriptVar(i); + if(*endptr=='.' || *endptr=='e' || *endptr=='E') { + d = strtod(the_string.c_str(),&endptr); + if(*endptr == '\0') + return newScriptVar(d); + } + return CScriptVar::getNumericVar(); +} -void CScriptVarString::native_Length(const CFunctionsScopePtr &c, void *data) -{ +void CScriptVarString::native_Length(const CFunctionsScopePtr &c, void *data) { c->setReturnVar(newScriptVar((int)this->data.size())); } @@ -2253,6 +2277,7 @@ bool CScriptVarIntegerBase::getBool() {return data!=0;} double CScriptVarIntegerBase::getDouble() {return data;} string CScriptVarIntegerBase::getString() {return int2string(data);} string CScriptVarIntegerBase::getVarType() { return "number"; } +CScriptVarPtr CScriptVarIntegerBase::getNumericVar() { return this; } ////////////////////////////////////////////////////////////////////////// CScriptVarInteger @@ -2270,6 +2295,7 @@ CScriptVarPtr CScriptVarBool::clone() { return new CScriptVarBool(*this); } bool CScriptVarBool::isBool() { return true; } string CScriptVarBool::getString() {return data!=0?"true":"false";} string CScriptVarBool::getVarType() { return "boolean"; } +CScriptVarPtr CScriptVarBool::getNumericVar() { return newScriptVar(data); } ////////////////////////////////////////////////////////////////////////// CScriptVarInfinity @@ -2295,6 +2321,7 @@ bool CScriptVarDouble::getBool() {return data!=0.0;} double CScriptVarDouble::getDouble() {return data;} string CScriptVarDouble::getString() {return float2string(data);} string CScriptVarDouble::getVarType() { return "number"; } +CScriptVarPtr CScriptVarDouble::getNumericVar() { return this; } ////////////////////////////////////////////////////////////////////////// CScriptVarFunction diff --git a/TinyJS.h b/TinyJS.h index 4078018..dfd64ba 100755 --- a/TinyJS.h +++ b/TinyJS.h @@ -89,6 +89,7 @@ enum LEX_TYPES { #define LEX_TOKEN_STRING_BEGIN LEX_ID LEX_ID, LEX_STR, + LEX_REGEXP, LEX_T_LABEL, #define LEX_TOKEN_STRING_END LEX_T_LABEL @@ -291,6 +292,7 @@ class CScriptLex CScriptLex(const char *Code, const std::string &File="", int Line=0, int Column=0); int tk; ///< The type of the token that we have + int last_tk; ///< The type of the last token that we have const char *tokenStart; std::string tkStr; ///< Data contained in the token we have here @@ -305,7 +307,6 @@ class CScriptLex int currentColumn() { return tokenStart-currentLineStart; } private: -// const std::string &data; const char *data; const char *dataPos; char currCh, nextCh; @@ -581,8 +582,8 @@ class CScriptVar : public fixed_size_object { virtual std::string getString(); ///< return "" virtual std::string getParsableString(const std::string &indentString, const std::string &indent); ///< get Data as a parsable javascript string - virtual std::string getVarType(); - virtual CScriptVarLink *getNumericVar(); ///< returns an Integer, a Double, an Infinity or a NaN + virtual std::string getVarType()=0; + virtual CScriptVarPtr getNumericVar(); ///< returns an Integer, a Double, an Infinity or a NaN /// find CScriptVarLink *findChild(const std::string &childName); ///< Tries to find a child with the given name, may return 0 @@ -738,6 +739,7 @@ class CScriptVarNull : public CScriptVar { virtual bool isNull(); // { return true; } virtual std::string getString(); // { return "null"; }; virtual std::string getVarType(); // { return "null"; } + virtual CScriptVarPtr getNumericVar(); ///< returns an Integer, a Double, an Infinity or a NaN friend define_newScriptVar_Fnc(Null, CTinyJS* Context, Null_t); }; inline define_newScriptVar_Fnc(Null, CTinyJS* Context, Null_t) { return new CScriptVarNull(Context); } @@ -796,8 +798,9 @@ class CScriptVarString : public CScriptVar { virtual bool getBool(); // {return data.length()!=0;} virtual double getDouble(); // {return strtod(data.c_str(),0);} virtual std::string getString(); // { return data; } - virtual std::string getVarType(); // { return "string"; } virtual std::string getParsableString(); // { return getJSString(data); } + virtual std::string getVarType(); // { return "string"; } + virtual CScriptVarPtr getNumericVar(); ///< returns an Integer, a Double, an Infinity or a NaN protected: std::string data; private: @@ -823,6 +826,7 @@ class CScriptVarIntegerBase : public CScriptVar { virtual double getDouble(); // {return data;} virtual std::string getString(); // {return int2string(data);} virtual std::string getVarType(); // { return "number"; } + virtual CScriptVarPtr getNumericVar(); ///< returns an Integer, a Double, an Infinity or a NaN protected: int data; }; @@ -858,6 +862,7 @@ class CScriptVarBool : public CScriptVarIntegerBase { virtual bool isBool(); // { return true; } virtual std::string getString(); // {return data!=0?"true":"false";} virtual std::string getVarType(); // { return "boolean"; } + virtual CScriptVarPtr getNumericVar(); ///< returns an Integer, a Double, an Infinity or a NaN friend define_newScriptVar_Fnc(Bool, CTinyJS* Context, bool); }; inline define_newScriptVar_Fnc(Bool, CTinyJS* Context, bool Obj) { return new CScriptVarBool(Context, Obj); } @@ -900,6 +905,7 @@ class CScriptVarDouble : public CScriptVar { virtual double getDouble(); // {return data;} virtual std::string getString(); // {return float2string(data);} virtual std::string getVarType(); // { return "number"; } + virtual CScriptVarPtr getNumericVar(); ///< returns an Integer, a Double, an Infinity or a NaN private: double data; friend define_newScriptVar_Fnc(Double, CTinyJS* Context, double); diff --git a/TinyJS_MathFunctions.cpp b/TinyJS_MathFunctions.cpp index 12c1f96..8e5915c 100644 --- a/TinyJS_MathFunctions.cpp +++ b/TinyJS_MathFunctions.cpp @@ -68,17 +68,17 @@ namespace } #endif -#define GET_PARAMETER_AS_NUMERIC_VAR(v,n) CScriptVarSmartLink v = c->getParameter(n)->getNumericVar() -#define RETURN_NAN_IS_NAN(v) do{ if((*v)->isNaN()) { c->setReturnVar(v); return; } }while(0) -#define RETURN_NAN_IS_NAN_OR_INFINITY(v) do{ if((*v)->isNaN() || (*v)->isInfinity()) { c->setReturnVar(v); return; } }while(0) -#define RETURN_INFINITY_IS_INFINITY(v) do{ if((*v)->isInfinity()) { c->setReturnVar(v); return; } }while(0) +#define GET_PARAMETER_AS_NUMERIC_VAR(v,n) CScriptVarPtr v = c->getParameter(n)->getNumericVar() +#define RETURN_NAN_IS_NAN(v) do{ if(v->isNaN()) { c->setReturnVar(v); return; } }while(0) +#define RETURN_NAN_IS_NAN_OR_INFINITY(v) do{ if(v->isNaN() || v->isInfinity()) { c->setReturnVar(v); return; } }while(0) +#define RETURN_INFINITY_IS_INFINITY(v) do{ if(v->isInfinity()) { c->setReturnVar(v); return; } }while(0) -#define GET_DOUBLE(v) (*v)->getDouble() +#define GET_DOUBLE(v) v->getDouble() //Math.abs(x) - returns absolute of given value static void scMathAbs(const CFunctionsScopePtr &c, void *userdata) { GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN(a); - if((*a)->getInt() < 0) { + if(a->getInt() < 0) { CScriptVarPtr zero = c->newScriptVar(0); c->setReturnVar(zero->mathsOp(a, '-')); } else @@ -110,8 +110,8 @@ static void scMathMin(const CFunctionsScopePtr &c, void *userdata) { for(int i=0; iisInfinity() < 0) { c->setReturnVar(a); return; } - CScriptVarPtr result = (*a)->mathsOp(ret, '<'); + if(a->isInfinity() < 0) { c->setReturnVar(a); return; } + CScriptVarPtr result = a->mathsOp(ret, '<'); if(result->getBool()) ret = a; } @@ -125,8 +125,8 @@ static void scMathMax(const CFunctionsScopePtr &c, void *userdata) { for(int i=0; iisInfinity() > 0) { c->setReturnVar(a); return; } - CScriptVarPtr result = (*a)->mathsOp(ret, '>'); + if(a->isInfinity() > 0) { c->setReturnVar(a); return; } + CScriptVarPtr result = a->mathsOp(ret, '>'); if(result->getBool()) ret = a; } @@ -142,15 +142,15 @@ static void scMathRange(const CFunctionsScopePtr &c, void *userdata) { CScriptVarPtr check; bool check_bool; - check = (*a)->mathsOp(b, LEX_LEQUAL); + check = a->mathsOp(b, LEX_LEQUAL); check_bool = check->getBool(); if(!check_bool) { scReturnNaN(); return; } - check = (*x)->mathsOp(a, '<'); + check = x->mathsOp(a, '<'); check_bool = check->getBool(); if(check_bool) { c->setReturnVar(a); return; } - check = (*x)->mathsOp(b, '>'); + check = x->mathsOp(b, '>'); check_bool = check->getBool(); c->setReturnVar((check_bool ? b:x)); } @@ -260,8 +260,8 @@ static void scMathATanh(const CFunctionsScopePtr &c, void *userdata) { //Math.log(a) - returns natural logaritm (base E) of given value static void scMathLog(const CFunctionsScopePtr &c, void *userdata) { GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN(a); - int a_i = (*a)->isInfinity(); - double a_d = (*a)->getDouble(); + int a_i = a->isInfinity(); + double a_d = a->getDouble(); if(a_i>0) { c->setReturnVar(c->newScriptVar(InfinityPositive)); return; } else if(a_i<0 || a_d<0.0) { scReturnNaN(); return; } scReturnDouble( log( a_d ) ); @@ -270,8 +270,8 @@ static void scMathLog(const CFunctionsScopePtr &c, void *userdata) { //Math.log10(a) - returns logaritm(base 10) of given value static void scMathLog10(const CFunctionsScopePtr &c, void *userdata) { GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN(a); - int a_i = (*a)->isInfinity(); - double a_d = (*a)->getDouble(); + int a_i = a->isInfinity(); + double a_d = a->getDouble(); if(a_i>0) { c->setReturnVar(c->newScriptVar(InfinityPositive)); return; } else if(a_i<0 || a_d<0.0) { c->setReturnVar(c->newScriptVar(NaN)); return; } scReturnDouble( log10( a_d ) ); @@ -280,7 +280,7 @@ static void scMathLog10(const CFunctionsScopePtr &c, void *userdata) { //Math.exp(a) - returns e raised to the power of a given number static void scMathExp(const CFunctionsScopePtr &c, void *userdata) { GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN(a); - int a_i = (*a)->isInfinity(); + int a_i = a->isInfinity(); if(a_i>0) { c->setReturnVar(c->newScriptVar(InfinityPositive)); return; } else if(a_i<0) { c->setReturnVar(c->newScriptVar(0)); return; } scReturnDouble( exp( GET_DOUBLE(a) ) ); @@ -290,8 +290,8 @@ static void scMathExp(const CFunctionsScopePtr &c, void *userdata) { static void scMathPow(const CFunctionsScopePtr &c, void *userdata) { GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN(a); GET_PARAMETER_AS_NUMERIC_VAR(b,"b"); RETURN_NAN_IS_NAN(b); - int a_i = (*a)->isInfinity(), b_i = (*b)->isInfinity(); - double a_d = (*a)->getDouble(), b_d = (*b)->getDouble(); + int a_i = a->isInfinity(), b_i = b->isInfinity(); + double a_d = a->getDouble(), b_d = b->getDouble(); if(b_i>0) { if(a_i || a_d>1.0 || a_d<-1.0) { c->setReturnVar(c->newScriptVar(InfinityPositive)); return; } else if(a_i==0 && (a_d==1.0 || a_d==-1.0)) { c->setReturnVar(c->newScriptVar(1)); return; } From 9834afb9c201c4ab92cd205563da500e937d3bee Mon Sep 17 00:00:00 2001 From: armin Date: Sun, 1 Jan 2012 21:43:57 +0000 Subject: [PATCH 37/43] 42tiny-js: - 'NaN' is no more handled like a literal 'NaN' is now a property of the lobal gscope and the Number object (Number.NaN) - 'Infinity' is no more handled like a literal 'Infinity' is now a property of the global scope and the Number object (Number.POSITIVE_INFINITY) - added Number.NEGATIVE_INFINITY - 'undefined' is no more handled like a literal 'undefined' is now a property of the global scope and the Number object (Number.POSITIVE_INFINITY) - moved Integer.parseInt to the global scope as parseInt and added the radix argument (parseInt(string [, radix])) - added global functions parseFloat, isNaN and isFinite --- TinyJS.cpp | 193 +++++++++++++++++++++++++++---------------- TinyJS.h | 28 ++++--- TinyJS_Functions.cpp | 8 -- 3 files changed, 139 insertions(+), 90 deletions(-) diff --git a/TinyJS.cpp b/TinyJS.cpp index acf3146..36212e1 100755 --- a/TinyJS.cpp +++ b/TinyJS.cpp @@ -424,9 +424,6 @@ string CScriptLex::getTokenStr(int token) { case LEX_R_TRUE : return "true"; case LEX_R_FALSE : return "false"; case LEX_R_NULL : return "null"; - case LEX_R_UNDEFINED : return "undefined"; - case LEX_R_INFINITY : return "Infinity"; - case LEX_R_NAN : return "NaN"; case LEX_R_NEW : return "new"; case LEX_R_TRY : return "try"; case LEX_R_CATCH : return "catch"; @@ -463,7 +460,7 @@ void CScriptLex::getNextCh() { currCh = '\n'; // Mac (only '\r') --> convert '\r' to '\n' } } -static uint16_t not_allowed_tokens_befor_regexp[] = {LEX_ID, LEX_INT, LEX_FLOAT, LEX_STR, LEX_R_TRUE, LEX_R_FALSE, LEX_R_NULL, LEX_R_UNDEFINED, LEX_R_INFINITY, LEX_R_NAN, ']', ')', '.', LEX_EOF}; +static uint16_t not_allowed_tokens_befor_regexp[] = {LEX_ID, LEX_INT, LEX_FLOAT, LEX_STR, LEX_R_TRUE, LEX_R_FALSE, LEX_R_NULL, ']', ')', '.', LEX_EOF}; void CScriptLex::getNextToken() { while (currCh && isWhitespace(currCh)) getNextCh(); // newline comments @@ -509,9 +506,6 @@ void CScriptLex::getNextToken() { else if (tkStr=="true") tk = LEX_R_TRUE; else if (tkStr=="false") tk = LEX_R_FALSE; else if (tkStr=="null") tk = LEX_R_NULL; - else if (tkStr=="undefined") tk = LEX_R_UNDEFINED; - else if (tkStr=="Infinity") tk = LEX_R_INFINITY; - else if (tkStr=="NaN") tk = LEX_R_NAN; else if (tkStr=="new") tk = LEX_R_NEW; else if (tkStr=="try") tk = LEX_R_TRY; else if (tkStr=="catch") tk = LEX_R_CATCH; @@ -696,14 +690,11 @@ void CScriptLex::getNextToken() { if(tk == LEX_REGEXP) { tkStr = "/"; while (currCh && currCh!='/' && currCh!='\n') { - if (currCh == '\\') { - getNextCh(); - if(currCh != '\n') { // ignore newline after '\' - tkStr.append("\\"); - tkStr.append(1, currCh); - } - } else + if (currCh == '\\' && nextCh == '/') { tkStr.append(1, currCh); + getNextCh(); + } + tkStr.append(1, currCh); getNextCh(); } if(currCh == '/') { @@ -2511,13 +2502,20 @@ CTinyJS::CTinyJS() { ////////////////////////////////////////////////////////////////////////// // Object-Prototype - objectPrototype = newScriptVar(Object); + objectPrototype = newScriptVar(Object); + // must be created as first object because the first object becomes not a prototype (see constructor of CScriptVar) ////////////////////////////////////////////////////////////////////////// // Scopes root = ::newScriptVar(this, Scope); scopes.push_back(root); + ////////////////////////////////////////////////////////////////////////// + // global built-in vars + root->addChild("NaN", newScriptVar(NaN), SCRIPTVARLINK_ENUMERABLE); + root->addChild("Infinity", newScriptVar(InfinityPositive), SCRIPTVARLINK_ENUMERABLE); + root->addChild("undefined", newScriptVar(Undefined), SCRIPTVARLINK_ENUMERABLE); + ////////////////////////////////////////////////////////////////////////// // Add built-in classes @@ -2546,6 +2544,9 @@ CTinyJS::CTinyJS() { numberPrototype = newScriptVar(Object); var = addNative("function Number()", this, &CTinyJS::native_Number); var->addChild(TINYJS_PROTOTYPE_CLASS, numberPrototype); + var->addChild("NaN", newScriptVar(NaN), SCRIPTVARLINK_ENUMERABLE); + var->addChild("POSITIVE_INFINITY", newScriptVar(InfinityPositive), SCRIPTVARLINK_ENUMERABLE); + var->addChild("NEGATIVE_INFINITY", newScriptVar(InfinityNegative), SCRIPTVARLINK_ENUMERABLE); ////////////////////////////////////////////////////////////////////////// // Function @@ -2556,9 +2557,16 @@ CTinyJS::CTinyJS() { addNative("function Function.prototype.apply(objc, args)", this, &CTinyJS::native_Function_apply); // execute the given string and return the result ////////////////////////////////////////////////////////////////////////// - addNative("function eval(jsCode)", this, &CTinyJS::native_Eval); // execute the given string and return the result - addNative("function JSON.parse(text, reviver)", this, &CTinyJS::native_JSON_parse); // execute the given string and return the result - + // global functions + addNative("function eval(jsCode)", this, &CTinyJS::native_eval); // execute the given string and return the result + addNative("function isNaN(objc)", this, &CTinyJS::native_isNAN); + addNative("function isFinite(objc)", this, &CTinyJS::native_isFinite); + addNative("function parseInt(string, radix)", this, &CTinyJS::native_parseInt); + addNative("function parseFloat(string)", this, &CTinyJS::native_parseFloat); + + + addNative("function JSON.parse(text, reviver)", this, &CTinyJS::native_JSON_parse); + } CTinyJS::~CTinyJS() { @@ -3033,15 +3041,6 @@ CScriptVarSmartLink CTinyJS::execute_literals(bool &execute) { } else if (t->tk==LEX_R_NULL) { t->match(LEX_R_NULL); return new CScriptVarLink(newScriptVar(Null)); - } else if (t->tk==LEX_R_UNDEFINED) { - t->match(LEX_R_UNDEFINED); - return new CScriptVarLink(newScriptVar(Undefined)); - } else if (t->tk==LEX_R_INFINITY) { - t->match(LEX_R_INFINITY); - return new CScriptVarLink(newScriptVar(InfinityPositive)); - } else if (t->tk==LEX_R_NAN) { - t->match(LEX_R_NAN); - return new CScriptVarLink(newScriptVar(NaN)); } else if (t->tk=='(') { t->match('('); CScriptVarSmartLink a = execute_base(execute); @@ -4056,6 +4055,9 @@ CScriptVarLink *CTinyJS::findInPrototypeChain(CScriptVarPtr object, const string */ return 0; } + +////////////////////////////////////////////////////////////////////////// native Object + void CTinyJS::native_Object(const CFunctionsScopePtr &c, void *data) { CScriptVarPtr objc = c->getParameter(0); if(objc->isUndefined() || objc->isNull()) @@ -4063,6 +4065,12 @@ void CTinyJS::native_Object(const CFunctionsScopePtr &c, void *data) { else c->setReturnVar(objc); } +void CTinyJS::native_Object_hasOwnProperty(const CFunctionsScopePtr &c, void *data) { + c->setReturnVar(c->newScriptVar(c->getParameter("this")->findChild(c->getParameter("prop")->getString()) != 0)); +} + +////////////////////////////////////////////////////////////////////////// native Array + void CTinyJS::native_Array(const CFunctionsScopePtr &c, void *data) { // CScriptVar *returnVar = new CScriptVarArray(c->getContext()); CScriptVarPtr returnVar = c->newScriptVar(Array); @@ -4073,19 +4081,72 @@ void CTinyJS::native_Array(const CFunctionsScopePtr &c, void *data) { else for(int i=0; isetArrayIndex(i, c->getParameter(i)); } + +////////////////////////////////////////////////////////////////////////// native String + void CTinyJS::native_String(const CFunctionsScopePtr &c, void *data) { if(c->getParameterLength()==0) c->setReturnVar(c->newScriptVar("")); else c->setReturnVar(c->newScriptVar(c->getParameter(0)->getString())); } + +////////////////////////////////////////////////////////////////////////// native Number + void CTinyJS::native_Number(const CFunctionsScopePtr &c, void *data) { if(c->getParameterLength()==0) c->setReturnVar(c->newScriptVar(0)); else c->setReturnVar(c->getParameter(0)->getNumericVar()); } -void CTinyJS::native_Eval(const CFunctionsScopePtr &c, void *data) { + +////////////////////////////////////////////////////////////////////////// native Function + +void CTinyJS::native_Function(const CFunctionsScopePtr &c, void *data) { + int length = c->getParameterLength(); + string params, body; + if(length>=1) + body = c->getParameter(length-1)->getString(); + if(length>=2) { + params = c->getParameter(0)->getString(); + for(int i=1; igetParameter(i)->getString()); + } + } + c->setReturnVar(parseFunctionsBodyFromString(params,body)); +} + +void CTinyJS::native_Function_call(const CFunctionsScopePtr &c, void *data) { + int length = c->getParameterLength(); + CScriptVarPtr Fnc = c->getParameter("this"); + CScriptVarPtr This = c->getParameter(0); + vector Params; + for(int i=1; igetParameter(i)); + bool execute = true; + callFunction(Fnc, Params, This, execute); +} +void CTinyJS::native_Function_apply(const CFunctionsScopePtr &c, void *data) { + CScriptVarPtr Fnc = c->getParameter("this"); + CScriptVarPtr This = c->getParameter(0); + CScriptVarPtr Array = c->getParameter(1); + CScriptVarLink *Length = Array->findChild("length"); + int length = Length ? Length->getValue()->getInt() : 0; + vector Params; + for(int i=0; ifindChild(int2string(i)); + if(value) Params.push_back(value->getValue()); + else Params.push_back(newScriptVar(Undefined)); + } + bool execute = true; + callFunction(Fnc, Params, This, execute); +} + +////////////////////////////////////////////////////////////////////////// global functions + +void CTinyJS::native_eval(const CFunctionsScopePtr &c, void *data) { string Code = c->getParameter("jsCode")->getString(); CScriptVarScopePtr scEvalScope = scopes.back(); // save scope scopes.pop_back(); // go back to the callers scope @@ -4115,6 +4176,38 @@ void CTinyJS::native_Eval(const CFunctionsScopePtr &c, void *data) { if(exceptionState) throw new CScriptEvalException(exceptionState); } +void CTinyJS::native_isNAN(const CFunctionsScopePtr &c, void *data) { + c->setReturnVar(newScriptVar(c->getParameter("objc")->getNumericVar()->isNaN())); +} + +void CTinyJS::native_isFinite(const CFunctionsScopePtr &c, void *data) { + CScriptVarPtr objc = c->getParameter("objc")->getNumericVar(); + c->setReturnVar(newScriptVar(!(objc->isInfinity() || objc->isNaN()))); +} + +void CTinyJS::native_parseInt(const CFunctionsScopePtr &c, void *) { + string str = c->getParameter("string")->getString(); + int radix = c->getParameter("radix")->getInt(); + char *endp = 0; + int val = strtol(str.c_str(),&endp,radix!=0 ? radix : 0); + if(endp == str.c_str()) + c->setReturnVar(c->newScriptVar(NaN)); + else + c->setReturnVar(c->newScriptVar(val)); +} + +void CTinyJS::native_parseFloat(const CFunctionsScopePtr &c, void *) { + string str = c->getParameter("string")->getString(); + char *endp = 0; + double val = strtod(str.c_str(),&endp); + if(endp == str.c_str()) + c->setReturnVar(c->newScriptVar(NaN)); + else + c->setReturnVar(c->newScriptVar(val)); +} + + + void CTinyJS::native_JSON_parse(const CFunctionsScopePtr &c, void *data) { string Code = "�" + c->getParameter("text")->getString(); // "�" is a spezal-token - it's for the tokenizer and means the code begins not in Statement-level @@ -4140,51 +4233,7 @@ void CTinyJS::native_JSON_parse(const CFunctionsScopePtr &c, void *data) { // runtimeFlags &= ~RUNTIME_LOOP_MASK; // if(exceptionState) throw new CScriptEvalException(exceptionState); } -void CTinyJS::native_Object_hasOwnProperty(const CFunctionsScopePtr &c, void *data) { - c->setReturnVar(c->newScriptVar(c->getParameter("this")->findChild(c->getParameter("prop")->getString()) != 0)); -} -void CTinyJS::native_Function(const CFunctionsScopePtr &c, void *data) { - int length = c->getParameterLength(); - string params, body; - if(length>=1) - body = c->getParameter(length-1)->getString(); - if(length>=2) { - params = c->getParameter(0)->getString(); - for(int i=1; igetParameter(i)->getString()); - } - } - c->setReturnVar(parseFunctionsBodyFromString(params,body)); -} - -void CTinyJS::native_Function_call(const CFunctionsScopePtr &c, void *data) { - int length = c->getParameterLength(); - CScriptVarPtr Fnc = c->getParameter("this"); - CScriptVarPtr This = c->getParameter(0); - vector Params; - for(int i=1; igetParameter(i)); - bool execute = true; - callFunction(Fnc, Params, This, execute); -} -void CTinyJS::native_Function_apply(const CFunctionsScopePtr &c, void *data) { - CScriptVarPtr Fnc = c->getParameter("this"); - CScriptVarPtr This = c->getParameter(0); - CScriptVarPtr Array = c->getParameter(1); - CScriptVarLink *Length = Array->findChild("length"); - int length = Length ? Length->getValue()->getInt() : 0; - vector Params; - for(int i=0; ifindChild(int2string(i)); - if(value) Params.push_back(value->getValue()); - else Params.push_back(newScriptVar(Undefined)); - } - bool execute = true; - callFunction(Fnc, Params, This, execute); -} void CTinyJS::ClearLostVars(const CScriptVarPtr &extra/*=CScriptVarPtr()*/) { int UniqueID = getUniqueID(); diff --git a/TinyJS.h b/TinyJS.h index dfd64ba..e754d5a 100755 --- a/TinyJS.h +++ b/TinyJS.h @@ -114,9 +114,6 @@ enum LEX_TYPES { LEX_R_TRUE, LEX_R_FALSE, LEX_R_NULL, - LEX_R_UNDEFINED, - LEX_R_INFINITY, - LEX_R_NAN, LEX_R_NEW, LEX_R_TRY, LEX_R_CATCH, @@ -1273,22 +1270,33 @@ class CTinyJS { void throwError(bool &execute, const std::string &message); void throwError(bool &execute, const std::string &message, CScriptTokenizer::ScriptTokenPosition &Pos); - /// native Functions + /// native Object-Constructors & prototype-functions void native_Object(const CFunctionsScopePtr &c, void *data); - void native_String(const CFunctionsScopePtr &c, void *data); - void native_Number(const CFunctionsScopePtr &c, void *data); + void native_Object_hasOwnProperty(const CFunctionsScopePtr &c, void *data); + void native_Array(const CFunctionsScopePtr &c, void *data); + void native_String(const CFunctionsScopePtr &c, void *data); - void native_Eval(const CFunctionsScopePtr &c, void *data); - void native_JSON_parse(const CFunctionsScopePtr &c, void *data); - void native_Object_hasOwnProperty(const CFunctionsScopePtr &c, void *data); - void native_Function(const CFunctionsScopePtr &c, void *data); + void native_Number(const CFunctionsScopePtr &c, void *data); + void native_Function(const CFunctionsScopePtr &c, void *data); void native_Function_call(const CFunctionsScopePtr &c, void *data); void native_Function_apply(const CFunctionsScopePtr &c, void *data); + /// globale function + + void native_eval(const CFunctionsScopePtr &c, void *data); + void native_isNAN(const CFunctionsScopePtr &c, void *data); + void native_isFinite(const CFunctionsScopePtr &c, void *data); + void native_parseInt(const CFunctionsScopePtr &c, void *data); + void native_parseFloat(const CFunctionsScopePtr &c, void *data); + + + + void native_JSON_parse(const CFunctionsScopePtr &c, void *data); + int uniqueID; public: diff --git a/TinyJS_Functions.cpp b/TinyJS_Functions.cpp index 5fd80d5..0e03298 100755 --- a/TinyJS_Functions.cpp +++ b/TinyJS_Functions.cpp @@ -65,12 +65,6 @@ static void scStringFromCharCode(const CFunctionsScopePtr &c, void *) { c->setReturnVar(c->newScriptVar(str)); } -static void scIntegerParseInt(const CFunctionsScopePtr &c, void *) { - string str = c->getParameter("str")->getString(); - int val = strtol(str.c_str(),0,0); - c->setReturnVar(c->newScriptVar(val)); -} - static void scIntegerValueOf(const CFunctionsScopePtr &c, void *) { string str = c->getParameter("str")->getString(); @@ -156,10 +150,8 @@ void registerFunctions(CTinyJS *tinyJS) { tinyJS->addNative("function Object.prototype.dump()", scObjectDump, 0); tinyJS->addNative("function Object.prototype.clone()", scObjectClone, 0); - tinyJS->addNative("function Integer.parseInt(str)", scIntegerParseInt, 0); // string to int tinyJS->addNative("function Integer.valueOf(str)", scIntegerValueOf, 0); // value of a single character tinyJS->addNative("function JSON.stringify(obj, replacer)", scJSONStringify, 0); // convert to JSON. replacer is ignored at the moment - // JSON.parse is left out as you can (unsafely!) use eval instead tinyJS->addNative("function Array.prototype.contains(obj)", scArrayContains, 0); tinyJS->addNative("function Array.prototype.remove(obj)", scArrayRemove, 0); tinyJS->addNative("function Array.prototype.join(separator)", scArrayJoin, 0); From e0cfe62161e2e02de57a1ca4f307e7f4bd53a8d8 Mon Sep 17 00:00:00 2001 From: armin Date: Tue, 3 Jan 2012 18:51:28 +0000 Subject: [PATCH 38/43] 42tiny-js: removed pool-allocators debug-output for release-builds now only gets debug-output by memory leaks unkomment //#define LOG_POOL_ALLOCATOR_MEMORY_USAGE in in pool_allocator.h for full debug-output --- pool_allocator.cpp | 53 +++++++++++++++++++++++++++++----------------- pool_allocator.h | 12 +++++++++++ 2 files changed, 46 insertions(+), 19 deletions(-) diff --git a/pool_allocator.cpp b/pool_allocator.cpp index 5d2bc56..fe3d567 100644 --- a/pool_allocator.cpp +++ b/pool_allocator.cpp @@ -17,19 +17,19 @@ static void* get_next(void* p) { fixed_size_allocator::fixed_size_allocator( size_t numObjects, size_t objectSize, const char *for_class ) { -/// fprintf(stderr, "allocator(%d) created\n", object_size); num_objects = numObjects; object_size = objectSize >= sizeof(block) ? objectSize : sizeof(block); head_of_free_list = head = 0; - //debug +#ifdef DEBUG_POOL_ALLOCATOR if(for_class) name = for_class; allocs= frees= max = current= blocks = 0; +#endif } fixed_size_allocator::~fixed_size_allocator() @@ -39,16 +39,26 @@ fixed_size_allocator::~fixed_size_allocator() head = head->next; delete [] p; } +#ifdef DEBUG_POOL_ALLOCATOR +# ifndef LOG_POOL_ALLOCATOR_MEMORY_USAGE + if(allocs != frees) { +# endif fprintf(stderr, "allocator [%s](%d) destroyed\n", name.c_str(), object_size); fprintf(stderr, " allocs:%i, ", allocs); fprintf(stderr, "frees:%i, ", frees); fprintf(stderr, "max:%i, ", max); fprintf(stderr, "blocks:%i\n\n", blocks); +# ifndef LOG_POOL_ALLOCATOR_MEMORY_USAGE + } +# endif +#endif } void* fixed_size_allocator::alloc( size_t ) { +#ifdef DEBUG_POOL_ALLOCATOR allocs++;current++; if(current>max)max=current; +#endif void* p = head_of_free_list; if(p) { head_of_free_list = get_next(p); @@ -64,13 +74,11 @@ void* fixed_size_allocator::alloc( size_t ) { set_next(&new_block[(num_objects - 1) * object_size], 0); p = new_block; head_of_free_list = &new_block[object_size]; -/// fprintf(stderr, "pool(%i) new block created\n", object_size); + +#ifdef DEBUG_POOL_ALLOCATOR blocks++; - } -#ifdef _DEBUG - *((int *)p) = 0x31415695; - p=((char*)p)+sizeof(int); #endif + } return p; } #include @@ -80,10 +88,8 @@ void* fixed_size_allocator::alloc( size_t ) { void fixed_size_allocator::free( void* p, size_t ) { if(p == 0) return; +#ifdef DEBUG_POOL_ALLOCATOR frees++;current--; -#ifdef _DEBUG - p=((char*)p)-sizeof(int); - ASSERT(*((int *)p) == 0x31415695); #endif block* dead_object = static_cast(p); @@ -97,38 +103,47 @@ static class _allocator_pool { public: _allocator_pool() : last_allocate_allocator(0), last_free_allocator(0) { - last_ok = last_access = 0; +#ifdef LOG_POOL_ALLOCATOR_MEMORY_USAGE + last_ok = last_access = 0; +#endif } ~_allocator_pool() { for(allocator_pool_it it = allocator_pool.begin(); it!=allocator_pool.end(); it++) delete it->second; +#ifdef LOG_POOL_ALLOCATOR_MEMORY_USAGE if(last_access) fprintf(stderr, "last_ok:%i(%i)=%i%%\n", last_ok, last_access, (last_ok*100)/last_access); -// getc(stdin); +#endif } allocator_pool_t allocator_pool; fixed_size_allocator *last_allocate_allocator; fixed_size_allocator *last_free_allocator; +#ifdef LOG_POOL_ALLOCATOR_MEMORY_USAGE int last_ok; int last_access; +#endif }allocator_pool; fixed_size_allocator *fixed_size_allocator::get(size_t size, bool for_alloc, const char *for_class) { -#ifdef _DEBUG - size += sizeof(int); -#endif +#ifdef LOG_POOL_ALLOCATOR_MEMORY_USAGE allocator_pool.last_access++; +#endif if(allocator_pool.last_allocate_allocator && allocator_pool.last_allocate_allocator->object_size==size) { +#ifdef LOG_POOL_ALLOCATOR_MEMORY_USAGE allocator_pool.last_ok++; +#endif return allocator_pool.last_allocate_allocator; } else if(allocator_pool.last_free_allocator && allocator_pool.last_free_allocator->object_size==size) { +#ifdef LOG_POOL_ALLOCATOR_MEMORY_USAGE allocator_pool.last_ok++; +#endif return allocator_pool.last_free_allocator; } allocator_pool_it it = allocator_pool.allocator_pool.find(size); if(it != allocator_pool.allocator_pool.end()) return (for_alloc ? allocator_pool.last_allocate_allocator : allocator_pool.last_free_allocator) = it->second; - else { -/// fprintf(stderr, "new allocator(%i) for %s\n", size, for_class); - return (for_alloc ? allocator_pool.last_allocate_allocator : allocator_pool.last_free_allocator) = allocator_pool.allocator_pool[size] = new fixed_size_allocator(64, size, for_class); - } + else if(for_alloc) { + return allocator_pool.last_allocate_allocator = allocator_pool.allocator_pool[size] = new fixed_size_allocator(64, size, for_class); + } else + ASSERT(0/* free called but not allocator defined*/); + return 0; } diff --git a/pool_allocator.h b/pool_allocator.h index fb71ee6..42b198b 100644 --- a/pool_allocator.h +++ b/pool_allocator.h @@ -6,6 +6,12 @@ #include #include +//#define LOG_POOL_ALLOCATOR_MEMORY_USAGE + +#if defined(_DEBUG) || defined(LOG_POOL_ALLOCATOR_MEMORY_USAGE) +# define DEBUG_POOL_ALLOCATOR +#endif + struct block_head; class fixed_size_allocator { public: @@ -21,6 +27,7 @@ class fixed_size_allocator { size_t object_size; void *head_of_free_list; block_head *head; +#ifdef DEBUG_POOL_ALLOCATOR // Debug std::string name; int allocs; @@ -28,13 +35,18 @@ class fixed_size_allocator { int current; int max; int blocks; +#endif }; //************************************************************************************** template class fixed_size_object { public: static void* operator new(size_t size) { +#ifdef DEBUG_POOL_ALLOCATOR return fixed_size_allocator::get(size, true, typeid(T).name())->alloc(size); +#else + return fixed_size_allocator::get(size, true)->alloc(size); +#endif } static void* operator new(size_t size, void* p) { return p; From 33f832f3f4a79c0ac61d581a08b1413869c6ff27 Mon Sep 17 00:00:00 2001 From: armin Date: Mon, 9 Jan 2012 15:02:53 +0000 Subject: [PATCH 39/43] 42tiny-js: hotfix --- TinyJS.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TinyJS.cpp b/TinyJS.cpp index 36212e1..9cc2207 100755 --- a/TinyJS.cpp +++ b/TinyJS.cpp @@ -1237,7 +1237,8 @@ void CScriptTokenizer::tokenizeFunction(TOKEN_VECT &Tokens, bool &Statement, vec vector functionBlockStart, marks; functionBlockStart.push_back(FncData.body.size()+1); l->check('{'); - tokenizeBlock(FncData.body, Statement, functionBlockStart, marks); + bool FncStatement = true; // functions-block starts always in Statement-Level + tokenizeBlock(FncData.body, FncStatement, functionBlockStart, marks); if(forward) { int tokenInsertIdx = BlockStart.back(); @@ -2799,7 +2800,6 @@ CScriptVarPtr CTinyJS::callFunction(const CScriptVarPtr &Function, std::vector Date: Tue, 10 Jan 2012 21:17:19 +0000 Subject: [PATCH 40/43] 42tiny-js: hotfix2 remove unneeded include --- TinyJS.h | 2615 +++++++++++++++++++++++++++--------------------------- 1 file changed, 1307 insertions(+), 1308 deletions(-) diff --git a/TinyJS.h b/TinyJS.h index e754d5a..6d83aa4 100755 --- a/TinyJS.h +++ b/TinyJS.h @@ -1,1308 +1,1307 @@ -/* - * TinyJS - * - * A single-file Javascript-alike engine - * - * Authored By Gordon Williams - * - * Copyright (C) 2009 Pur3 Ltd - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef TINYJS_H -#define TINYJS_H - -#include -#include -#include -#include -#include "smart_pointer.h" -#include "pool_allocator.h" -#include -#include - -#ifndef ASSERT -# define ASSERT(X) assert(X) -#endif - -#ifndef __GNUC__ -# define __attribute__(x) -#endif - - -#undef TRACE -#ifndef TRACE -#define TRACE printf -#endif // TRACE - -const int TINYJS_LOOP_MAX_ITERATIONS = 8192; - -enum LEX_TYPES { - LEX_EOF = 0, -#define LEX_RELATIONS_1_BEGIN LEX_EQUAL - LEX_EQUAL = 256, - LEX_TYPEEQUAL, - LEX_NEQUAL, - LEX_NTYPEEQUAL, -#define LEX_RELATIONS_1_END LEX_NTYPEEQUAL - LEX_LEQUAL, - LEX_GEQUAL, -#define LEX_SHIFTS_BEGIN LEX_LSHIFT - LEX_LSHIFT, - LEX_RSHIFT, - LEX_RSHIFTU, // unsigned -#define LEX_SHIFTS_END LEX_RSHIFTU - LEX_PLUSPLUS, - LEX_MINUSMINUS, - LEX_ANDAND, - LEX_OROR, - LEX_INT, - -#define LEX_ASSIGNMENTS_BEGIN LEX_PLUSEQUAL - LEX_PLUSEQUAL, - LEX_MINUSEQUAL, - LEX_ASTERISKEQUAL, - LEX_SLASHEQUAL, - LEX_PERCENTEQUAL, - LEX_LSHIFTEQUAL, - LEX_RSHIFTEQUAL, - LEX_RSHIFTUEQUAL, // unsigned - LEX_ANDEQUAL, - LEX_OREQUAL, - LEX_XOREQUAL, -#define LEX_ASSIGNMENTS_END LEX_XOREQUAL - -#define LEX_TOKEN_STRING_BEGIN LEX_ID - LEX_ID, - LEX_STR, - LEX_REGEXP, - LEX_T_LABEL, -#define LEX_TOKEN_STRING_END LEX_T_LABEL - - LEX_FLOAT, - - // reserved words - LEX_R_IF, - LEX_R_ELSE, -#define LEX_TOKEN_LOOP_BEGIN LEX_R_DO - LEX_R_DO, - LEX_R_WHILE, - LEX_R_FOR, -#define LEX_TOKEN_LOOP_END LEX_R_FOR - LEX_R_IN, - LEX_R_BREAK, - LEX_R_CONTINUE, - LEX_R_FUNCTION, - LEX_R_RETURN, - LEX_R_VAR, - LEX_R_LET, - LEX_R_WITH, - LEX_R_TRUE, - LEX_R_FALSE, - LEX_R_NULL, - LEX_R_NEW, - LEX_R_TRY, - LEX_R_CATCH, - LEX_R_FINALLY, - LEX_R_THROW, - LEX_R_TYPEOF, - LEX_R_VOID, - LEX_R_DELETE, - LEX_R_INSTANCEOF, - LEX_R_SWITCH, - LEX_R_CASE, - LEX_R_DEFAULT, - - // special token -// LEX_T_FILE, - LEX_T_FUNCTION_OPERATOR, - LEX_T_GET, - LEX_T_SET, - - LEX_T_FOR_IN, - LEX_T_FOR_EACH_IN, - - LEX_T_SKIP, - -}; -#define LEX_TOKEN_DATA_STRING(tk) ((LEX_TOKEN_STRING_BEGIN<= tk && tk <= LEX_TOKEN_STRING_END)) -#define LEX_TOKEN_DATA_FLOAT(tk) (tk==LEX_FLOAT) -#define LEX_TOKEN_DATA_FUNCTION(tk) (tk==LEX_R_FUNCTION || tk==LEX_T_FUNCTION_OPERATOR || tk==LEX_T_SET || tk==LEX_T_GET) -#define LEX_TOKEN_DATA_SIMPLE(tk) (!LEX_TOKEN_DATA_STRING(tk) && !LEX_TOKEN_DATA_FLOAT(tk) && !LEX_TOKEN_DATA_FUNCTION(tk)) -/* -enum SCRIPTVAR_FLAGS { - SCRIPTVAR_UNDEFINED = 0, - SCRIPTVAR_FUNCTION = 1<<0, - SCRIPTVAR_OBJECT = 1<<1, - SCRIPTVAR_ARRAY = 1<<2, - SCRIPTVAR_DOUBLE = 1<<3, // floating point double - SCRIPTVAR_INTEGER = 1<<4, // integer number - SCRIPTVAR_BOOLEAN = 1<<5, // boolean - SCRIPTVAR_STRING = 1<<6, // string - SCRIPTVAR_NULL = 1<<7, // it seems null is its own data type - SCRIPTVAR_INFINITY = 1<<8, // it seems infinity is its own data type - SCRIPTVAR_NAN = 1<<9, // it seems NaN is its own data type - SCRIPTVAR_ACCESSOR = 1<<10, // it seems an Object with get() and set() - - SCRIPTVAR_END_OF_TYPES = 1<<15, // it seems NaN is its own data type - - SCRIPTVAR_NATIVE_FNC = 1<<16, // to specify this is a native function - SCRIPTVAR_NATIVE_MFNC = 1<<17, // to specify this is a native function from class->memberFunc - - SCRIPTVAR_NUMBERMASK = SCRIPTVAR_DOUBLE | - SCRIPTVAR_INTEGER, - SCRIPTVAR_NUMERICMASK = SCRIPTVAR_NUMBERMASK | - SCRIPTVAR_NULL | - SCRIPTVAR_BOOLEAN, - - SCRIPTVAR_VARTYPEMASK = SCRIPTVAR_END_OF_TYPES - 1, - - SCRIPTVAR_NATIVE = SCRIPTVAR_NATIVE_FNC | - SCRIPTVAR_NATIVE_MFNC, -}; -*/ -enum SCRIPTVARLINK_FLAGS { - SCRIPTVARLINK_OWNED = 1<<0, - SCRIPTVARLINK_WRITABLE = 1<<1, - SCRIPTVARLINK_DELETABLE = 1<<2, - SCRIPTVARLINK_ENUMERABLE = 1<<3, - SCRIPTVARLINK_HIDDEN = 1<<4, - SCRIPTVARLINK_DEFAULT = SCRIPTVARLINK_WRITABLE | SCRIPTVARLINK_DELETABLE | SCRIPTVARLINK_ENUMERABLE, -}; -enum RUNTIME_FLAGS { - RUNTIME_CANRETURN = 1<<0, - - RUNTIME_CANBREAK = 1<<1, - RUNTIME_BREAK = 1<<2, - RUNTIME_BREAK_MASK = RUNTIME_CANBREAK | RUNTIME_BREAK, - - RUNTIME_CANCONTINUE = 1<<3, - RUNTIME_CONTINUE = 1<<4, - RUNTIME_LOOP_MASK = RUNTIME_BREAK_MASK | RUNTIME_CANCONTINUE | RUNTIME_CONTINUE, - - RUNTIME_NEW = 1<<5, - - RUNTIME_CANTHROW = 1<<6, - RUNTIME_THROW = 1<<7, - RUNTIME_THROW_MASK = RUNTIME_CANTHROW | RUNTIME_THROW, - -}; - -#define SAVE_RUNTIME_RETURN int old_return_runtimeFlags = runtimeFlags & RUNTIME_CANRETURN -#define RESTORE_RUNTIME_RETURN runtimeFlags = (runtimeFlags & ~RUNTIME_CANRETURN) | old_pass_runtimeFlags -#define SET_RUNTIME_CANRETURN runtimeFlags |= RUNTIME_CANRETURN -#define IS_RUNTIME_CANRETURN ((runtimeFlags & RUNTIME_CANRETURN) == RUNTIME_CANRETURN) - - -#define TINYJS_RETURN_VAR "return" -#define TINYJS_LOKALE_VAR "__locale__" -#define TINYJS_ANONYMOUS_VAR "__anonymous__" -#define TINYJS_ARGUMENTS_VAR "arguments" -#define TINYJS___PROTO___VAR "__proto__" -#define TINYJS_PROTOTYPE_CLASS "prototype" -#define TINYJS_FUNCTION_CLOSURE_VAR "__function_closure__" -#define TINYJS_SCOPE_PARENT_VAR "__scope_parent__" -#define TINYJS_SCOPE_WITH_VAR "__scope_with__" -#define TINYJS_ACCESSOR_GET_VAR "__accessor_get__" -#define TINYJS_ACCESSOR_SET_VAR "__accessor_set__" -#define TINYJS_TEMP_NAME "" -#define TINYJS_BLANK_DATA "" -#define TINYJS_NEGATIVE_INFINITY_DATA "-1" -#define TINYJS_POSITIVE_INFINITY_DATA "+1" - -typedef std::vector STRING_VECTOR_t; -typedef STRING_VECTOR_t::iterator STRING_VECTOR_it; - -/// convert the given string into a quoted string suitable for javascript -std::string getJSString(const std::string &str); -/// convert the given int into a string -std::string int2string(int intData); -/// convert the given double into a string -std::string float2string(const double &floatData); - -// ----------------------------------------------------------------------------------- CScriptSmartPointer -template -class CScriptSmartPointer -{ -public: - CScriptSmartPointer() : pointer(0) {} - explicit CScriptSmartPointer(T *Pointer) : pointer(Pointer ? new p(Pointer) : 0) {} - CScriptSmartPointer(const CScriptSmartPointer &Copy) : pointer(0) {*this = Copy;} - CScriptSmartPointer &operator=(const CScriptSmartPointer &Copy) - { - if(this == &Copy) return *this; - if(pointer) pointer->unref(); - if(Copy.pointer) - pointer = Copy.pointer->ref(); - else - pointer = 0; - return *this; - } - ~CScriptSmartPointer() { release(); } - void release() { if(pointer) pointer->unref(); pointer = 0;} - T *operator->() { return operator T*(); } - operator T*() { return pointer ? pointer->pointer : 0; } -private: - class p - { - public: - p(T *Pointer) : pointer(Pointer), refs(1) {} - p *ref() { refs++; return this; } - void unref() { if(--refs == 0) { delete pointer; delete this; } } - T *pointer; - int refs; - } *pointer; -}; - -class CScriptException { -public: - std::string text; -// int pos; - std::string file; - int line; - int column; -// CScriptException(const std::string &exceptionText, int Pos=-1); - CScriptException(const std::string &Text, const std::string &File, int Line=-1, int Column=-1) : - text(Text), file(File), line(Line), column(Column){} - CScriptException(const std::string &Text, const char *File="", int Line=-1, int Column=-1) : - text(Text), file(File), line(Line), column(Column){} -}; - -// ----------------------------------------------------------------------------------- CSCRIPTLEX -class CScriptLex -{ -public: - CScriptLex(const char *Code, const std::string &File="", int Line=0, int Column=0); - - int tk; ///< The type of the token that we have - int last_tk; ///< The type of the last token that we have - const char *tokenStart; - std::string tkStr; ///< Data contained in the token we have here - - void check(int expected_tk); ///< Lexical check wotsit - void match(int expected_tk); ///< Lexical match wotsit - static std::string getTokenStr(int token); ///< Get the string representation of the given token - void reset(const char *toPos, int line, const char *LineStart); ///< Reset this lex so we can start again - - std::string currentFile; - int currentLine; - const char *currentLineStart; - int currentColumn() { return tokenStart-currentLineStart; } - -private: - const char *data; - const char *dataPos; - char currCh, nextCh; - - void getNextCh(); - void getNextToken(); ///< Get the text token from our text string -}; - -// ----------------------------------------------------------------------------------- CSCRIPTTOKEN -class CScriptToken; -typedef std::vector TOKEN_VECT; -typedef std::vector::iterator TOKEN_VECT_it; -class CScriptTokenData -{ -public: - virtual ~CScriptTokenData() {} - void ref() { refs++; } - void unref() { if(--refs == 0) delete this; } -protected: - CScriptTokenData() : refs(0){} -private: - int refs; -}; -class CScriptTokenDataString : public fixed_size_object, public CScriptTokenData { -public: - CScriptTokenDataString(const std::string &String) : tokenStr(String) {} - std::string tokenStr; -private: -}; -class CScriptTokenDataFnc : public fixed_size_object, public CScriptTokenData { -public: - std::string file; - int line; - std::string name; - std::vector parameter; - TOKEN_VECT body; -private: -}; - -class CScriptTokenizer; -/* - a Token needs 8 Byte - 2 Bytes for the Row-Position of the Token - 2 Bytes for the Token self - and - 4 Bytes for special Datas in an union - e.g. an int for interger-literals - or pointer for double-literals, - for string-literals or for functions -*/ -class CScriptToken : public fixed_size_object -{ -public: - CScriptToken() : line(0), column(0), token(0) {} - CScriptToken(CScriptLex *l, int Match=-1); - CScriptToken(uint16_t Tk, int IntData=0); - CScriptToken(uint16_t Tk, const std::string &TkStr); - CScriptToken(const CScriptToken &Copy) : token(0) { *this = Copy; } - CScriptToken &operator =(const CScriptToken &Copy); - ~CScriptToken() { clear(); } - - int &Int() { ASSERT(LEX_TOKEN_DATA_SIMPLE(token)); return intData; } - std::string &String() { ASSERT(LEX_TOKEN_DATA_STRING(token)); return stringData->tokenStr; } - double &Float() { ASSERT(LEX_TOKEN_DATA_FLOAT(token)); return *floatData; } - CScriptTokenDataFnc &Fnc() { ASSERT(LEX_TOKEN_DATA_FUNCTION(token)); return *fncData; } - uint16_t line; - uint16_t column; - uint16_t token; - void print(std::string &indent); - std::string getParsableString(const std::string Indent=""); - std::string getParsableString(std::string &indentString, int &newln, const std::string &indent); ///< newln ==> 1=inject indentString, 2=inject \n, 3=last='{' -private: - - void clear(); - union { - int intData; - double *floatData; - CScriptTokenDataString *stringData; - CScriptTokenDataFnc *fncData; - }; -}; - -// ----------------------------------------------------------------------------------- CSCRIPTTOKENIZER -/* - the tokenizer converts the code in an Vector with Tokens -*/ -class CScriptTokenizer -{ -public: - struct ScriptTokenPosition { - ScriptTokenPosition(TOKEN_VECT *Tokens) : tokens(Tokens), pos(tokens->begin())/*, currentLine(0)*//*, currentColumn(0)*/ {} - bool operator ==(const ScriptTokenPosition &eq) { return pos == eq.pos; } - ScriptTokenPosition &operator =(const ScriptTokenPosition ©) { - tokens=copy.tokens; pos=copy.pos; -// currentLine=copy.currentLine; - return *this; - } - TOKEN_VECT *tokens; - TOKEN_VECT::iterator pos; - int currentLine() { return pos->line; } - int currentColumn() { return pos->column; } - }; - CScriptTokenizer(); - CScriptTokenizer(CScriptLex &Lexer); - CScriptTokenizer(const char *Code, const std::string &File="", int Line=0, int Column=0); - void tokenizeCode(CScriptLex &Lexer); - - CScriptToken &getToken() { return *(tokenScopeStack.back().pos); } - void getNextToken(); - bool check(int ExpectedToken); - void match(int ExpectedToken); - void pushTokenScope(TOKEN_VECT &Tokens); - ScriptTokenPosition &getPos() { return tokenScopeStack.back(); } - void setPos(ScriptTokenPosition &TokenPos); - ScriptTokenPosition &getPrevPos() { return prevPos; } - void skip(int Tokens); - int tk; // current Token - std::string currentFile; - int currentLine() { return getPos().currentLine();} - int currentColumn() { return getPos().currentColumn();} - std::string &tkStr() { return getToken().String(); } -private: - void tokenizeTry(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); - void tokenizeSwitch(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); - void tokenizeWith(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); - void tokenizeWhile(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); - void tokenizeDo(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); - void tokenizeIf(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); - void tokenizeForIn(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); - void tokenizeFor(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); - void tokenizeFunction(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); - void tokenizeBlock(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); - void tokenizeLet(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); - void tokenizeVar(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); - void tokenizeStatement(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); - void tokenizeToken(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); - int pushToken(TOKEN_VECT &Tokens, int Match=-1); - void throwTokenNotExpected(); - CScriptLex *l; - TOKEN_VECT tokens; - ScriptTokenPosition prevPos; - std::vector tokenScopeStack; -}; -////////////////////////////////////////////////////////////////////////// CScriptVarPointer - -class CScriptVar; -class CScriptVarLink; -class CScriptVarSmartLink; -template -class CScriptVarPointer { -public: - CScriptVarPointer() : var(0) {} ///< explizit 0-Pointer - CScriptVarPointer(CScriptVar *Var) : var(dynamic_cast(Var)) { if(Var) ASSERT(var); if(var) var->ref(); } - CScriptVarPointer(const CScriptVarLink *Link); - CScriptVarPointer(const CScriptVarSmartLink &Link); - //: var(dynamic_cast(Var)) { if(Var) ASSERT(var); if(var) var->ref(); } - ~CScriptVarPointer() { if(var) var->unref(); } - CScriptVarPointer(const CScriptVarPointer &VarPtr) : var(0) { *this = VarPtr; } - CScriptVarPointer& operator=(const CScriptVarPointer &VarPtr) { - if(var != VarPtr.var) { - if(var) var->unref(); - var = VarPtr.var; if(var) var->ref(); - } - return *this; - } - template - operator CScriptVarPointer() const { return CScriptVarPointer(var); } - operator bool() const { return var!=0; } - - template - bool operator ==(const CScriptVarPointer &Other) const { return (CScriptVar*)var == (CScriptVar*)(Other.getVar()); } - template - bool operator !=(const CScriptVarPointer &Other) const { return (CScriptVar*)var != (CScriptVar*)(Other.getVar()); } - - C * operator ->() const { ASSERT(var); return var; } - CScriptVar *getVar() const { ASSERT(var); return var; } -private: - C *var; -}; - -typedef CScriptVarPointer CScriptVarPtr; - -// ----------------------------------------------------------------------------------- CSCRIPTVARLINK -class CScriptVar; -class CScriptVarLink : public fixed_size_object -{ -public: - std::string name; -// const std::string &Name() { return name; } -// void SetName(const std::string &Name) { name = Name; } -private: - CScriptVar *var; -public: - CScriptVarPtr getValue(); -private: -// CScriptVar *get() { return var; } -public: - CScriptVarPtr getVarPtr() { return var; } - CScriptVar *owner; // pointer to the owner CScriptVar - uint32_t flags; - CScriptVarLink(const CScriptVarPtr &var, const std::string &name = TINYJS_TEMP_NAME, int flags = SCRIPTVARLINK_DEFAULT); - CScriptVarLink(const CScriptVarLink &link); ///< Copy constructor - ~CScriptVarLink(); - void replaceWith(const CScriptVarPtr &newVar); ///< Replace the Variable pointed to - void replaceWith(CScriptVarLink *newVar); ///< Replace the Variable pointed to (just dereferences) -// void SetOwner(CScriptVar *Owner) { owner = Owner; } -// void SetOwner(CScriptVar *Owner, bool Owned) { owner = Owner; SetOwned(Owned); } -// bool IsOwned() { return owner != 0; } - bool IsOwned() { return (flags & SCRIPTVARLINK_OWNED) != 0; } - void SetOwned(bool On) { On ? (flags |= SCRIPTVARLINK_OWNED) : (flags &= ~SCRIPTVARLINK_OWNED); } - - bool IsWritable() { return (flags & SCRIPTVARLINK_WRITABLE) != 0; } - void SetWritable(bool On) { On ? (flags |= SCRIPTVARLINK_WRITABLE) : (flags &= ~SCRIPTVARLINK_DELETABLE); } - bool IsDeletable() { return (flags & SCRIPTVARLINK_DELETABLE) != 0; } - void SetDeletable(bool On) { On ? (flags |= SCRIPTVARLINK_DELETABLE) : (flags &= ~SCRIPTVARLINK_DELETABLE); } - bool IsEnumerable() { return (flags & SCRIPTVARLINK_ENUMERABLE) != 0; } - void SetEnumerable(bool On) { On ? (flags |= SCRIPTVARLINK_ENUMERABLE) : (flags &= ~SCRIPTVARLINK_ENUMERABLE); } - bool IsHidden() { return (flags & SCRIPTVARLINK_HIDDEN) != 0; } - void SetHidden(bool On) { On ? (flags |= SCRIPTVARLINK_HIDDEN) : (flags &= ~SCRIPTVARLINK_HIDDEN); } - CScriptVar * operator ->() const { return var; } -// friend class CScriptVar; -// friend class CScriptVarSmartLink; -// friend class CScriptVarObject; -}; -template -CScriptVarPointer::CScriptVarPointer(const CScriptVarLink *Link) : var(0) { if(Link) { var = dynamic_cast(Link->operator->()); ASSERT(var); } if(var) var->ref(); } - -// ----------------------------------------------------------------------------------- CSCRIPTVAR -typedef std::vector SCRIPTVAR_CHILDS_t; -typedef SCRIPTVAR_CHILDS_t::iterator SCRIPTVAR_CHILDS_it; -typedef SCRIPTVAR_CHILDS_t::const_iterator SCRIPTVAR_CHILDS_cit; -class CTinyJS; -class CScriptVarScopeFnc; -typedef CScriptVarPointer CFunctionsScopePtr; -typedef void (*JSCallback)(const CFunctionsScopePtr &var, void *userdata); - - -class CScriptVar : public fixed_size_object { -protected: - CScriptVar(CTinyJS *Context, const CScriptVarPtr &Prototype=CScriptVarPtr()); ///< Create - CScriptVar(const CScriptVar &Copy); ///< Copy protected -> use clone for public -private: - CScriptVar & operator=(const CScriptVar &Copy); ///< private -> no assignment-Copy -public: - virtual ~CScriptVar(void); - virtual CScriptVarPtr clone()=0; - - /// Type - virtual bool isInt(); // {return false;} - virtual bool isBool(); // {return false;} - virtual bool isDouble(); // {return false;} - virtual bool isString(); // {return false;} - virtual bool isNumber(); // {return false;} - virtual bool isNumeric(); // {return false;} - virtual bool isFunction(); // {return false;} - virtual bool isObject(); // {return false;} - virtual bool isArray(); // {return false;} - virtual bool isNative(); // {return false;} - virtual bool isUndefined();// {return false;} - virtual bool isNull(); // {return false;} - virtual bool isNaN(); // {return false;} - virtual int isInfinity(); // { return 0; } ///< +1==POSITIVE_INFINITY, -1==NEGATIVE_INFINITY, 0==is not an InfinityVar - virtual bool isAccessor(); // {return false;} - - bool isBasic() { return Childs.empty(); } ///< Is this *not* an array/object/etc - - - /// Value - virtual int getInt(); ///< return 0 - virtual bool getBool(); ///< return false - virtual double getDouble(); ///< return 0.0 - virtual CScriptTokenDataFnc *getFunctionData(); ///< { return 0; } - virtual std::string getString(); ///< return "" - - virtual std::string getParsableString(const std::string &indentString, const std::string &indent); ///< get Data as a parsable javascript string - virtual std::string getVarType()=0; - virtual CScriptVarPtr getNumericVar(); ///< returns an Integer, a Double, an Infinity or a NaN - - /// find - CScriptVarLink *findChild(const std::string &childName); ///< Tries to find a child with the given name, may return 0 - CScriptVarLink *findChildByPath(const std::string &path); ///< Tries to find a child with the given path (separated by dots) - CScriptVarLink *findChildOrCreate(const std::string &childName/*, int varFlags=SCRIPTVAR_UNDEFINED*/); ///< Tries to find a child with the given name, or will create it with the given flags - CScriptVarLink *findChildOrCreateByPath(const std::string &path); ///< Tries to find a child with the given path (separated by dots) - - /// add & remove - CScriptVarLink *addChild(const std::string &childName, const CScriptVarPtr &child, int linkFlags = SCRIPTVARLINK_DEFAULT); - CScriptVarLink *addChildNoDup(const std::string &childName, const CScriptVarPtr &child, int linkFlags = SCRIPTVARLINK_DEFAULT); ///< add a child overwriting any with the same name - bool removeLink(CScriptVarLink *&link); ///< Remove a specific link (this is faster than finding via a child) - void removeAllChildren(); - - - /// funcions for FUNCTION - void setReturnVar(const CScriptVarPtr &var); ///< Set the result value. Use this when setting complex return data as it avoids a deepCopy() - CScriptVarPtr getParameter(const std::string &name); ///< If this is a function, get the parameter with the given name (for use by native functions) - CScriptVarPtr getParameter(int Idx); ///< If this is a function, get the parameter with the given index (for use by native functions) - int getParameterLength(); ///< If this is a function, get the count of parameters - - - /// ARRAY - CScriptVarPtr getArrayIndex(int idx); ///< The the value at an array index - void setArrayIndex(int idx, const CScriptVarPtr &value); ///< Set the value at an array index - int getArrayLength(); ///< If this is an array, return the number of items in it (else 0) - - ////////////////////////////////////////////////////////////////////////// - int getChildren() { return Childs.size(); } ///< Get the number of children - CTinyJS *getContext() { return context; } - CScriptVarPtr mathsOp(const CScriptVarPtr &b, int op); ///< do a maths op with another script variable - - void trace(const std::string &name = ""); ///< Dump out the contents of this using trace - void trace(std::string &indentStr, int uniqueID, const std::string &name = ""); ///< Dump out the contents of this using trace - std::string getFlagsAsString(); ///< For debugging - just dump a string version of the flags -// void getJSON(std::ostringstream &destination, const std::string linePrefix=""); ///< Write out all the JS code needed to recreate this script variable to the stream (as JSON) - - SCRIPTVAR_CHILDS_t Childs; - - /// For memory management/garbage collection - CScriptVar *ref(); ///< Add reference to this variable - void unref(); ///< Remove a reference, and delete this variable if required - int getRefs(); ///< Get the number of references to this script variable - template - operator T *(){ T* ret = dynamic_cast(this); ASSERT(ret!=0); return ret; } - template - T *get(){ T* ret = dynamic_cast(this); ASSERT(ret!=0); return ret; } - - template - CScriptVarPtr newScriptVar(T t); // { return ::newScriptVar(context, t); } - void setTempraryID(int ID) { temporaryID = ID; } - void setTempraryIDrecursive(int ID); - int getTempraryID() { return temporaryID; } -protected: - CTinyJS *context; - int refs; ///< The number of references held to this - used for garbage collection - CScriptVar *prev; -public: - CScriptVar *next; - int temporaryID; -}; -////////////////////////////////////////////////////////////////////////// -#define define_dummy_t(t1) struct t1##_t{}; extern t1##_t t1 -#define declare_dummy_t(t1) t1##_t t1 -#define define_newScriptVar_Fnc(t1, ...) CScriptVar##t1##Ptr newScriptVar(__VA_ARGS__) -#define define_ScriptVarPtr_Type(t1) class CScriptVar##t1; typedef CScriptVarPointer CScriptVar##t1##Ptr - - -////////////////////////////////////////////////////////////////////////// CScriptVarObject - -define_dummy_t(Object); -define_ScriptVarPtr_Type(Object); - -class CScriptVarObject : public CScriptVar { -protected: - CScriptVarObject(CTinyJS *Context, const CScriptVarPtr &Prototype=CScriptVarPtr()) : CScriptVar(Context, Prototype) {} - CScriptVarObject(const CScriptVarObject &Copy) : CScriptVar(Copy) {} ///< Copy protected -> use clone for public -public: - virtual ~CScriptVarObject(); - virtual CScriptVarPtr clone(); - virtual bool isObject(); // { return true; } - - virtual std::string getString(); // { return "[ Object ]"; }; - virtual std::string getParsableString(const std::string &indentString, const std::string &indent); - virtual std::string getVarType(); // { return "object"; } -private: - - friend define_newScriptVar_Fnc(Object, CTinyJS* Context, Object_t); - friend define_newScriptVar_Fnc(Object, CTinyJS* Context, Object_t, const CScriptVarPtr &); -}; -inline define_newScriptVar_Fnc(Object, CTinyJS* Context, Object_t) { return new CScriptVarObject(Context); } -inline define_newScriptVar_Fnc(Object, CTinyJS* Context, Object_t, const CScriptVarPtr &Prototype) { return new CScriptVarObject(Context, Prototype); } - - -////////////////////////////////////////////////////////////////////////// CScriptVarAccessor - -define_dummy_t(Accessor); -define_ScriptVarPtr_Type(Accessor); - -class CScriptVarAccessor : public CScriptVar { -protected: - CScriptVarAccessor(CTinyJS *Context) : CScriptVar(Context) {} - CScriptVarAccessor(const CScriptVarAccessor &Copy) : CScriptVar(Copy) {} ///< Copy protected -> use clone for public -public: - virtual ~CScriptVarAccessor(); - virtual CScriptVarPtr clone(); - virtual bool isAccessor(); // { return true; } - - virtual std::string getString(); // { return "[ Object ]"; }; - virtual std::string getParsableString(const std::string &indentString, const std::string &indent); - virtual std::string getVarType(); // { return "object"; } - - CScriptVarPtr getValue(); - - friend define_newScriptVar_Fnc(Accessor, CTinyJS* Context, Accessor_t); - -}; -inline define_newScriptVar_Fnc(Accessor, CTinyJS* Context, Accessor_t) { return new CScriptVarAccessor(Context); } - - -////////////////////////////////////////////////////////////////////////// CScriptVarArray - -define_dummy_t(Array); -define_ScriptVarPtr_Type(Array); -class CScriptVarArray : public CScriptVar { -protected: - CScriptVarArray(CTinyJS *Context); - CScriptVarArray(const CScriptVarArray &Copy) : CScriptVar(Copy) {} ///< Copy protected -> use clone for public -public: - virtual ~CScriptVarArray(); - virtual CScriptVarPtr clone(); - virtual bool isArray(); // { return true; } - virtual std::string getString(); - virtual std::string getVarType(); // { return "object"; } - virtual std::string getParsableString(const std::string &indentString, const std::string &indent); - friend define_newScriptVar_Fnc(Array, CTinyJS* Context, Array_t); -private: - void native_Length(const CFunctionsScopePtr &c, void *data); -}; -inline define_newScriptVar_Fnc(Array, CTinyJS* Context, Array_t) { return new CScriptVarArray(Context); } - - -////////////////////////////////////////////////////////////////////////// CScriptVarNull - -define_dummy_t(Null); -define_ScriptVarPtr_Type(Null); -class CScriptVarNull : public CScriptVar { -protected: - CScriptVarNull(CTinyJS *Context) : CScriptVar(Context) {} - CScriptVarNull(const CScriptVarNull &Copy) : CScriptVar(Copy) {} ///< Copy protected -> use clone for public -public: - virtual ~CScriptVarNull(); - virtual CScriptVarPtr clone(); - virtual bool isNull(); // { return true; } - virtual std::string getString(); // { return "null"; }; - virtual std::string getVarType(); // { return "null"; } - virtual CScriptVarPtr getNumericVar(); ///< returns an Integer, a Double, an Infinity or a NaN - friend define_newScriptVar_Fnc(Null, CTinyJS* Context, Null_t); -}; -inline define_newScriptVar_Fnc(Null, CTinyJS* Context, Null_t) { return new CScriptVarNull(Context); } - - -////////////////////////////////////////////////////////////////////////// CScriptVarUndefined - -define_dummy_t(Undefined); -define_ScriptVarPtr_Type(Undefined); -class CScriptVarUndefined : public CScriptVar { -protected: - CScriptVarUndefined(CTinyJS *Context) : CScriptVar(Context) {} - CScriptVarUndefined(const CScriptVarUndefined &Copy) : CScriptVar(Copy) {} ///< Copy protected -> use clone for public -public: - virtual ~CScriptVarUndefined(); - virtual CScriptVarPtr clone(); - virtual bool isUndefined(); // { return true; } - virtual std::string getString(); // { return "undefined"; }; - virtual std::string getVarType(); // { return "undefined"; } - friend define_newScriptVar_Fnc(Undefined, CTinyJS* Context, Undefined_t); -}; -inline define_newScriptVar_Fnc(Undefined, CTinyJS* Context, Undefined_t) { return new CScriptVarUndefined(Context); } - - -////////////////////////////////////////////////////////////////////////// CScriptVarNaN - -define_dummy_t(NaN); -define_ScriptVarPtr_Type(NaN); -class CScriptVarNaN : public CScriptVar { -protected: - CScriptVarNaN(CTinyJS *Context) : CScriptVar(Context) {} - CScriptVarNaN(const CScriptVarNaN &Copy) : CScriptVar(Copy) {} ///< Copy protected -> use clone for public -public: - virtual ~CScriptVarNaN(); - virtual CScriptVarPtr clone(); - virtual bool isNaN();// { return true; } - virtual std::string getString(); // { return "NaN"; }; - virtual std::string getVarType(); // { return "number"; } - friend define_newScriptVar_Fnc(NaN, CTinyJS* Context, NaN_t); -}; -inline define_newScriptVar_Fnc(NaN, CTinyJS* Context, NaN_t) { return new CScriptVarNaN(Context); } - - -////////////////////////////////////////////////////////////////////////// CScriptVarString - -define_ScriptVarPtr_Type(String); -class CScriptVarString : public CScriptVar { -protected: - CScriptVarString(CTinyJS *Context, const std::string &Data); - CScriptVarString(const CScriptVarString &Copy) : CScriptVar(Copy), data(Copy.data) {} ///< Copy protected -> use clone for public -public: - virtual ~CScriptVarString(); - virtual CScriptVarPtr clone(); - virtual bool isString(); // { return true; } - virtual int getInt(); // {return strtol(data.c_str(),0,0); } - virtual bool getBool(); // {return data.length()!=0;} - virtual double getDouble(); // {return strtod(data.c_str(),0);} - virtual std::string getString(); // { return data; } - virtual std::string getParsableString(); // { return getJSString(data); } - virtual std::string getVarType(); // { return "string"; } - virtual CScriptVarPtr getNumericVar(); ///< returns an Integer, a Double, an Infinity or a NaN -protected: - std::string data; -private: - void native_Length(const CFunctionsScopePtr &c, void *data); - - friend define_newScriptVar_Fnc(String, CTinyJS* Context, const std::string &); - friend define_newScriptVar_Fnc(String, CTinyJS* Context, const char *); -}; -inline define_newScriptVar_Fnc(String, CTinyJS* Context, const std::string &Obj) { return new CScriptVarString(Context, Obj); } -inline define_newScriptVar_Fnc(String, CTinyJS* Context, const char *Obj) { return new CScriptVarString(Context, Obj); } - - -////////////////////////////////////////////////////////////////////////// CScriptVarIntegerBase - -class CScriptVarIntegerBase : public CScriptVar { -protected: - CScriptVarIntegerBase(CTinyJS *Context, int Data); - CScriptVarIntegerBase(const CScriptVarIntegerBase &Copy) : CScriptVar(Copy), data(Copy.data) {} ///< Copy protected -> use clone for public -public: - virtual ~CScriptVarIntegerBase(); - virtual int getInt(); // {return data; } - virtual bool getBool(); // {return data!=0;} - virtual double getDouble(); // {return data;} - virtual std::string getString(); // {return int2string(data);} - virtual std::string getVarType(); // { return "number"; } - virtual CScriptVarPtr getNumericVar(); ///< returns an Integer, a Double, an Infinity or a NaN -protected: - int data; -}; - - -////////////////////////////////////////////////////////////////////////// CScriptVarInteger - -define_ScriptVarPtr_Type(Integer); -class CScriptVarInteger : public CScriptVarIntegerBase { -protected: - CScriptVarInteger(CTinyJS *Context, int Data) : CScriptVarIntegerBase(Context, Data) {} - CScriptVarInteger(const CScriptVarInteger &Copy) : CScriptVarIntegerBase(Copy) {} ///< Copy protected -> use clone for public -public: - virtual ~CScriptVarInteger(); - virtual CScriptVarPtr clone(); - virtual bool isNumber(); // { return true; } - virtual bool isInt(); // { return true; } - friend define_newScriptVar_Fnc(Integer, CTinyJS* Context, int); -}; -inline define_newScriptVar_Fnc(Integer, CTinyJS* Context, int Obj) { return new CScriptVarInteger(Context, Obj); } - - -////////////////////////////////////////////////////////////////////////// CScriptVarBool - -define_ScriptVarPtr_Type(Bool); -class CScriptVarBool : public CScriptVarIntegerBase { -protected: - CScriptVarBool(CTinyJS *Context, bool Data) : CScriptVarIntegerBase(Context, Data?1:0) {} - CScriptVarBool(const CScriptVarBool &Copy) : CScriptVarIntegerBase(Copy) {} ///< Copy protected -> use clone for public -public: - virtual ~CScriptVarBool(); - virtual CScriptVarPtr clone(); - virtual bool isBool(); // { return true; } - virtual std::string getString(); // {return data!=0?"true":"false";} - virtual std::string getVarType(); // { return "boolean"; } - virtual CScriptVarPtr getNumericVar(); ///< returns an Integer, a Double, an Infinity or a NaN - friend define_newScriptVar_Fnc(Bool, CTinyJS* Context, bool); -}; -inline define_newScriptVar_Fnc(Bool, CTinyJS* Context, bool Obj) { return new CScriptVarBool(Context, Obj); } - - -////////////////////////////////////////////////////////////////////////// CScriptVarInfinity - -struct Infinity{Infinity(int Sig=1):sig(Sig){} int sig; }; -extern Infinity InfinityPositive; -extern Infinity InfinityNegative; -define_ScriptVarPtr_Type(Infinity); -class CScriptVarInfinity : public CScriptVarIntegerBase { -protected: - CScriptVarInfinity(CTinyJS *Context, int Data) : CScriptVarIntegerBase(Context, Data<0?-1:1) {} - CScriptVarInfinity(const CScriptVarInfinity &Copy) : CScriptVarIntegerBase(Copy) {} ///< Copy protected -> use clone for public -public: - virtual ~CScriptVarInfinity(); - virtual CScriptVarPtr clone(); - virtual int isInfinity(); // { return data; } - virtual std::string getString(); // {return data<0?"-Infinity":"Infinity";} - friend define_newScriptVar_Fnc(Infinity, CTinyJS* Context, Infinity); -}; -inline define_newScriptVar_Fnc(Infinity, CTinyJS* Context, Infinity Obj) { return new CScriptVarInfinity(Context, Obj.sig); } - - -////////////////////////////////////////////////////////////////////////// CScriptVarDouble - -define_ScriptVarPtr_Type(Double); -class CScriptVarDouble : public CScriptVar { -protected: - CScriptVarDouble(CTinyJS *Context, double Data); - CScriptVarDouble(const CScriptVarDouble &Copy) : CScriptVar(Copy), data(Copy.data) {} ///< Copy protected -> use clone for public -public: - virtual ~CScriptVarDouble(); - virtual CScriptVarPtr clone(); - virtual bool isNumber(); // { return true; } - virtual bool isDouble(); // { return true; } - virtual int getInt(); // {return (int)data; } - virtual bool getBool(); // {return data!=0.0;} - virtual double getDouble(); // {return data;} - virtual std::string getString(); // {return float2string(data);} - virtual std::string getVarType(); // { return "number"; } - virtual CScriptVarPtr getNumericVar(); ///< returns an Integer, a Double, an Infinity or a NaN -private: - double data; - friend define_newScriptVar_Fnc(Double, CTinyJS* Context, double); -}; -inline define_newScriptVar_Fnc(Double, CTinyJS* Context, double Obj) { return new CScriptVarDouble(Context, Obj); } - - -////////////////////////////////////////////////////////////////////////// CScriptVarFunction - -define_ScriptVarPtr_Type(Function); -class CScriptVarFunction : public CScriptVar { -protected: - CScriptVarFunction(CTinyJS *Context, CScriptTokenDataFnc *Data); - CScriptVarFunction(const CScriptVarFunction &Copy) : CScriptVar(Copy), data(Copy.data) { data->ref(); } ///< Copy protected -> use clone for public -public: - virtual ~CScriptVarFunction(); - virtual CScriptVarPtr clone(); - virtual bool isFunction(); // { return true; } - - virtual std::string getString(); // {return "[ Function ]";} - virtual std::string getVarType(); // { return "function"; } - virtual std::string getParsableString(const std::string &indentString, const std::string &indent); - virtual CScriptTokenDataFnc *getFunctionData(); - void setFunctionData(CScriptTokenDataFnc *Data); -private: - CScriptTokenDataFnc *data; - std::string getParsableBlockString(TOKEN_VECT::iterator &it, TOKEN_VECT::iterator end, const std::string indentString, const std::string indent); - - - friend define_newScriptVar_Fnc(Function, CTinyJS* Context, CScriptTokenDataFnc *); -}; -inline define_newScriptVar_Fnc(Function, CTinyJS* Context, CScriptTokenDataFnc *Obj) { return new CScriptVarFunction(Context, Obj); } - - -////////////////////////////////////////////////////////////////////////// CScriptVarFunctionNative - -define_ScriptVarPtr_Type(FunctionNative); -class CScriptVarFunctionNative : public CScriptVarFunction { -protected: - CScriptVarFunctionNative(CTinyJS *Context, void *Userdata) : CScriptVarFunction(Context, 0), jsUserData(Userdata) { } - CScriptVarFunctionNative(const CScriptVarFunctionNative &Copy) : CScriptVarFunction(Copy), jsUserData(Copy.jsUserData) { } ///< Copy protected -> use clone for public -public: - virtual ~CScriptVarFunctionNative(); - virtual CScriptVarPtr clone()=0; - virtual bool isNative(); // { return true; } - - virtual std::string getString(); // {return "[ Function Native ]";} - - virtual void callFunction(const CFunctionsScopePtr &c)=0;// { jsCallback(c, jsCallbackUserData); } -protected: - void *jsUserData; ///< user data passed as second argument to native functions -}; - - -////////////////////////////////////////////////////////////////////////// CScriptVarFunctionNativeCallback - - -define_ScriptVarPtr_Type(FunctionNativeCallback); -class CScriptVarFunctionNativeCallback : public CScriptVarFunctionNative { -protected: - CScriptVarFunctionNativeCallback(CTinyJS *Context, JSCallback Callback, void *Userdata) : CScriptVarFunctionNative(Context, Userdata), jsCallback(Callback) { } - CScriptVarFunctionNativeCallback(const CScriptVarFunctionNativeCallback &Copy) : CScriptVarFunctionNative(Copy), jsCallback(Copy.jsCallback) { } ///< Copy protected -> use clone for public -public: - virtual ~CScriptVarFunctionNativeCallback(); - virtual CScriptVarPtr clone(); - virtual void callFunction(const CFunctionsScopePtr &c); -private: - JSCallback jsCallback; ///< Callback for native functions - friend define_newScriptVar_Fnc(FunctionNativeCallback, CTinyJS* Context, JSCallback Callback, void*); -}; -inline define_newScriptVar_Fnc(FunctionNativeCallback, CTinyJS* Context, JSCallback Callback, void* Userdata) { return new CScriptVarFunctionNativeCallback(Context, Callback, Userdata); } - - -////////////////////////////////////////////////////////////////////////// CScriptVarFunctionNativeClass - -template -class CScriptVarFunctionNativeClass : public CScriptVarFunctionNative { -protected: - CScriptVarFunctionNativeClass(CTinyJS *Context, native *ClassPtr, void (native::*ClassFnc)(const CFunctionsScopePtr &, void *), void *Userdata) : CScriptVarFunctionNative(Context, Userdata), classPtr(ClassPtr), classFnc(ClassFnc) { } - CScriptVarFunctionNativeClass(const CScriptVarFunctionNativeClass &Copy) : CScriptVarFunctionNative(Copy), classPtr(Copy.classPtr), classFnc(Copy.classFnc) { } ///< Copy protected -> use clone for public -public: - virtual CScriptVarPtr clone() { return new CScriptVarFunctionNativeClass(*this); } - - virtual void callFunction(const CFunctionsScopePtr &c) { (classPtr->*classFnc)(c, jsUserData); } -private: - native *classPtr; - void (native::*classFnc)(const CFunctionsScopePtr &c, void *userdata); - template - friend CScriptVarFunctionNativePtr newScriptVar(CTinyJS* Context, native2 *ClassPtr, void (native2::*ClassFnc)(const CFunctionsScopePtr &, void *), void *Userdata); -}; -template -inline CScriptVarFunctionNativePtr newScriptVar(CTinyJS* Context, native *ClassPtr, void (native::*ClassFnc)(const CFunctionsScopePtr &, void *), void *Userdata) { return new CScriptVarFunctionNativeClass(Context, ClassPtr, ClassFnc, Userdata); } - - -////////////////////////////////////////////////////////////////////////// CScriptVarScope - -define_dummy_t(Scope); -define_ScriptVarPtr_Type(Scope); -class CScriptVarScope : public CScriptVarObject { -protected: // only derived classes or friends can be created - CScriptVarScope(CTinyJS *Context) // constructor for rootScope - : CScriptVarObject(Context) {} - virtual CScriptVarPtr clone(); - virtual bool isObject(); // { return false; } -public: - virtual ~CScriptVarScope(); - virtual CScriptVarPtr scopeVar(); ///< to create var like: var a = ... - virtual CScriptVarPtr scopeLet(); ///< to create var like: let a = ... - virtual CScriptVarLink *findInScopes(const std::string &childName); - virtual CScriptVarScopePtr getParent(); - friend define_newScriptVar_Fnc(Scope, CTinyJS* Context, Scope_t); -}; -inline define_newScriptVar_Fnc(Scope, CTinyJS* Context, Scope_t) { return new CScriptVarScope(Context); } - - -////////////////////////////////////////////////////////////////////////// CScriptVarScopeFnc - -define_dummy_t(ScopeFnc); -define_ScriptVarPtr_Type(ScopeFnc); -class CScriptVarScopeFnc : public CScriptVarScope { -protected: // only derived classes or friends can be created - CScriptVarScopeFnc(CTinyJS *Context, const CScriptVarScopePtr &Closure) // constructor for FncScope - : CScriptVarScope(Context), closure(Closure ? addChild(TINYJS_FUNCTION_CLOSURE_VAR, Closure, 0) : 0) {} -public: - virtual ~CScriptVarScopeFnc(); - virtual CScriptVarLink *findInScopes(const std::string &childName); -protected: - CScriptVarLink *closure; - friend define_newScriptVar_Fnc(ScopeFnc, CTinyJS* Context, ScopeFnc_t, const CScriptVarScopePtr &Closure); -}; -inline define_newScriptVar_Fnc(ScopeFnc, CTinyJS* Context, ScopeFnc_t, const CScriptVarScopePtr &Closure) { return new CScriptVarScopeFnc(Context, Closure); } - - -////////////////////////////////////////////////////////////////////////// CScriptVarScopeLet - -define_dummy_t(ScopeLet); -define_ScriptVarPtr_Type(ScopeLet); -class CScriptVarScopeLet : public CScriptVarScope { -protected: // only derived classes or friends can be created - CScriptVarScopeLet(const CScriptVarScopePtr &Parent); // constructor for LetScope -// : CScriptVarScope(Parent->getContext()), parent( context->getRoot() != Parent ? addChild(TINYJS_SCOPE_PARENT_VAR, Parent, 0) : 0) {} -public: - virtual ~CScriptVarScopeLet(); - virtual CScriptVarLink *findInScopes(const std::string &childName); - virtual CScriptVarPtr scopeVar(); ///< to create var like: var a = ... - virtual CScriptVarScopePtr getParent(); -protected: - CScriptVarLink *parent; - friend define_newScriptVar_Fnc(ScopeLet, CTinyJS* Context, ScopeLet_t, const CScriptVarScopePtr &Parent); -}; -inline define_newScriptVar_Fnc(ScopeLet, CTinyJS* , ScopeLet_t, const CScriptVarScopePtr &Parent) { return new CScriptVarScopeLet(Parent); } - - -////////////////////////////////////////////////////////////////////////// CScriptVarScopeWith - -define_dummy_t(ScopeWith); -define_ScriptVarPtr_Type(ScopeWith); -class CScriptVarScopeWith : public CScriptVarScopeLet { -protected: - CScriptVarScopeWith(const CScriptVarScopePtr &Parent, const CScriptVarPtr &With) - : CScriptVarScopeLet(Parent), with(addChild(TINYJS_SCOPE_WITH_VAR, With, 0)) {} - -public: - virtual ~CScriptVarScopeWith(); - virtual CScriptVarPtr scopeLet(); ///< to create var like: let a = ... - virtual CScriptVarLink *findInScopes(const std::string &childName); -private: - CScriptVarLink *with; - friend define_newScriptVar_Fnc(ScopeWith, CTinyJS* Context, ScopeWith_t, const CScriptVarScopePtr &Parent, const CScriptVarPtr &With); -}; -inline define_newScriptVar_Fnc(ScopeWith, CTinyJS* , ScopeWith_t, const CScriptVarScopePtr &Parent, const CScriptVarPtr &With) { return new CScriptVarScopeWith(Parent, With); } - -////////////////////////////////////////////////////////////////////////// -template -inline CScriptVarPtr CScriptVar::newScriptVar(T t) { return ::newScriptVar(context, t); } -////////////////////////////////////////////////////////////////////////// -// ----------------------------------------------------------------------------------- CSCRIPTVARSMARTLINK -class CScriptVarSmartLink : public fixed_size_object -{ -public: - CScriptVarSmartLink() : link(0){} - CScriptVarSmartLink(CScriptVarLink *Link) : link(Link) {} - explicit CScriptVarSmartLink (CScriptVarPtr Var) : link(0) { *this = Var; } - ~ CScriptVarSmartLink() { if(link && !link->IsOwned()) delete link; } - /// Copy - when copying a SmartLink to an other then the right hand side will lost your link - CScriptVarSmartLink (const CScriptVarSmartLink &Link) : link(0) { *this = Link; } - CScriptVarSmartLink &operator = (const CScriptVarSmartLink &Link); - ///- - CScriptVarSmartLink &operator = (CScriptVarLink *Link); - CScriptVarSmartLink &operator = (CScriptVarPtr Var); - CScriptVarLink *operator ->() const { return link; } - CScriptVarLink &operator *() { return *link; } - CScriptVarSmartLink &operator << (CScriptVarSmartLink &Link); - operator bool() const { return link != 0; } -// operator CScriptVarLink *() { return link; } - CScriptVarLink *&getLink() { return link; }; -private: - CScriptVarLink *link; -}; -template -CScriptVarPointer::CScriptVarPointer(const CScriptVarSmartLink &Link) : var(0) { if(Link) { var = dynamic_cast(Link->operator->()); ASSERT(var); } if(var) var->ref(); } - -// ----------------------------------------------------------------------------------- CTINYJS -class CTinyJS { -public: - CTinyJS(); - ~CTinyJS(); - - void execute(CScriptTokenizer &Tokenizer); - void execute(const char *Code, const std::string &File="", int Line=0, int Column=0); - void execute(const std::string &Code, const std::string &File="", int Line=0, int Column=0); - /** Evaluate the given code and return a link to a javascript object, - * useful for (dangerous) JSON parsing. If nothing to return, will return - * 'undefined' variable type. CScriptVarLink is returned as this will - * automatically unref the result as it goes out of scope. If you want to - * keep it, you must use ref() and unref() */ - CScriptVarLink evaluateComplex(CScriptTokenizer &Tokenizer); - /** Evaluate the given code and return a link to a javascript object, - * useful for (dangerous) JSON parsing. If nothing to return, will return - * 'undefined' variable type. CScriptVarLink is returned as this will - * automatically unref the result as it goes out of scope. If you want to - * keep it, you must use ref() and unref() */ - CScriptVarLink evaluateComplex(const char *code, const std::string &File="", int Line=0, int Column=0); - /** Evaluate the given code and return a link to a javascript object, - * useful for (dangerous) JSON parsing. If nothing to return, will return - * 'undefined' variable type. CScriptVarLink is returned as this will - * automatically unref the result as it goes out of scope. If you want to - * keep it, you must use ref() and unref() */ - CScriptVarLink evaluateComplex(const std::string &code, const std::string &File="", int Line=0, int Column=0); - /** Evaluate the given code and return a string. If nothing to return, will return - * 'undefined' */ - std::string evaluate(CScriptTokenizer &Tokenizer); - /** Evaluate the given code and return a string. If nothing to return, will return - * 'undefined' */ - std::string evaluate(const char *code, const std::string &File="", int Line=0, int Column=0); - /** Evaluate the given code and return a string. If nothing to return, will return - * 'undefined' */ - std::string evaluate(const std::string &code, const std::string &File="", int Line=0, int Column=0); - - /// add a native function to be called from TinyJS - /** example: - \code - void scRandInt(const CFunctionsScopePtr &c, void *userdata) { ... } - tinyJS->addNative("function randInt(min, max)", scRandInt, 0); - \endcode - - or - - \code - void scSubstring(const CFunctionsScopePtr &c, void *userdata) { ... } - tinyJS->addNative("function String.substring(lo, hi)", scSubstring, 0); - \endcode - or - - \code - class Class - { - public: - void scSubstring(const CFunctionsScopePtr &c, void *userdata) { ... } - }; - Class Instanz; - tinyJS->addNative("function String.substring(lo, hi)", &Instanz, &Class::*scSubstring, 0); - \endcode - */ - - CScriptVarFunctionNativePtr addNative(const std::string &funcDesc, JSCallback ptr, void *userdata=0); - template - CScriptVarFunctionNativePtr addNative(const std::string &funcDesc, C *class_ptr, void(C::*class_fnc)(const CFunctionsScopePtr &, void *), void *userdata=0) - { - return addNative(funcDesc, ::newScriptVar(this, class_ptr, class_fnc, userdata)); - } - - /// Send all variables to stdout - void trace(); - - const CScriptVarScopePtr &getRoot() { return root; }; /// gets the root of symbol table - // CScriptVar *root; /// root of symbol table - - template - CScriptVarPtr newScriptVar(T t) { return ::newScriptVar(this, t); } -private: - static bool noexecute; - CScriptTokenizer *t; /// current tokenizer - int runtimeFlags; - std::vector loop_labels; - std::vectorscopes; - CScriptVarScopePtr root; - - const CScriptVarScopePtr &scope() { return scopes.back(); } - - class CScopeControl { - private: - CScopeControl(const CScopeControl& Copy); // no copy - CScopeControl& operator =(const CScopeControl& Copy); - public: - CScopeControl(CTinyJS* Context) : context(Context), count(0) {} - ~CScopeControl() { while(count--) {CScriptVarScopePtr parent = context->scopes.back()->getParent(); if(parent) context->scopes.back() = parent; else context->scopes.pop_back() ;} } - void addFncScope(const CScriptVarScopePtr &Scope) { context->scopes.push_back(Scope); count++; } - void addLetScope() { context->scopes.back() = ::newScriptVar(context, ScopeLet, context->scopes.back()); count++; } - void addWithScope(const CScriptVarPtr &With) { context->scopes.back() = ::newScriptVar(context, ScopeWith, context->scopes.back(), With); count++; } - private: - CTinyJS *context; - int count; - }; - friend class CScopeControl; -public: - CScriptVarPtr objectPrototype; /// Built in object class - CScriptVarPtr arrayPrototype; /// Built in array class - CScriptVarPtr stringPrototype; /// Built in string class - CScriptVarPtr numberPrototype; /// Built in string class - CScriptVarPtr functionPrototype; /// Built in function class -private: - CScriptVarPtr exceptionVar; /// containing the exception var by (runtimeFlags&RUNTIME_THROW) == true; - - void CheckRightHandVar(bool &execute, CScriptVarSmartLink &link) - { - if(execute && link && !link->IsOwned() && !link->owner && link->name.length()>0) - throwError(execute, link->name + " is not defined", t->getPrevPos()); - } - - void CheckRightHandVar(bool &execute, CScriptVarSmartLink &link, CScriptTokenizer::ScriptTokenPosition &Pos) - { - if(execute && link && !link->IsOwned() && !link->owner && link->name.length()>0) - throwError(execute, link->name + " is not defined", Pos); - } - - // function call -public: -// CScriptVarSmartLink callFunction(CScriptVarSmartLink &Function, std::vector &Arguments, const CScriptVarPtr& This, bool &execute); - CScriptVarPtr callFunction(const CScriptVarPtr &Function, std::vector &Arguments, const CScriptVarPtr& This, bool &execute); -private: - // - CScriptVarSmartLink getValue(CScriptVarSmartLink Var, bool execute); - CScriptVarSmartLink setValue(CScriptVarSmartLink Var); - // parsing - in order of precedence - CScriptVarSmartLink execute_literals(bool &execute); - CScriptVarSmartLink execute_member(CScriptVarSmartLink &parent, bool &execute); - CScriptVarSmartLink execute_function_call(bool &execute); - CScriptVarSmartLink execute_unary(bool &execute); - CScriptVarSmartLink execute_term(bool &execute); - CScriptVarSmartLink execute_expression(bool &execute); - CScriptVarSmartLink execute_binary_shift(bool &execute); - CScriptVarSmartLink execute_relation(bool &execute, int set=LEX_EQUAL, int set_n='<'); - CScriptVarSmartLink execute_binary_logic(bool &execute, int op='|', int op_n1='^', int op_n2='&'); - CScriptVarSmartLink execute_logic(bool &execute, int op=LEX_OROR, int op_n=LEX_ANDAND); - CScriptVarSmartLink execute_condition(bool &execute); - CScriptVarSmartLink execute_assignment(bool &execute); - CScriptVarSmartLink execute_base(bool &execute); - void execute_block(bool &execute); - CScriptVarSmartLink execute_statement(bool &execute); - // parsing utility functions - CScriptVarSmartLink parseFunctionDefinition(CScriptToken &FncToken); -// CScriptVarSmartLink parseFunctionDefinition(); -// void parseFunctionArguments(CScriptVar *funcVar); - CScriptVarSmartLink parseFunctionsBodyFromString(const std::string &Parameter, const std::string &FncBody); - - CScriptVarLink *findInScopes(const std::string &childName); ///< Finds a child, looking recursively up the scopes - /// Get all Keynames of en given object (optionial look up the prototype-chain) - void keys(STRING_VECTOR_t &Keys, CScriptVarPtr object, bool WithPrototypeChain); - /// Look up in any parent classes of the given object - CScriptVarLink *findInPrototypeChain(CScriptVarPtr object, const std::string &name); - CScriptVarFunctionNativePtr addNative(const std::string &funcDesc, CScriptVarFunctionNativePtr Var); - - /// throws an Error - void throwError(bool &execute, const std::string &message); - void throwError(bool &execute, const std::string &message, CScriptTokenizer::ScriptTokenPosition &Pos); - - /// native Object-Constructors & prototype-functions - - void native_Object(const CFunctionsScopePtr &c, void *data); - void native_Object_hasOwnProperty(const CFunctionsScopePtr &c, void *data); - - void native_Array(const CFunctionsScopePtr &c, void *data); - - void native_String(const CFunctionsScopePtr &c, void *data); - - void native_Number(const CFunctionsScopePtr &c, void *data); - - void native_Function(const CFunctionsScopePtr &c, void *data); - void native_Function_call(const CFunctionsScopePtr &c, void *data); - void native_Function_apply(const CFunctionsScopePtr &c, void *data); - - /// globale function - - void native_eval(const CFunctionsScopePtr &c, void *data); - void native_isNAN(const CFunctionsScopePtr &c, void *data); - void native_isFinite(const CFunctionsScopePtr &c, void *data); - void native_parseInt(const CFunctionsScopePtr &c, void *data); - void native_parseFloat(const CFunctionsScopePtr &c, void *data); - - - - void native_JSON_parse(const CFunctionsScopePtr &c, void *data); - - - int uniqueID; -public: - int getUniqueID() { return ++uniqueID; } - CScriptVar *first; - void ClearLostVars(const CScriptVarPtr &extra=CScriptVarPtr()); -}; - -#endif +/* + * TinyJS + * + * A single-file Javascript-alike engine + * + * Authored By Gordon Williams + * + * Copyright (C) 2009 Pur3 Ltd + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef TINYJS_H +#define TINYJS_H + +#include +#include +#include +#include +#include "pool_allocator.h" +#include +#include + +#ifndef ASSERT +# define ASSERT(X) assert(X) +#endif + +#ifndef __GNUC__ +# define __attribute__(x) +#endif + + +#undef TRACE +#ifndef TRACE +#define TRACE printf +#endif // TRACE + +const int TINYJS_LOOP_MAX_ITERATIONS = 8192; + +enum LEX_TYPES { + LEX_EOF = 0, +#define LEX_RELATIONS_1_BEGIN LEX_EQUAL + LEX_EQUAL = 256, + LEX_TYPEEQUAL, + LEX_NEQUAL, + LEX_NTYPEEQUAL, +#define LEX_RELATIONS_1_END LEX_NTYPEEQUAL + LEX_LEQUAL, + LEX_GEQUAL, +#define LEX_SHIFTS_BEGIN LEX_LSHIFT + LEX_LSHIFT, + LEX_RSHIFT, + LEX_RSHIFTU, // unsigned +#define LEX_SHIFTS_END LEX_RSHIFTU + LEX_PLUSPLUS, + LEX_MINUSMINUS, + LEX_ANDAND, + LEX_OROR, + LEX_INT, + +#define LEX_ASSIGNMENTS_BEGIN LEX_PLUSEQUAL + LEX_PLUSEQUAL, + LEX_MINUSEQUAL, + LEX_ASTERISKEQUAL, + LEX_SLASHEQUAL, + LEX_PERCENTEQUAL, + LEX_LSHIFTEQUAL, + LEX_RSHIFTEQUAL, + LEX_RSHIFTUEQUAL, // unsigned + LEX_ANDEQUAL, + LEX_OREQUAL, + LEX_XOREQUAL, +#define LEX_ASSIGNMENTS_END LEX_XOREQUAL + +#define LEX_TOKEN_STRING_BEGIN LEX_ID + LEX_ID, + LEX_STR, + LEX_REGEXP, + LEX_T_LABEL, +#define LEX_TOKEN_STRING_END LEX_T_LABEL + + LEX_FLOAT, + + // reserved words + LEX_R_IF, + LEX_R_ELSE, +#define LEX_TOKEN_LOOP_BEGIN LEX_R_DO + LEX_R_DO, + LEX_R_WHILE, + LEX_R_FOR, +#define LEX_TOKEN_LOOP_END LEX_R_FOR + LEX_R_IN, + LEX_R_BREAK, + LEX_R_CONTINUE, + LEX_R_FUNCTION, + LEX_R_RETURN, + LEX_R_VAR, + LEX_R_LET, + LEX_R_WITH, + LEX_R_TRUE, + LEX_R_FALSE, + LEX_R_NULL, + LEX_R_NEW, + LEX_R_TRY, + LEX_R_CATCH, + LEX_R_FINALLY, + LEX_R_THROW, + LEX_R_TYPEOF, + LEX_R_VOID, + LEX_R_DELETE, + LEX_R_INSTANCEOF, + LEX_R_SWITCH, + LEX_R_CASE, + LEX_R_DEFAULT, + + // special token +// LEX_T_FILE, + LEX_T_FUNCTION_OPERATOR, + LEX_T_GET, + LEX_T_SET, + + LEX_T_FOR_IN, + LEX_T_FOR_EACH_IN, + + LEX_T_SKIP, + +}; +#define LEX_TOKEN_DATA_STRING(tk) ((LEX_TOKEN_STRING_BEGIN<= tk && tk <= LEX_TOKEN_STRING_END)) +#define LEX_TOKEN_DATA_FLOAT(tk) (tk==LEX_FLOAT) +#define LEX_TOKEN_DATA_FUNCTION(tk) (tk==LEX_R_FUNCTION || tk==LEX_T_FUNCTION_OPERATOR || tk==LEX_T_SET || tk==LEX_T_GET) +#define LEX_TOKEN_DATA_SIMPLE(tk) (!LEX_TOKEN_DATA_STRING(tk) && !LEX_TOKEN_DATA_FLOAT(tk) && !LEX_TOKEN_DATA_FUNCTION(tk)) +/* +enum SCRIPTVAR_FLAGS { + SCRIPTVAR_UNDEFINED = 0, + SCRIPTVAR_FUNCTION = 1<<0, + SCRIPTVAR_OBJECT = 1<<1, + SCRIPTVAR_ARRAY = 1<<2, + SCRIPTVAR_DOUBLE = 1<<3, // floating point double + SCRIPTVAR_INTEGER = 1<<4, // integer number + SCRIPTVAR_BOOLEAN = 1<<5, // boolean + SCRIPTVAR_STRING = 1<<6, // string + SCRIPTVAR_NULL = 1<<7, // it seems null is its own data type + SCRIPTVAR_INFINITY = 1<<8, // it seems infinity is its own data type + SCRIPTVAR_NAN = 1<<9, // it seems NaN is its own data type + SCRIPTVAR_ACCESSOR = 1<<10, // it seems an Object with get() and set() + + SCRIPTVAR_END_OF_TYPES = 1<<15, // it seems NaN is its own data type + + SCRIPTVAR_NATIVE_FNC = 1<<16, // to specify this is a native function + SCRIPTVAR_NATIVE_MFNC = 1<<17, // to specify this is a native function from class->memberFunc + + SCRIPTVAR_NUMBERMASK = SCRIPTVAR_DOUBLE | + SCRIPTVAR_INTEGER, + SCRIPTVAR_NUMERICMASK = SCRIPTVAR_NUMBERMASK | + SCRIPTVAR_NULL | + SCRIPTVAR_BOOLEAN, + + SCRIPTVAR_VARTYPEMASK = SCRIPTVAR_END_OF_TYPES - 1, + + SCRIPTVAR_NATIVE = SCRIPTVAR_NATIVE_FNC | + SCRIPTVAR_NATIVE_MFNC, +}; +*/ +enum SCRIPTVARLINK_FLAGS { + SCRIPTVARLINK_OWNED = 1<<0, + SCRIPTVARLINK_WRITABLE = 1<<1, + SCRIPTVARLINK_DELETABLE = 1<<2, + SCRIPTVARLINK_ENUMERABLE = 1<<3, + SCRIPTVARLINK_HIDDEN = 1<<4, + SCRIPTVARLINK_DEFAULT = SCRIPTVARLINK_WRITABLE | SCRIPTVARLINK_DELETABLE | SCRIPTVARLINK_ENUMERABLE, +}; +enum RUNTIME_FLAGS { + RUNTIME_CANRETURN = 1<<0, + + RUNTIME_CANBREAK = 1<<1, + RUNTIME_BREAK = 1<<2, + RUNTIME_BREAK_MASK = RUNTIME_CANBREAK | RUNTIME_BREAK, + + RUNTIME_CANCONTINUE = 1<<3, + RUNTIME_CONTINUE = 1<<4, + RUNTIME_LOOP_MASK = RUNTIME_BREAK_MASK | RUNTIME_CANCONTINUE | RUNTIME_CONTINUE, + + RUNTIME_NEW = 1<<5, + + RUNTIME_CANTHROW = 1<<6, + RUNTIME_THROW = 1<<7, + RUNTIME_THROW_MASK = RUNTIME_CANTHROW | RUNTIME_THROW, + +}; + +#define SAVE_RUNTIME_RETURN int old_return_runtimeFlags = runtimeFlags & RUNTIME_CANRETURN +#define RESTORE_RUNTIME_RETURN runtimeFlags = (runtimeFlags & ~RUNTIME_CANRETURN) | old_pass_runtimeFlags +#define SET_RUNTIME_CANRETURN runtimeFlags |= RUNTIME_CANRETURN +#define IS_RUNTIME_CANRETURN ((runtimeFlags & RUNTIME_CANRETURN) == RUNTIME_CANRETURN) + + +#define TINYJS_RETURN_VAR "return" +#define TINYJS_LOKALE_VAR "__locale__" +#define TINYJS_ANONYMOUS_VAR "__anonymous__" +#define TINYJS_ARGUMENTS_VAR "arguments" +#define TINYJS___PROTO___VAR "__proto__" +#define TINYJS_PROTOTYPE_CLASS "prototype" +#define TINYJS_FUNCTION_CLOSURE_VAR "__function_closure__" +#define TINYJS_SCOPE_PARENT_VAR "__scope_parent__" +#define TINYJS_SCOPE_WITH_VAR "__scope_with__" +#define TINYJS_ACCESSOR_GET_VAR "__accessor_get__" +#define TINYJS_ACCESSOR_SET_VAR "__accessor_set__" +#define TINYJS_TEMP_NAME "" +#define TINYJS_BLANK_DATA "" +#define TINYJS_NEGATIVE_INFINITY_DATA "-1" +#define TINYJS_POSITIVE_INFINITY_DATA "+1" + +typedef std::vector STRING_VECTOR_t; +typedef STRING_VECTOR_t::iterator STRING_VECTOR_it; + +/// convert the given string into a quoted string suitable for javascript +std::string getJSString(const std::string &str); +/// convert the given int into a string +std::string int2string(int intData); +/// convert the given double into a string +std::string float2string(const double &floatData); + +// ----------------------------------------------------------------------------------- CScriptSmartPointer +template +class CScriptSmartPointer +{ +public: + CScriptSmartPointer() : pointer(0) {} + explicit CScriptSmartPointer(T *Pointer) : pointer(Pointer ? new p(Pointer) : 0) {} + CScriptSmartPointer(const CScriptSmartPointer &Copy) : pointer(0) {*this = Copy;} + CScriptSmartPointer &operator=(const CScriptSmartPointer &Copy) + { + if(this == &Copy) return *this; + if(pointer) pointer->unref(); + if(Copy.pointer) + pointer = Copy.pointer->ref(); + else + pointer = 0; + return *this; + } + ~CScriptSmartPointer() { release(); } + void release() { if(pointer) pointer->unref(); pointer = 0;} + T *operator->() { return operator T*(); } + operator T*() { return pointer ? pointer->pointer : 0; } +private: + class p + { + public: + p(T *Pointer) : pointer(Pointer), refs(1) {} + p *ref() { refs++; return this; } + void unref() { if(--refs == 0) { delete pointer; delete this; } } + T *pointer; + int refs; + } *pointer; +}; + +class CScriptException { +public: + std::string text; +// int pos; + std::string file; + int line; + int column; +// CScriptException(const std::string &exceptionText, int Pos=-1); + CScriptException(const std::string &Text, const std::string &File, int Line=-1, int Column=-1) : + text(Text), file(File), line(Line), column(Column){} + CScriptException(const std::string &Text, const char *File="", int Line=-1, int Column=-1) : + text(Text), file(File), line(Line), column(Column){} +}; + +// ----------------------------------------------------------------------------------- CSCRIPTLEX +class CScriptLex +{ +public: + CScriptLex(const char *Code, const std::string &File="", int Line=0, int Column=0); + + int tk; ///< The type of the token that we have + int last_tk; ///< The type of the last token that we have + const char *tokenStart; + std::string tkStr; ///< Data contained in the token we have here + + void check(int expected_tk); ///< Lexical check wotsit + void match(int expected_tk); ///< Lexical match wotsit + static std::string getTokenStr(int token); ///< Get the string representation of the given token + void reset(const char *toPos, int line, const char *LineStart); ///< Reset this lex so we can start again + + std::string currentFile; + int currentLine; + const char *currentLineStart; + int currentColumn() { return tokenStart-currentLineStart; } + +private: + const char *data; + const char *dataPos; + char currCh, nextCh; + + void getNextCh(); + void getNextToken(); ///< Get the text token from our text string +}; + +// ----------------------------------------------------------------------------------- CSCRIPTTOKEN +class CScriptToken; +typedef std::vector TOKEN_VECT; +typedef std::vector::iterator TOKEN_VECT_it; +class CScriptTokenData +{ +public: + virtual ~CScriptTokenData() {} + void ref() { refs++; } + void unref() { if(--refs == 0) delete this; } +protected: + CScriptTokenData() : refs(0){} +private: + int refs; +}; +class CScriptTokenDataString : public fixed_size_object, public CScriptTokenData { +public: + CScriptTokenDataString(const std::string &String) : tokenStr(String) {} + std::string tokenStr; +private: +}; +class CScriptTokenDataFnc : public fixed_size_object, public CScriptTokenData { +public: + std::string file; + int line; + std::string name; + std::vector parameter; + TOKEN_VECT body; +private: +}; + +class CScriptTokenizer; +/* + a Token needs 8 Byte + 2 Bytes for the Row-Position of the Token + 2 Bytes for the Token self + and + 4 Bytes for special Datas in an union + e.g. an int for interger-literals + or pointer for double-literals, + for string-literals or for functions +*/ +class CScriptToken : public fixed_size_object +{ +public: + CScriptToken() : line(0), column(0), token(0) {} + CScriptToken(CScriptLex *l, int Match=-1); + CScriptToken(uint16_t Tk, int IntData=0); + CScriptToken(uint16_t Tk, const std::string &TkStr); + CScriptToken(const CScriptToken &Copy) : token(0) { *this = Copy; } + CScriptToken &operator =(const CScriptToken &Copy); + ~CScriptToken() { clear(); } + + int &Int() { ASSERT(LEX_TOKEN_DATA_SIMPLE(token)); return intData; } + std::string &String() { ASSERT(LEX_TOKEN_DATA_STRING(token)); return stringData->tokenStr; } + double &Float() { ASSERT(LEX_TOKEN_DATA_FLOAT(token)); return *floatData; } + CScriptTokenDataFnc &Fnc() { ASSERT(LEX_TOKEN_DATA_FUNCTION(token)); return *fncData; } + uint16_t line; + uint16_t column; + uint16_t token; + void print(std::string &indent); + std::string getParsableString(const std::string Indent=""); + std::string getParsableString(std::string &indentString, int &newln, const std::string &indent); ///< newln ==> 1=inject indentString, 2=inject \n, 3=last='{' +private: + + void clear(); + union { + int intData; + double *floatData; + CScriptTokenDataString *stringData; + CScriptTokenDataFnc *fncData; + }; +}; + +// ----------------------------------------------------------------------------------- CSCRIPTTOKENIZER +/* + the tokenizer converts the code in an Vector with Tokens +*/ +class CScriptTokenizer +{ +public: + struct ScriptTokenPosition { + ScriptTokenPosition(TOKEN_VECT *Tokens) : tokens(Tokens), pos(tokens->begin())/*, currentLine(0)*//*, currentColumn(0)*/ {} + bool operator ==(const ScriptTokenPosition &eq) { return pos == eq.pos; } + ScriptTokenPosition &operator =(const ScriptTokenPosition ©) { + tokens=copy.tokens; pos=copy.pos; +// currentLine=copy.currentLine; + return *this; + } + TOKEN_VECT *tokens; + TOKEN_VECT::iterator pos; + int currentLine() { return pos->line; } + int currentColumn() { return pos->column; } + }; + CScriptTokenizer(); + CScriptTokenizer(CScriptLex &Lexer); + CScriptTokenizer(const char *Code, const std::string &File="", int Line=0, int Column=0); + void tokenizeCode(CScriptLex &Lexer); + + CScriptToken &getToken() { return *(tokenScopeStack.back().pos); } + void getNextToken(); + bool check(int ExpectedToken); + void match(int ExpectedToken); + void pushTokenScope(TOKEN_VECT &Tokens); + ScriptTokenPosition &getPos() { return tokenScopeStack.back(); } + void setPos(ScriptTokenPosition &TokenPos); + ScriptTokenPosition &getPrevPos() { return prevPos; } + void skip(int Tokens); + int tk; // current Token + std::string currentFile; + int currentLine() { return getPos().currentLine();} + int currentColumn() { return getPos().currentColumn();} + std::string &tkStr() { return getToken().String(); } +private: + void tokenizeTry(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); + void tokenizeSwitch(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); + void tokenizeWith(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); + void tokenizeWhile(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); + void tokenizeDo(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); + void tokenizeIf(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); + void tokenizeForIn(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); + void tokenizeFor(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); + void tokenizeFunction(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); + void tokenizeBlock(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); + void tokenizeLet(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); + void tokenizeVar(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); + void tokenizeStatement(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); + void tokenizeToken(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); + int pushToken(TOKEN_VECT &Tokens, int Match=-1); + void throwTokenNotExpected(); + CScriptLex *l; + TOKEN_VECT tokens; + ScriptTokenPosition prevPos; + std::vector tokenScopeStack; +}; +////////////////////////////////////////////////////////////////////////// CScriptVarPointer + +class CScriptVar; +class CScriptVarLink; +class CScriptVarSmartLink; +template +class CScriptVarPointer { +public: + CScriptVarPointer() : var(0) {} ///< explizit 0-Pointer + CScriptVarPointer(CScriptVar *Var) : var(dynamic_cast(Var)) { if(Var) ASSERT(var); if(var) var->ref(); } + CScriptVarPointer(const CScriptVarLink *Link); + CScriptVarPointer(const CScriptVarSmartLink &Link); + //: var(dynamic_cast(Var)) { if(Var) ASSERT(var); if(var) var->ref(); } + ~CScriptVarPointer() { if(var) var->unref(); } + CScriptVarPointer(const CScriptVarPointer &VarPtr) : var(0) { *this = VarPtr; } + CScriptVarPointer& operator=(const CScriptVarPointer &VarPtr) { + if(var != VarPtr.var) { + if(var) var->unref(); + var = VarPtr.var; if(var) var->ref(); + } + return *this; + } + template + operator CScriptVarPointer() const { return CScriptVarPointer(var); } + operator bool() const { return var!=0; } + + template + bool operator ==(const CScriptVarPointer &Other) const { return (CScriptVar*)var == (CScriptVar*)(Other.getVar()); } + template + bool operator !=(const CScriptVarPointer &Other) const { return (CScriptVar*)var != (CScriptVar*)(Other.getVar()); } + + C * operator ->() const { ASSERT(var); return var; } + CScriptVar *getVar() const { ASSERT(var); return var; } +private: + C *var; +}; + +typedef CScriptVarPointer CScriptVarPtr; + +// ----------------------------------------------------------------------------------- CSCRIPTVARLINK +class CScriptVar; +class CScriptVarLink : public fixed_size_object +{ +public: + std::string name; +// const std::string &Name() { return name; } +// void SetName(const std::string &Name) { name = Name; } +private: + CScriptVar *var; +public: + CScriptVarPtr getValue(); +private: +// CScriptVar *get() { return var; } +public: + CScriptVarPtr getVarPtr() { return var; } + CScriptVar *owner; // pointer to the owner CScriptVar + uint32_t flags; + CScriptVarLink(const CScriptVarPtr &var, const std::string &name = TINYJS_TEMP_NAME, int flags = SCRIPTVARLINK_DEFAULT); + CScriptVarLink(const CScriptVarLink &link); ///< Copy constructor + ~CScriptVarLink(); + void replaceWith(const CScriptVarPtr &newVar); ///< Replace the Variable pointed to + void replaceWith(CScriptVarLink *newVar); ///< Replace the Variable pointed to (just dereferences) +// void SetOwner(CScriptVar *Owner) { owner = Owner; } +// void SetOwner(CScriptVar *Owner, bool Owned) { owner = Owner; SetOwned(Owned); } +// bool IsOwned() { return owner != 0; } + bool IsOwned() { return (flags & SCRIPTVARLINK_OWNED) != 0; } + void SetOwned(bool On) { On ? (flags |= SCRIPTVARLINK_OWNED) : (flags &= ~SCRIPTVARLINK_OWNED); } + + bool IsWritable() { return (flags & SCRIPTVARLINK_WRITABLE) != 0; } + void SetWritable(bool On) { On ? (flags |= SCRIPTVARLINK_WRITABLE) : (flags &= ~SCRIPTVARLINK_DELETABLE); } + bool IsDeletable() { return (flags & SCRIPTVARLINK_DELETABLE) != 0; } + void SetDeletable(bool On) { On ? (flags |= SCRIPTVARLINK_DELETABLE) : (flags &= ~SCRIPTVARLINK_DELETABLE); } + bool IsEnumerable() { return (flags & SCRIPTVARLINK_ENUMERABLE) != 0; } + void SetEnumerable(bool On) { On ? (flags |= SCRIPTVARLINK_ENUMERABLE) : (flags &= ~SCRIPTVARLINK_ENUMERABLE); } + bool IsHidden() { return (flags & SCRIPTVARLINK_HIDDEN) != 0; } + void SetHidden(bool On) { On ? (flags |= SCRIPTVARLINK_HIDDEN) : (flags &= ~SCRIPTVARLINK_HIDDEN); } + CScriptVar * operator ->() const { return var; } +// friend class CScriptVar; +// friend class CScriptVarSmartLink; +// friend class CScriptVarObject; +}; +template +CScriptVarPointer::CScriptVarPointer(const CScriptVarLink *Link) : var(0) { if(Link) { var = dynamic_cast(Link->operator->()); ASSERT(var); } if(var) var->ref(); } + +// ----------------------------------------------------------------------------------- CSCRIPTVAR +typedef std::vector SCRIPTVAR_CHILDS_t; +typedef SCRIPTVAR_CHILDS_t::iterator SCRIPTVAR_CHILDS_it; +typedef SCRIPTVAR_CHILDS_t::const_iterator SCRIPTVAR_CHILDS_cit; +class CTinyJS; +class CScriptVarScopeFnc; +typedef CScriptVarPointer CFunctionsScopePtr; +typedef void (*JSCallback)(const CFunctionsScopePtr &var, void *userdata); + + +class CScriptVar : public fixed_size_object { +protected: + CScriptVar(CTinyJS *Context, const CScriptVarPtr &Prototype=CScriptVarPtr()); ///< Create + CScriptVar(const CScriptVar &Copy); ///< Copy protected -> use clone for public +private: + CScriptVar & operator=(const CScriptVar &Copy); ///< private -> no assignment-Copy +public: + virtual ~CScriptVar(void); + virtual CScriptVarPtr clone()=0; + + /// Type + virtual bool isInt(); // {return false;} + virtual bool isBool(); // {return false;} + virtual bool isDouble(); // {return false;} + virtual bool isString(); // {return false;} + virtual bool isNumber(); // {return false;} + virtual bool isNumeric(); // {return false;} + virtual bool isFunction(); // {return false;} + virtual bool isObject(); // {return false;} + virtual bool isArray(); // {return false;} + virtual bool isNative(); // {return false;} + virtual bool isUndefined();// {return false;} + virtual bool isNull(); // {return false;} + virtual bool isNaN(); // {return false;} + virtual int isInfinity(); // { return 0; } ///< +1==POSITIVE_INFINITY, -1==NEGATIVE_INFINITY, 0==is not an InfinityVar + virtual bool isAccessor(); // {return false;} + + bool isBasic() { return Childs.empty(); } ///< Is this *not* an array/object/etc + + + /// Value + virtual int getInt(); ///< return 0 + virtual bool getBool(); ///< return false + virtual double getDouble(); ///< return 0.0 + virtual CScriptTokenDataFnc *getFunctionData(); ///< { return 0; } + virtual std::string getString(); ///< return "" + + virtual std::string getParsableString(const std::string &indentString, const std::string &indent); ///< get Data as a parsable javascript string + virtual std::string getVarType()=0; + virtual CScriptVarPtr getNumericVar(); ///< returns an Integer, a Double, an Infinity or a NaN + + /// find + CScriptVarLink *findChild(const std::string &childName); ///< Tries to find a child with the given name, may return 0 + CScriptVarLink *findChildByPath(const std::string &path); ///< Tries to find a child with the given path (separated by dots) + CScriptVarLink *findChildOrCreate(const std::string &childName/*, int varFlags=SCRIPTVAR_UNDEFINED*/); ///< Tries to find a child with the given name, or will create it with the given flags + CScriptVarLink *findChildOrCreateByPath(const std::string &path); ///< Tries to find a child with the given path (separated by dots) + + /// add & remove + CScriptVarLink *addChild(const std::string &childName, const CScriptVarPtr &child, int linkFlags = SCRIPTVARLINK_DEFAULT); + CScriptVarLink *addChildNoDup(const std::string &childName, const CScriptVarPtr &child, int linkFlags = SCRIPTVARLINK_DEFAULT); ///< add a child overwriting any with the same name + bool removeLink(CScriptVarLink *&link); ///< Remove a specific link (this is faster than finding via a child) + void removeAllChildren(); + + + /// funcions for FUNCTION + void setReturnVar(const CScriptVarPtr &var); ///< Set the result value. Use this when setting complex return data as it avoids a deepCopy() + CScriptVarPtr getParameter(const std::string &name); ///< If this is a function, get the parameter with the given name (for use by native functions) + CScriptVarPtr getParameter(int Idx); ///< If this is a function, get the parameter with the given index (for use by native functions) + int getParameterLength(); ///< If this is a function, get the count of parameters + + + /// ARRAY + CScriptVarPtr getArrayIndex(int idx); ///< The the value at an array index + void setArrayIndex(int idx, const CScriptVarPtr &value); ///< Set the value at an array index + int getArrayLength(); ///< If this is an array, return the number of items in it (else 0) + + ////////////////////////////////////////////////////////////////////////// + int getChildren() { return Childs.size(); } ///< Get the number of children + CTinyJS *getContext() { return context; } + CScriptVarPtr mathsOp(const CScriptVarPtr &b, int op); ///< do a maths op with another script variable + + void trace(const std::string &name = ""); ///< Dump out the contents of this using trace + void trace(std::string &indentStr, int uniqueID, const std::string &name = ""); ///< Dump out the contents of this using trace + std::string getFlagsAsString(); ///< For debugging - just dump a string version of the flags +// void getJSON(std::ostringstream &destination, const std::string linePrefix=""); ///< Write out all the JS code needed to recreate this script variable to the stream (as JSON) + + SCRIPTVAR_CHILDS_t Childs; + + /// For memory management/garbage collection + CScriptVar *ref(); ///< Add reference to this variable + void unref(); ///< Remove a reference, and delete this variable if required + int getRefs(); ///< Get the number of references to this script variable + template + operator T *(){ T* ret = dynamic_cast(this); ASSERT(ret!=0); return ret; } + template + T *get(){ T* ret = dynamic_cast(this); ASSERT(ret!=0); return ret; } + + template + CScriptVarPtr newScriptVar(T t); // { return ::newScriptVar(context, t); } + void setTempraryID(int ID) { temporaryID = ID; } + void setTempraryIDrecursive(int ID); + int getTempraryID() { return temporaryID; } +protected: + CTinyJS *context; + int refs; ///< The number of references held to this - used for garbage collection + CScriptVar *prev; +public: + CScriptVar *next; + int temporaryID; +}; +////////////////////////////////////////////////////////////////////////// +#define define_dummy_t(t1) struct t1##_t{}; extern t1##_t t1 +#define declare_dummy_t(t1) t1##_t t1 +#define define_newScriptVar_Fnc(t1, ...) CScriptVar##t1##Ptr newScriptVar(__VA_ARGS__) +#define define_ScriptVarPtr_Type(t1) class CScriptVar##t1; typedef CScriptVarPointer CScriptVar##t1##Ptr + + +////////////////////////////////////////////////////////////////////////// CScriptVarObject + +define_dummy_t(Object); +define_ScriptVarPtr_Type(Object); + +class CScriptVarObject : public CScriptVar { +protected: + CScriptVarObject(CTinyJS *Context, const CScriptVarPtr &Prototype=CScriptVarPtr()) : CScriptVar(Context, Prototype) {} + CScriptVarObject(const CScriptVarObject &Copy) : CScriptVar(Copy) {} ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarObject(); + virtual CScriptVarPtr clone(); + virtual bool isObject(); // { return true; } + + virtual std::string getString(); // { return "[ Object ]"; }; + virtual std::string getParsableString(const std::string &indentString, const std::string &indent); + virtual std::string getVarType(); // { return "object"; } +private: + + friend define_newScriptVar_Fnc(Object, CTinyJS* Context, Object_t); + friend define_newScriptVar_Fnc(Object, CTinyJS* Context, Object_t, const CScriptVarPtr &); +}; +inline define_newScriptVar_Fnc(Object, CTinyJS* Context, Object_t) { return new CScriptVarObject(Context); } +inline define_newScriptVar_Fnc(Object, CTinyJS* Context, Object_t, const CScriptVarPtr &Prototype) { return new CScriptVarObject(Context, Prototype); } + + +////////////////////////////////////////////////////////////////////////// CScriptVarAccessor + +define_dummy_t(Accessor); +define_ScriptVarPtr_Type(Accessor); + +class CScriptVarAccessor : public CScriptVar { +protected: + CScriptVarAccessor(CTinyJS *Context) : CScriptVar(Context) {} + CScriptVarAccessor(const CScriptVarAccessor &Copy) : CScriptVar(Copy) {} ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarAccessor(); + virtual CScriptVarPtr clone(); + virtual bool isAccessor(); // { return true; } + + virtual std::string getString(); // { return "[ Object ]"; }; + virtual std::string getParsableString(const std::string &indentString, const std::string &indent); + virtual std::string getVarType(); // { return "object"; } + + CScriptVarPtr getValue(); + + friend define_newScriptVar_Fnc(Accessor, CTinyJS* Context, Accessor_t); + +}; +inline define_newScriptVar_Fnc(Accessor, CTinyJS* Context, Accessor_t) { return new CScriptVarAccessor(Context); } + + +////////////////////////////////////////////////////////////////////////// CScriptVarArray + +define_dummy_t(Array); +define_ScriptVarPtr_Type(Array); +class CScriptVarArray : public CScriptVar { +protected: + CScriptVarArray(CTinyJS *Context); + CScriptVarArray(const CScriptVarArray &Copy) : CScriptVar(Copy) {} ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarArray(); + virtual CScriptVarPtr clone(); + virtual bool isArray(); // { return true; } + virtual std::string getString(); + virtual std::string getVarType(); // { return "object"; } + virtual std::string getParsableString(const std::string &indentString, const std::string &indent); + friend define_newScriptVar_Fnc(Array, CTinyJS* Context, Array_t); +private: + void native_Length(const CFunctionsScopePtr &c, void *data); +}; +inline define_newScriptVar_Fnc(Array, CTinyJS* Context, Array_t) { return new CScriptVarArray(Context); } + + +////////////////////////////////////////////////////////////////////////// CScriptVarNull + +define_dummy_t(Null); +define_ScriptVarPtr_Type(Null); +class CScriptVarNull : public CScriptVar { +protected: + CScriptVarNull(CTinyJS *Context) : CScriptVar(Context) {} + CScriptVarNull(const CScriptVarNull &Copy) : CScriptVar(Copy) {} ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarNull(); + virtual CScriptVarPtr clone(); + virtual bool isNull(); // { return true; } + virtual std::string getString(); // { return "null"; }; + virtual std::string getVarType(); // { return "null"; } + virtual CScriptVarPtr getNumericVar(); ///< returns an Integer, a Double, an Infinity or a NaN + friend define_newScriptVar_Fnc(Null, CTinyJS* Context, Null_t); +}; +inline define_newScriptVar_Fnc(Null, CTinyJS* Context, Null_t) { return new CScriptVarNull(Context); } + + +////////////////////////////////////////////////////////////////////////// CScriptVarUndefined + +define_dummy_t(Undefined); +define_ScriptVarPtr_Type(Undefined); +class CScriptVarUndefined : public CScriptVar { +protected: + CScriptVarUndefined(CTinyJS *Context) : CScriptVar(Context) {} + CScriptVarUndefined(const CScriptVarUndefined &Copy) : CScriptVar(Copy) {} ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarUndefined(); + virtual CScriptVarPtr clone(); + virtual bool isUndefined(); // { return true; } + virtual std::string getString(); // { return "undefined"; }; + virtual std::string getVarType(); // { return "undefined"; } + friend define_newScriptVar_Fnc(Undefined, CTinyJS* Context, Undefined_t); +}; +inline define_newScriptVar_Fnc(Undefined, CTinyJS* Context, Undefined_t) { return new CScriptVarUndefined(Context); } + + +////////////////////////////////////////////////////////////////////////// CScriptVarNaN + +define_dummy_t(NaN); +define_ScriptVarPtr_Type(NaN); +class CScriptVarNaN : public CScriptVar { +protected: + CScriptVarNaN(CTinyJS *Context) : CScriptVar(Context) {} + CScriptVarNaN(const CScriptVarNaN &Copy) : CScriptVar(Copy) {} ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarNaN(); + virtual CScriptVarPtr clone(); + virtual bool isNaN();// { return true; } + virtual std::string getString(); // { return "NaN"; }; + virtual std::string getVarType(); // { return "number"; } + friend define_newScriptVar_Fnc(NaN, CTinyJS* Context, NaN_t); +}; +inline define_newScriptVar_Fnc(NaN, CTinyJS* Context, NaN_t) { return new CScriptVarNaN(Context); } + + +////////////////////////////////////////////////////////////////////////// CScriptVarString + +define_ScriptVarPtr_Type(String); +class CScriptVarString : public CScriptVar { +protected: + CScriptVarString(CTinyJS *Context, const std::string &Data); + CScriptVarString(const CScriptVarString &Copy) : CScriptVar(Copy), data(Copy.data) {} ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarString(); + virtual CScriptVarPtr clone(); + virtual bool isString(); // { return true; } + virtual int getInt(); // {return strtol(data.c_str(),0,0); } + virtual bool getBool(); // {return data.length()!=0;} + virtual double getDouble(); // {return strtod(data.c_str(),0);} + virtual std::string getString(); // { return data; } + virtual std::string getParsableString(); // { return getJSString(data); } + virtual std::string getVarType(); // { return "string"; } + virtual CScriptVarPtr getNumericVar(); ///< returns an Integer, a Double, an Infinity or a NaN +protected: + std::string data; +private: + void native_Length(const CFunctionsScopePtr &c, void *data); + + friend define_newScriptVar_Fnc(String, CTinyJS* Context, const std::string &); + friend define_newScriptVar_Fnc(String, CTinyJS* Context, const char *); +}; +inline define_newScriptVar_Fnc(String, CTinyJS* Context, const std::string &Obj) { return new CScriptVarString(Context, Obj); } +inline define_newScriptVar_Fnc(String, CTinyJS* Context, const char *Obj) { return new CScriptVarString(Context, Obj); } + + +////////////////////////////////////////////////////////////////////////// CScriptVarIntegerBase + +class CScriptVarIntegerBase : public CScriptVar { +protected: + CScriptVarIntegerBase(CTinyJS *Context, int Data); + CScriptVarIntegerBase(const CScriptVarIntegerBase &Copy) : CScriptVar(Copy), data(Copy.data) {} ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarIntegerBase(); + virtual int getInt(); // {return data; } + virtual bool getBool(); // {return data!=0;} + virtual double getDouble(); // {return data;} + virtual std::string getString(); // {return int2string(data);} + virtual std::string getVarType(); // { return "number"; } + virtual CScriptVarPtr getNumericVar(); ///< returns an Integer, a Double, an Infinity or a NaN +protected: + int data; +}; + + +////////////////////////////////////////////////////////////////////////// CScriptVarInteger + +define_ScriptVarPtr_Type(Integer); +class CScriptVarInteger : public CScriptVarIntegerBase { +protected: + CScriptVarInteger(CTinyJS *Context, int Data) : CScriptVarIntegerBase(Context, Data) {} + CScriptVarInteger(const CScriptVarInteger &Copy) : CScriptVarIntegerBase(Copy) {} ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarInteger(); + virtual CScriptVarPtr clone(); + virtual bool isNumber(); // { return true; } + virtual bool isInt(); // { return true; } + friend define_newScriptVar_Fnc(Integer, CTinyJS* Context, int); +}; +inline define_newScriptVar_Fnc(Integer, CTinyJS* Context, int Obj) { return new CScriptVarInteger(Context, Obj); } + + +////////////////////////////////////////////////////////////////////////// CScriptVarBool + +define_ScriptVarPtr_Type(Bool); +class CScriptVarBool : public CScriptVarIntegerBase { +protected: + CScriptVarBool(CTinyJS *Context, bool Data) : CScriptVarIntegerBase(Context, Data?1:0) {} + CScriptVarBool(const CScriptVarBool &Copy) : CScriptVarIntegerBase(Copy) {} ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarBool(); + virtual CScriptVarPtr clone(); + virtual bool isBool(); // { return true; } + virtual std::string getString(); // {return data!=0?"true":"false";} + virtual std::string getVarType(); // { return "boolean"; } + virtual CScriptVarPtr getNumericVar(); ///< returns an Integer, a Double, an Infinity or a NaN + friend define_newScriptVar_Fnc(Bool, CTinyJS* Context, bool); +}; +inline define_newScriptVar_Fnc(Bool, CTinyJS* Context, bool Obj) { return new CScriptVarBool(Context, Obj); } + + +////////////////////////////////////////////////////////////////////////// CScriptVarInfinity + +struct Infinity{Infinity(int Sig=1):sig(Sig){} int sig; }; +extern Infinity InfinityPositive; +extern Infinity InfinityNegative; +define_ScriptVarPtr_Type(Infinity); +class CScriptVarInfinity : public CScriptVarIntegerBase { +protected: + CScriptVarInfinity(CTinyJS *Context, int Data) : CScriptVarIntegerBase(Context, Data<0?-1:1) {} + CScriptVarInfinity(const CScriptVarInfinity &Copy) : CScriptVarIntegerBase(Copy) {} ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarInfinity(); + virtual CScriptVarPtr clone(); + virtual int isInfinity(); // { return data; } + virtual std::string getString(); // {return data<0?"-Infinity":"Infinity";} + friend define_newScriptVar_Fnc(Infinity, CTinyJS* Context, Infinity); +}; +inline define_newScriptVar_Fnc(Infinity, CTinyJS* Context, Infinity Obj) { return new CScriptVarInfinity(Context, Obj.sig); } + + +////////////////////////////////////////////////////////////////////////// CScriptVarDouble + +define_ScriptVarPtr_Type(Double); +class CScriptVarDouble : public CScriptVar { +protected: + CScriptVarDouble(CTinyJS *Context, double Data); + CScriptVarDouble(const CScriptVarDouble &Copy) : CScriptVar(Copy), data(Copy.data) {} ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarDouble(); + virtual CScriptVarPtr clone(); + virtual bool isNumber(); // { return true; } + virtual bool isDouble(); // { return true; } + virtual int getInt(); // {return (int)data; } + virtual bool getBool(); // {return data!=0.0;} + virtual double getDouble(); // {return data;} + virtual std::string getString(); // {return float2string(data);} + virtual std::string getVarType(); // { return "number"; } + virtual CScriptVarPtr getNumericVar(); ///< returns an Integer, a Double, an Infinity or a NaN +private: + double data; + friend define_newScriptVar_Fnc(Double, CTinyJS* Context, double); +}; +inline define_newScriptVar_Fnc(Double, CTinyJS* Context, double Obj) { return new CScriptVarDouble(Context, Obj); } + + +////////////////////////////////////////////////////////////////////////// CScriptVarFunction + +define_ScriptVarPtr_Type(Function); +class CScriptVarFunction : public CScriptVar { +protected: + CScriptVarFunction(CTinyJS *Context, CScriptTokenDataFnc *Data); + CScriptVarFunction(const CScriptVarFunction &Copy) : CScriptVar(Copy), data(Copy.data) { data->ref(); } ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarFunction(); + virtual CScriptVarPtr clone(); + virtual bool isFunction(); // { return true; } + + virtual std::string getString(); // {return "[ Function ]";} + virtual std::string getVarType(); // { return "function"; } + virtual std::string getParsableString(const std::string &indentString, const std::string &indent); + virtual CScriptTokenDataFnc *getFunctionData(); + void setFunctionData(CScriptTokenDataFnc *Data); +private: + CScriptTokenDataFnc *data; + std::string getParsableBlockString(TOKEN_VECT::iterator &it, TOKEN_VECT::iterator end, const std::string indentString, const std::string indent); + + + friend define_newScriptVar_Fnc(Function, CTinyJS* Context, CScriptTokenDataFnc *); +}; +inline define_newScriptVar_Fnc(Function, CTinyJS* Context, CScriptTokenDataFnc *Obj) { return new CScriptVarFunction(Context, Obj); } + + +////////////////////////////////////////////////////////////////////////// CScriptVarFunctionNative + +define_ScriptVarPtr_Type(FunctionNative); +class CScriptVarFunctionNative : public CScriptVarFunction { +protected: + CScriptVarFunctionNative(CTinyJS *Context, void *Userdata) : CScriptVarFunction(Context, 0), jsUserData(Userdata) { } + CScriptVarFunctionNative(const CScriptVarFunctionNative &Copy) : CScriptVarFunction(Copy), jsUserData(Copy.jsUserData) { } ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarFunctionNative(); + virtual CScriptVarPtr clone()=0; + virtual bool isNative(); // { return true; } + + virtual std::string getString(); // {return "[ Function Native ]";} + + virtual void callFunction(const CFunctionsScopePtr &c)=0;// { jsCallback(c, jsCallbackUserData); } +protected: + void *jsUserData; ///< user data passed as second argument to native functions +}; + + +////////////////////////////////////////////////////////////////////////// CScriptVarFunctionNativeCallback + + +define_ScriptVarPtr_Type(FunctionNativeCallback); +class CScriptVarFunctionNativeCallback : public CScriptVarFunctionNative { +protected: + CScriptVarFunctionNativeCallback(CTinyJS *Context, JSCallback Callback, void *Userdata) : CScriptVarFunctionNative(Context, Userdata), jsCallback(Callback) { } + CScriptVarFunctionNativeCallback(const CScriptVarFunctionNativeCallback &Copy) : CScriptVarFunctionNative(Copy), jsCallback(Copy.jsCallback) { } ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarFunctionNativeCallback(); + virtual CScriptVarPtr clone(); + virtual void callFunction(const CFunctionsScopePtr &c); +private: + JSCallback jsCallback; ///< Callback for native functions + friend define_newScriptVar_Fnc(FunctionNativeCallback, CTinyJS* Context, JSCallback Callback, void*); +}; +inline define_newScriptVar_Fnc(FunctionNativeCallback, CTinyJS* Context, JSCallback Callback, void* Userdata) { return new CScriptVarFunctionNativeCallback(Context, Callback, Userdata); } + + +////////////////////////////////////////////////////////////////////////// CScriptVarFunctionNativeClass + +template +class CScriptVarFunctionNativeClass : public CScriptVarFunctionNative { +protected: + CScriptVarFunctionNativeClass(CTinyJS *Context, native *ClassPtr, void (native::*ClassFnc)(const CFunctionsScopePtr &, void *), void *Userdata) : CScriptVarFunctionNative(Context, Userdata), classPtr(ClassPtr), classFnc(ClassFnc) { } + CScriptVarFunctionNativeClass(const CScriptVarFunctionNativeClass &Copy) : CScriptVarFunctionNative(Copy), classPtr(Copy.classPtr), classFnc(Copy.classFnc) { } ///< Copy protected -> use clone for public +public: + virtual CScriptVarPtr clone() { return new CScriptVarFunctionNativeClass(*this); } + + virtual void callFunction(const CFunctionsScopePtr &c) { (classPtr->*classFnc)(c, jsUserData); } +private: + native *classPtr; + void (native::*classFnc)(const CFunctionsScopePtr &c, void *userdata); + template + friend CScriptVarFunctionNativePtr newScriptVar(CTinyJS* Context, native2 *ClassPtr, void (native2::*ClassFnc)(const CFunctionsScopePtr &, void *), void *Userdata); +}; +template +inline CScriptVarFunctionNativePtr newScriptVar(CTinyJS* Context, native *ClassPtr, void (native::*ClassFnc)(const CFunctionsScopePtr &, void *), void *Userdata) { return new CScriptVarFunctionNativeClass(Context, ClassPtr, ClassFnc, Userdata); } + + +////////////////////////////////////////////////////////////////////////// CScriptVarScope + +define_dummy_t(Scope); +define_ScriptVarPtr_Type(Scope); +class CScriptVarScope : public CScriptVarObject { +protected: // only derived classes or friends can be created + CScriptVarScope(CTinyJS *Context) // constructor for rootScope + : CScriptVarObject(Context) {} + virtual CScriptVarPtr clone(); + virtual bool isObject(); // { return false; } +public: + virtual ~CScriptVarScope(); + virtual CScriptVarPtr scopeVar(); ///< to create var like: var a = ... + virtual CScriptVarPtr scopeLet(); ///< to create var like: let a = ... + virtual CScriptVarLink *findInScopes(const std::string &childName); + virtual CScriptVarScopePtr getParent(); + friend define_newScriptVar_Fnc(Scope, CTinyJS* Context, Scope_t); +}; +inline define_newScriptVar_Fnc(Scope, CTinyJS* Context, Scope_t) { return new CScriptVarScope(Context); } + + +////////////////////////////////////////////////////////////////////////// CScriptVarScopeFnc + +define_dummy_t(ScopeFnc); +define_ScriptVarPtr_Type(ScopeFnc); +class CScriptVarScopeFnc : public CScriptVarScope { +protected: // only derived classes or friends can be created + CScriptVarScopeFnc(CTinyJS *Context, const CScriptVarScopePtr &Closure) // constructor for FncScope + : CScriptVarScope(Context), closure(Closure ? addChild(TINYJS_FUNCTION_CLOSURE_VAR, Closure, 0) : 0) {} +public: + virtual ~CScriptVarScopeFnc(); + virtual CScriptVarLink *findInScopes(const std::string &childName); +protected: + CScriptVarLink *closure; + friend define_newScriptVar_Fnc(ScopeFnc, CTinyJS* Context, ScopeFnc_t, const CScriptVarScopePtr &Closure); +}; +inline define_newScriptVar_Fnc(ScopeFnc, CTinyJS* Context, ScopeFnc_t, const CScriptVarScopePtr &Closure) { return new CScriptVarScopeFnc(Context, Closure); } + + +////////////////////////////////////////////////////////////////////////// CScriptVarScopeLet + +define_dummy_t(ScopeLet); +define_ScriptVarPtr_Type(ScopeLet); +class CScriptVarScopeLet : public CScriptVarScope { +protected: // only derived classes or friends can be created + CScriptVarScopeLet(const CScriptVarScopePtr &Parent); // constructor for LetScope +// : CScriptVarScope(Parent->getContext()), parent( context->getRoot() != Parent ? addChild(TINYJS_SCOPE_PARENT_VAR, Parent, 0) : 0) {} +public: + virtual ~CScriptVarScopeLet(); + virtual CScriptVarLink *findInScopes(const std::string &childName); + virtual CScriptVarPtr scopeVar(); ///< to create var like: var a = ... + virtual CScriptVarScopePtr getParent(); +protected: + CScriptVarLink *parent; + friend define_newScriptVar_Fnc(ScopeLet, CTinyJS* Context, ScopeLet_t, const CScriptVarScopePtr &Parent); +}; +inline define_newScriptVar_Fnc(ScopeLet, CTinyJS* , ScopeLet_t, const CScriptVarScopePtr &Parent) { return new CScriptVarScopeLet(Parent); } + + +////////////////////////////////////////////////////////////////////////// CScriptVarScopeWith + +define_dummy_t(ScopeWith); +define_ScriptVarPtr_Type(ScopeWith); +class CScriptVarScopeWith : public CScriptVarScopeLet { +protected: + CScriptVarScopeWith(const CScriptVarScopePtr &Parent, const CScriptVarPtr &With) + : CScriptVarScopeLet(Parent), with(addChild(TINYJS_SCOPE_WITH_VAR, With, 0)) {} + +public: + virtual ~CScriptVarScopeWith(); + virtual CScriptVarPtr scopeLet(); ///< to create var like: let a = ... + virtual CScriptVarLink *findInScopes(const std::string &childName); +private: + CScriptVarLink *with; + friend define_newScriptVar_Fnc(ScopeWith, CTinyJS* Context, ScopeWith_t, const CScriptVarScopePtr &Parent, const CScriptVarPtr &With); +}; +inline define_newScriptVar_Fnc(ScopeWith, CTinyJS* , ScopeWith_t, const CScriptVarScopePtr &Parent, const CScriptVarPtr &With) { return new CScriptVarScopeWith(Parent, With); } + +////////////////////////////////////////////////////////////////////////// +template +inline CScriptVarPtr CScriptVar::newScriptVar(T t) { return ::newScriptVar(context, t); } +////////////////////////////////////////////////////////////////////////// +// ----------------------------------------------------------------------------------- CSCRIPTVARSMARTLINK +class CScriptVarSmartLink : public fixed_size_object +{ +public: + CScriptVarSmartLink() : link(0){} + CScriptVarSmartLink(CScriptVarLink *Link) : link(Link) {} + explicit CScriptVarSmartLink (CScriptVarPtr Var) : link(0) { *this = Var; } + ~ CScriptVarSmartLink() { if(link && !link->IsOwned()) delete link; } + /// Copy - when copying a SmartLink to an other then the right hand side will lost your link + CScriptVarSmartLink (const CScriptVarSmartLink &Link) : link(0) { *this = Link; } + CScriptVarSmartLink &operator = (const CScriptVarSmartLink &Link); + ///- + CScriptVarSmartLink &operator = (CScriptVarLink *Link); + CScriptVarSmartLink &operator = (CScriptVarPtr Var); + CScriptVarLink *operator ->() const { return link; } + CScriptVarLink &operator *() { return *link; } + CScriptVarSmartLink &operator << (CScriptVarSmartLink &Link); + operator bool() const { return link != 0; } +// operator CScriptVarLink *() { return link; } + CScriptVarLink *&getLink() { return link; }; +private: + CScriptVarLink *link; +}; +template +CScriptVarPointer::CScriptVarPointer(const CScriptVarSmartLink &Link) : var(0) { if(Link) { var = dynamic_cast(Link->operator->()); ASSERT(var); } if(var) var->ref(); } + +// ----------------------------------------------------------------------------------- CTINYJS +class CTinyJS { +public: + CTinyJS(); + ~CTinyJS(); + + void execute(CScriptTokenizer &Tokenizer); + void execute(const char *Code, const std::string &File="", int Line=0, int Column=0); + void execute(const std::string &Code, const std::string &File="", int Line=0, int Column=0); + /** Evaluate the given code and return a link to a javascript object, + * useful for (dangerous) JSON parsing. If nothing to return, will return + * 'undefined' variable type. CScriptVarLink is returned as this will + * automatically unref the result as it goes out of scope. If you want to + * keep it, you must use ref() and unref() */ + CScriptVarLink evaluateComplex(CScriptTokenizer &Tokenizer); + /** Evaluate the given code and return a link to a javascript object, + * useful for (dangerous) JSON parsing. If nothing to return, will return + * 'undefined' variable type. CScriptVarLink is returned as this will + * automatically unref the result as it goes out of scope. If you want to + * keep it, you must use ref() and unref() */ + CScriptVarLink evaluateComplex(const char *code, const std::string &File="", int Line=0, int Column=0); + /** Evaluate the given code and return a link to a javascript object, + * useful for (dangerous) JSON parsing. If nothing to return, will return + * 'undefined' variable type. CScriptVarLink is returned as this will + * automatically unref the result as it goes out of scope. If you want to + * keep it, you must use ref() and unref() */ + CScriptVarLink evaluateComplex(const std::string &code, const std::string &File="", int Line=0, int Column=0); + /** Evaluate the given code and return a string. If nothing to return, will return + * 'undefined' */ + std::string evaluate(CScriptTokenizer &Tokenizer); + /** Evaluate the given code and return a string. If nothing to return, will return + * 'undefined' */ + std::string evaluate(const char *code, const std::string &File="", int Line=0, int Column=0); + /** Evaluate the given code and return a string. If nothing to return, will return + * 'undefined' */ + std::string evaluate(const std::string &code, const std::string &File="", int Line=0, int Column=0); + + /// add a native function to be called from TinyJS + /** example: + \code + void scRandInt(const CFunctionsScopePtr &c, void *userdata) { ... } + tinyJS->addNative("function randInt(min, max)", scRandInt, 0); + \endcode + + or + + \code + void scSubstring(const CFunctionsScopePtr &c, void *userdata) { ... } + tinyJS->addNative("function String.substring(lo, hi)", scSubstring, 0); + \endcode + or + + \code + class Class + { + public: + void scSubstring(const CFunctionsScopePtr &c, void *userdata) { ... } + }; + Class Instanz; + tinyJS->addNative("function String.substring(lo, hi)", &Instanz, &Class::*scSubstring, 0); + \endcode + */ + + CScriptVarFunctionNativePtr addNative(const std::string &funcDesc, JSCallback ptr, void *userdata=0); + template + CScriptVarFunctionNativePtr addNative(const std::string &funcDesc, C *class_ptr, void(C::*class_fnc)(const CFunctionsScopePtr &, void *), void *userdata=0) + { + return addNative(funcDesc, ::newScriptVar(this, class_ptr, class_fnc, userdata)); + } + + /// Send all variables to stdout + void trace(); + + const CScriptVarScopePtr &getRoot() { return root; }; /// gets the root of symbol table + // CScriptVar *root; /// root of symbol table + + template + CScriptVarPtr newScriptVar(T t) { return ::newScriptVar(this, t); } +private: + static bool noexecute; + CScriptTokenizer *t; /// current tokenizer + int runtimeFlags; + std::vector loop_labels; + std::vectorscopes; + CScriptVarScopePtr root; + + const CScriptVarScopePtr &scope() { return scopes.back(); } + + class CScopeControl { + private: + CScopeControl(const CScopeControl& Copy); // no copy + CScopeControl& operator =(const CScopeControl& Copy); + public: + CScopeControl(CTinyJS* Context) : context(Context), count(0) {} + ~CScopeControl() { while(count--) {CScriptVarScopePtr parent = context->scopes.back()->getParent(); if(parent) context->scopes.back() = parent; else context->scopes.pop_back() ;} } + void addFncScope(const CScriptVarScopePtr &Scope) { context->scopes.push_back(Scope); count++; } + void addLetScope() { context->scopes.back() = ::newScriptVar(context, ScopeLet, context->scopes.back()); count++; } + void addWithScope(const CScriptVarPtr &With) { context->scopes.back() = ::newScriptVar(context, ScopeWith, context->scopes.back(), With); count++; } + private: + CTinyJS *context; + int count; + }; + friend class CScopeControl; +public: + CScriptVarPtr objectPrototype; /// Built in object class + CScriptVarPtr arrayPrototype; /// Built in array class + CScriptVarPtr stringPrototype; /// Built in string class + CScriptVarPtr numberPrototype; /// Built in string class + CScriptVarPtr functionPrototype; /// Built in function class +private: + CScriptVarPtr exceptionVar; /// containing the exception var by (runtimeFlags&RUNTIME_THROW) == true; + + void CheckRightHandVar(bool &execute, CScriptVarSmartLink &link) + { + if(execute && link && !link->IsOwned() && !link->owner && link->name.length()>0) + throwError(execute, link->name + " is not defined", t->getPrevPos()); + } + + void CheckRightHandVar(bool &execute, CScriptVarSmartLink &link, CScriptTokenizer::ScriptTokenPosition &Pos) + { + if(execute && link && !link->IsOwned() && !link->owner && link->name.length()>0) + throwError(execute, link->name + " is not defined", Pos); + } + + // function call +public: +// CScriptVarSmartLink callFunction(CScriptVarSmartLink &Function, std::vector &Arguments, const CScriptVarPtr& This, bool &execute); + CScriptVarPtr callFunction(const CScriptVarPtr &Function, std::vector &Arguments, const CScriptVarPtr& This, bool &execute); +private: + // + CScriptVarSmartLink getValue(CScriptVarSmartLink Var, bool execute); + CScriptVarSmartLink setValue(CScriptVarSmartLink Var); + // parsing - in order of precedence + CScriptVarSmartLink execute_literals(bool &execute); + CScriptVarSmartLink execute_member(CScriptVarSmartLink &parent, bool &execute); + CScriptVarSmartLink execute_function_call(bool &execute); + CScriptVarSmartLink execute_unary(bool &execute); + CScriptVarSmartLink execute_term(bool &execute); + CScriptVarSmartLink execute_expression(bool &execute); + CScriptVarSmartLink execute_binary_shift(bool &execute); + CScriptVarSmartLink execute_relation(bool &execute, int set=LEX_EQUAL, int set_n='<'); + CScriptVarSmartLink execute_binary_logic(bool &execute, int op='|', int op_n1='^', int op_n2='&'); + CScriptVarSmartLink execute_logic(bool &execute, int op=LEX_OROR, int op_n=LEX_ANDAND); + CScriptVarSmartLink execute_condition(bool &execute); + CScriptVarSmartLink execute_assignment(bool &execute); + CScriptVarSmartLink execute_base(bool &execute); + void execute_block(bool &execute); + CScriptVarSmartLink execute_statement(bool &execute); + // parsing utility functions + CScriptVarSmartLink parseFunctionDefinition(CScriptToken &FncToken); +// CScriptVarSmartLink parseFunctionDefinition(); +// void parseFunctionArguments(CScriptVar *funcVar); + CScriptVarSmartLink parseFunctionsBodyFromString(const std::string &Parameter, const std::string &FncBody); + + CScriptVarLink *findInScopes(const std::string &childName); ///< Finds a child, looking recursively up the scopes + /// Get all Keynames of en given object (optionial look up the prototype-chain) + void keys(STRING_VECTOR_t &Keys, CScriptVarPtr object, bool WithPrototypeChain); + /// Look up in any parent classes of the given object + CScriptVarLink *findInPrototypeChain(CScriptVarPtr object, const std::string &name); + CScriptVarFunctionNativePtr addNative(const std::string &funcDesc, CScriptVarFunctionNativePtr Var); + + /// throws an Error + void throwError(bool &execute, const std::string &message); + void throwError(bool &execute, const std::string &message, CScriptTokenizer::ScriptTokenPosition &Pos); + + /// native Object-Constructors & prototype-functions + + void native_Object(const CFunctionsScopePtr &c, void *data); + void native_Object_hasOwnProperty(const CFunctionsScopePtr &c, void *data); + + void native_Array(const CFunctionsScopePtr &c, void *data); + + void native_String(const CFunctionsScopePtr &c, void *data); + + void native_Number(const CFunctionsScopePtr &c, void *data); + + void native_Function(const CFunctionsScopePtr &c, void *data); + void native_Function_call(const CFunctionsScopePtr &c, void *data); + void native_Function_apply(const CFunctionsScopePtr &c, void *data); + + /// globale function + + void native_eval(const CFunctionsScopePtr &c, void *data); + void native_isNAN(const CFunctionsScopePtr &c, void *data); + void native_isFinite(const CFunctionsScopePtr &c, void *data); + void native_parseInt(const CFunctionsScopePtr &c, void *data); + void native_parseFloat(const CFunctionsScopePtr &c, void *data); + + + + void native_JSON_parse(const CFunctionsScopePtr &c, void *data); + + + int uniqueID; +public: + int getUniqueID() { return ++uniqueID; } + CScriptVar *first; + void ClearLostVars(const CScriptVarPtr &extra=CScriptVarPtr()); +}; + +#endif From d6da7ea7d15a9f69dcebd08ed05aedb00b334e22 Mon Sep 17 00:00:00 2001 From: armin Date: Sat, 28 Jan 2012 09:14:12 +0000 Subject: [PATCH 41/43] 42TinyJS: Milestone for changelog visit http://code.google.com/p/42tiny-js/source/list --- Script.cpp | 48 +- TinyJS.cpp | 3612 ++++++++++++++++++++--------------- TinyJS.h | 2949 +++++++++++++++------------- TinyJS_Functions.cpp | 90 +- TinyJS_Functions.h | 41 +- TinyJS_MathFunctions.cpp | 87 +- TinyJS_MathFunctions.h | 40 + TinyJS_StringFunctions.cpp | 96 +- TinyJS_StringFunctions.h | 38 +- lib-tiny-js.vcxproj | 1 - lib-tiny-js.vcxproj.filters | 3 - pool_allocator.cpp | 170 +- pool_allocator.h | 89 +- run_tests.cpp | 54 +- run_tests.vcxproj | 4 +- time_logger.h | 35 +- 16 files changed, 4311 insertions(+), 3046 deletions(-) diff --git a/Script.cpp b/Script.cpp index 2c53234..4a51e6f 100755 --- a/Script.cpp +++ b/Script.cpp @@ -7,22 +7,36 @@ * * Copyright (C) 2009 Pur3 Ltd * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + + * 42TinyJS + * + * A fork of TinyJS with the goal to makes a more JavaScript/ECMA compliant engine + * + * Authored / Changed By Armin Diedering + * + * Copyright (C) 2010 ardisoft * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + /* * This is a simple program showing how to use TinyJS */ @@ -46,7 +60,7 @@ const char *code = "function myfunc(x, y) { return x + y; } var a = myfunc(1,2); print(a);"; void js_print(const CFunctionsScopePtr &v, void *) { - printf("> %s\n", v->getParameter("text")->getString().c_str()); + printf("> %s\n", v->getArgument("text")->getString().c_str()); } void js_dump(const CFunctionsScopePtr &v, void *) { @@ -70,17 +84,17 @@ int main(int , char **) js->execute("var lets_quit = 0; function quit() { lets_quit = 1; }"); js->execute("print(\"Interactive mode... Type quit(); to exit, or print(...); to print something, or dump() to dump the symbol table!\");"); } catch (CScriptException *e) { - printf("ERROR: %s\n", e->text.c_str()); + printf("%s\n", e->toString().c_str()); delete e; } - + int lineNumber = 0; while (js->evaluate("lets_quit") == "0") { char buffer[2048]; fgets ( buffer, sizeof(buffer), stdin ); try { - js->execute(buffer); + js->execute(buffer, "console.input", lineNumber++); } catch (CScriptException *e) { - printf("ERROR: %s\n", e->text.c_str()); + printf("%s\n", e->toString().c_str()); delete e; } } diff --git a/TinyJS.cpp b/TinyJS.cpp index 9cc2207..7aa69e6 100755 --- a/TinyJS.cpp +++ b/TinyJS.cpp @@ -7,103 +7,33 @@ * * Copyright (C) 2009 Pur3 Ltd * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + + * 42TinyJS * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. + * A fork of TinyJS with the goal to makes a more JavaScript/ECMA compliant engine * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -/* Version 0.1 : (gw) First published on Google Code - Version 0.11 : Making sure the 'root' variable never changes - 'symbol_base' added for the current base of the symbol table - Version 0.12 : Added findChildOrCreate, changed string passing to use references - Fixed broken string encoding in getJSString() - Removed getInitCode and added getJSON instead - Added nil - Added rough JSON parsing - Improved example app - Version 0.13 : Added tokenEnd/tokenLastEnd to lexer to avoid parsing whitespace - Ability to define functions without names - Can now do "var mine = function(a,b) { ... };" - Slightly better 'trace' function - Added findChildOrCreateByPath function - Added simple test suite - Added skipping of blocks when not executing - Version 0.14 : Added parsing of more number types - Added parsing of string defined with ' - Changed nil to null as per spec, added 'undefined' - Now set variables with the correct scope, and treat unknown - as 'undefined' rather than failing - Added proper (I hope) handling of null and undefined - Added === check - Version 0.15 : Fix for possible memory leaks - Version 0.16 : Removal of un-needed findRecursive calls - symbol_base removed and replaced with 'scopes' stack - Added reference counting a proper tree structure - (Allowing pass by reference) - Allowed JSON output to output IDs, not strings - Added get/set for array indices - Changed Callbacks to include user data pointer - Added some support for objects - Added more Java-esque builtin functions - Version 0.17 : Now we don't deepCopy the parent object of the class - Added JSON.stringify and eval() - Nicer JSON indenting - Fixed function output in JSON - Added evaluateComplex - Fixed some reentrancy issues with evaluate/execute - Version 0.18 : Fixed some issues with code being executed when it shouldn't - Version 0.19 : Added array.length - Changed '__parent' to 'prototype' to bring it more in line with javascript - Version 0.20 : Added '%' operator - Version 0.21 : Added array type - String.length() no more - now String.length - Added extra constructors to reduce confusion - Fixed checks against undefined - - - Forked to 42tiny-js by Armin diedering - R3 Added boolean datatype (in string operators its shown as 'true'/'false' but in math as '1'/'0' - Added '~' operator - Added bit-shift operators '<<' '>>' - Added assignment operators '<<=' '>>=' '|=' '&=' '^=' - Added comma operator like this 'var i=1,j,k=9;' or 'for(i=0,j=12; i<10; i++, j++)' works - Added Conditional operator ( ? : ) - Added automatic cast from doubles to integer e.G. by logic and binary operators - Added pre-increment/-decrement like '++i'/'--i' operator - Fixed post-increment/decrement now returns the previous value - Fixed throws an Error when invalid using of post/pre-increment/decrement (like this 5++ works no more) - Fixed memoryleak (unref arrayClass at deconstructor of JS CTinyJS) - Fixed unary operator handling (like this '-~-5' works now) - Fixed operator priority order -> ',' - -> '=' '+=' '-=' '<<=' '>>=' '&=' '^=' '|=' - -> '? :' -> '||' -> '&&' -> '|' -> '^' -> '&' - -> ['==' '===' '!=' '!=='] - -> [ '<' '<=' '=>' '>'] - -> ['<<' '>>'] -> [ '*' '/' '%'] - -> ['!' '~' '-' '++' '--'] - Added do-while-loop ( do .... while(..); ) - Added break and continue statements for loops - R4 Changed "owned"-member of CScriptVarLink from bool to a pointer of CScriptVarowned - Added some Visual Studio Preprocessor stuff - Added now allowed stuff like this (function (v) {print(v);})(12); - Remove unneeded and wrong deepCopy by assignment operator '=' - - - NOTE: This doesn't support constructors for objects - Recursive loops of data such as a.foo = a; fail to be garbage collected - 'length' cannot be set - There is no ternary operator implemented yet + * Authored / Changed By Armin Diedering + * + * Copyright (C) 2010 ardisoft + * + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ #ifdef _DEBUG @@ -111,12 +41,13 @@ # define DEBUG_MEMORY 1 # endif #endif -#include -#include +#include +#include #include #include #include #include +#include #include // for auto_ptr #include @@ -125,12 +56,12 @@ #ifndef ASSERT # define ASSERT(X) assert(X) #endif - +#if 0 inline CScriptVarSmartLink &CScriptVarSmartLink::operator = (CScriptVarPtr Var) { - if(link && !link->IsOwned()) delete link; + if(link && !link->isOwned()) delete link; link = new CScriptVarLink(Var); /* - if (!link || link->IsOwned()) + if (!link || link->isOwned()) link = new CScriptVarLink(Var); else { link->replaceWith(Var); @@ -140,7 +71,7 @@ inline CScriptVarSmartLink &CScriptVarSmartLink::operator = (CScriptVarPtr Var) return *this; } inline CScriptVarSmartLink &CScriptVarSmartLink::operator = (const CScriptVarSmartLink &Link) { - if(link && !link->IsOwned()) delete link; + if(link && !link->isOwned()) delete link; link = Link.link; ((CScriptVarSmartLink &)Link).link = 0; // explicit cast to a non const ref return *this; @@ -148,7 +79,7 @@ inline CScriptVarSmartLink &CScriptVarSmartLink::operator = (const CScriptVarSma // this operator corresponds "CLEAN(link); link = Link;" inline CScriptVarSmartLink &CScriptVarSmartLink::operator = (CScriptVarLink *Link) { - if(link && !link->IsOwned()) delete link; + if(link && !link->isOwned()) delete link; link = Link; return *this; } @@ -158,6 +89,8 @@ inline CScriptVarSmartLink &CScriptVarSmartLink::operator <<(CScriptVarSmartLink link->replaceWith(Link.link); return *this; } +#endif + using namespace std; #ifdef __GNUC__ @@ -210,7 +143,7 @@ void show_allocated() { allocatedVars[i]->trace(" "); } for (size_t i=0;iname.c_str(), (*allocatedLinks[i])->getRefs()); + printf("ALLOCATED LINK %s, allocated[%d] to \n", allocatedLinks[i]->getName().c_str(), (*allocatedLinks[i])->getRefs()); (*allocatedLinks[i])->trace(" "); } allocatedVars.clear(); @@ -316,14 +249,23 @@ bool isAlphaNum(const string &str) { return true; } -// ----------------------------------------------------------------------------------- CSCRIPTEXCEPTION -/* -CScriptException::CScriptException(const string &exceptionText, int Pos) { - text = exceptionText; - pos = Pos; +////////////////////////////////////////////////////////////////////////// +/// CScriptException +////////////////////////////////////////////////////////////////////////// + +string CScriptException::toString() { + ostringstream msg; + msg << ERROR_NAME[errorType] << ": " << message; + if(lineNumber >= 0) msg << " at Line:" << lineNumber; + if(column >=0) msg << " Column:" << column; + if(fileName.length()) msg << " in " << fileName; + return msg.str(); } -*/ -// ----------------------------------------------------------------------------------- CSCRIPTLEX + +////////////////////////////////////////////////////////////////////////// +/// CSCRIPTLEX +////////////////////////////////////////////////////////////////////////// + CScriptLex::CScriptLex(const char *Code, const string &File, int Line, int Column) : data(Code) { currentFile = File; @@ -339,26 +281,30 @@ void CScriptLex::reset(const char *toPos, int Line, const char *LineStart) { tkStr = ""; currentLine = Line; currentLineStart = LineStart; + lineBreakBeforeToken = false; currCh = nextCh = 0; getNextCh(); // currCh getNextCh(); // nextCh getNextToken(); } -void CScriptLex::check(int expected_tk) { +void CScriptLex::check(int expected_tk, int alternate_tk/*=-1*/) { if (expected_tk==';' && tk==LEX_EOF) return; // ignore last missing ';' - if (tk!=expected_tk) { + if (tk!=expected_tk && tk!=alternate_tk) { ostringstream errorString; if(expected_tk == LEX_EOF) errorString << "Got unexpected " << getTokenStr(tk); else - errorString << "Got " << getTokenStr(tk) << " expected " << getTokenStr(expected_tk); - throw new CScriptException(errorString.str(), currentFile, currentLine, currentColumn()); + errorString << "Got '" << getTokenStr(tk) << "' expected '" << getTokenStr(expected_tk) << "'"; + if(alternate_tk!=-1) errorString << " or '" << getTokenStr(alternate_tk) << "'"; + throw new CScriptException(SyntaxError, errorString.str(), currentFile, currentLine, currentColumn()); } } -void CScriptLex::match(int expected_tk) { - check(expected_tk); +void CScriptLex::match(int expected_tk1, int alternate_tk/*=-1*/) { + check(expected_tk1, alternate_tk); + int line = currentLine; getNextToken(); + lineBreakBeforeToken = line != currentLine; } string CScriptLex::getTokenStr(int token) { @@ -438,7 +384,9 @@ string CScriptLex::getTokenStr(int token) { case LEX_R_DEFAULT : return "default"; case LEX_T_SKIP : return "LEX_SKIP"; + case LEX_T_DUMMY_LABEL: case LEX_T_LABEL: return "LABEL"; + case LEX_T_LOOP_LABEL: return "LEX_LOOP_LABEL"; } ostringstream msg; @@ -591,7 +539,7 @@ void CScriptLex::getNextToken() { getNextCh(); } if(currCh != endCh) - throw new CScriptException("unterminated string literal", currentFile, currentLine, currentColumn()); + throw new CScriptException(SyntaxError, "unterminated string literal", currentFile, currentLine, currentColumn()); getNextCh(); tk = LEX_STR; } else { @@ -703,7 +651,7 @@ void CScriptLex::getNextToken() { getNextCh(); } while (currCh && currCh=='g' && currCh=='i' && currCh=='m' && currCh=='y'); } else - throw new CScriptException("unterminated regular expression literal", currentFile, currentLine, currentColumn()); + throw new CScriptException(SyntaxError, "unterminated regular expression literal", currentFile, currentLine, currentColumn()); } else if(currCh=='=') { tk = LEX_SLASHEQUAL; getNextCh(); @@ -717,7 +665,7 @@ void CScriptLex::getNextToken() { } // ----------------------------------------------------------------------------------- CSCRIPTTOKEN -CScriptToken::CScriptToken(CScriptLex *l, int Match) : line(l->currentLine), column(l->currentColumn()), token(l->tk), intData(0) +CScriptToken::CScriptToken(CScriptLex *l, int Match, int Alternate) : line(l->currentLine), column(l->currentColumn()), token(l->tk), intData(0) { if(token == LEX_INT) intData = strtol(l->tkStr.c_str(),0,0); @@ -731,7 +679,7 @@ CScriptToken::CScriptToken(CScriptLex *l, int Match) : line(l->currentLine), col fncData->ref(); } if(Match>=0) - l->match(Match); + l->match(Match, Alternate); else l->match(l->tk); } @@ -789,13 +737,13 @@ string CScriptToken::getParsableString(string &indentString, int &newln, const s if(token == LEX_R_FUNCTION) OutString.append(Fnc().name); OutString.append("("); - if(Fnc().parameter.size()) { - OutString.append(Fnc().parameter.front()); - for(vector::iterator it=Fnc().parameter.begin()+1; it!=Fnc().parameter.end(); ++it) + if(Fnc().arguments.size()) { + OutString.append(Fnc().arguments.front()); + for(STRING_VECTOR_it it=Fnc().arguments.begin()+1; it!=Fnc().arguments.end(); ++it) OutString.append(", ").append(*it); } OutString.append(") "); - for(TOKEN_VECT::iterator it=Fnc().body.begin(); it != Fnc().body.end(); ++it) { + for(TOKEN_VECT_it it=Fnc().body.begin(); it != Fnc().body.end(); ++it) { OutString.append(it->getParsableString(indentString, newln, indent)); } } else if(token == '{') { @@ -831,13 +779,13 @@ void CScriptToken::print(string &indent ) printf("%sfunction %s(", indent.c_str(), Fnc().name.c_str()); else printf("%sfunction (", indent.c_str()); - if(Fnc().parameter.size()) { - printf("%s", Fnc().parameter.front().c_str()); - for(vector::iterator it=Fnc().parameter.begin()+1; it!=Fnc().parameter.end(); ++it) + if(Fnc().arguments.size()) { + printf("%s", Fnc().arguments.front().c_str()); + for(STRING_VECTOR_it it=Fnc().arguments.begin()+1; it!=Fnc().arguments.end(); ++it) printf(",%s", it->c_str()); } printf(")"); - for(TOKEN_VECT::iterator it=Fnc().body.begin(); it != Fnc().body.end(); ++it) + for(TOKEN_VECT_it it=Fnc().body.begin(); it != Fnc().body.end(); ++it) it->print (indent); } else if(token == '{') { printf("%s{\n", indent.c_str()); @@ -877,27 +825,21 @@ void CScriptTokenizer::tokenizeCode(CScriptLex &Lexer) { tokens.clear(); tokenScopeStack.clear(); vector blockStart(1, tokens.size()), marks; - bool statement = true; + STRING_VECTOR_t labels, loopLabels; if(l->tk == '�') { // special-Token at Start means the code begins not at Statement-Level l->match('�'); - statement = false; - } - do { - tokenizeToken(tokens, statement, blockStart, marks); + tokenizeLiteral(tokens, blockStart, marks, labels, loopLabels, 0); + } else do { + tokenizeStatement(tokens, blockStart, marks, labels, loopLabels, 0); } while (l->tk!=LEX_EOF); - pushToken(tokens); // add LEX_EOF-Token + pushToken(tokens, LEX_EOF); // add LEX_EOF-Token TOKEN_VECT(tokens).swap(tokens);// tokens.shrink_to_fit(); pushTokenScope(tokens); currentFile = l->currentFile; tk = getToken().token; - } catch (CScriptException *e) { - ostringstream msg; - msg << "Error " << e->text; - if(e->line >= 0) msg << " at Line:" << e->line+1 << " Column:" << e->column+1; - if(e->file.length()) msg << " in " << e->file; - delete e; + } catch (...) { l=0; - throw new CScriptException(msg.str(),""); + throw; } } @@ -915,20 +857,22 @@ void CScriptTokenizer::getNextToken() { -void CScriptTokenizer::match(int ExpectedToken) { - if(check(ExpectedToken)) +void CScriptTokenizer::match(int ExpectedToken, int AlternateToken/*=-1*/) { + if(check(ExpectedToken, AlternateToken)) getNextToken(); } -bool CScriptTokenizer::check(int ExpectedToken) { +bool CScriptTokenizer::check(int ExpectedToken, int AlternateToken/*=-1*/) { int currentToken = getToken().token; if (ExpectedToken==';' && (currentToken==LEX_EOF || currentToken=='}')) return false; // ignore last missing ';' - if (currentToken!=ExpectedToken) { + if (currentToken!=ExpectedToken && currentToken!=AlternateToken) { ostringstream errorString; if(ExpectedToken == LEX_EOF) errorString << "Got unexpected " << CScriptLex::getTokenStr(currentToken); - else - errorString << "Got " << CScriptLex::getTokenStr(currentToken) << " expected " << CScriptLex::getTokenStr(ExpectedToken); - throw new CScriptException(errorString.str(), currentFile, currentLine(), currentColumn()); + else { + errorString << "Got '" << CScriptLex::getTokenStr(currentToken) << "' expected '" << CScriptLex::getTokenStr(ExpectedToken) << "'"; + if(AlternateToken!=-1) errorString << " or '" << CScriptLex::getTokenStr(AlternateToken) << "'"; + } + throw new CScriptException(SyntaxError, errorString.str(), currentFile, currentLine(), currentColumn()); } return true; } @@ -946,187 +890,220 @@ void CScriptTokenizer::skip(int Tokens) { tokenScopeStack.back().pos+=Tokens; tk = getToken().token; } -void CScriptTokenizer::tokenizeTry(TOKEN_VECT &Tokens, bool &Statement, vector &BlockStart, vector &Marks) { - if(!Statement) throwTokenNotExpected(); - int tk = l->tk; - Marks.push_back(pushToken(Tokens)); // push Token & push tokenBeginIdx - Statement = true; - l->check('{'); -// BlockStart.push_back(Tokens.size()); // set a blockStart - tokenizeBlock(Tokens, Statement, BlockStart, Marks); -// BlockStart.pop_back(); - - int tokenBeginIdx = Marks.back(); - Marks.pop_back(); // clean-up Marks - Tokens[tokenBeginIdx].Int() = Tokens.size()-tokenBeginIdx; - - if(l->tk == LEX_R_CATCH && tk == LEX_R_TRY) - tokenizeTry(Tokens, Statement, BlockStart, Marks); - else if(l->tk == LEX_R_FINALLY && (tk == LEX_R_TRY || tk == LEX_R_CATCH)) - tokenizeTry(Tokens, Statement, BlockStart, Marks); -} + static inline void setTokenSkip(TOKEN_VECT &Tokens, vector &Marks) { int tokenBeginIdx = Marks.back(); Marks.pop_back(); Tokens[tokenBeginIdx].Int() = Tokens.size()-tokenBeginIdx; } -void CScriptTokenizer::tokenizeSwitch(TOKEN_VECT &Tokens, bool &Statement, vector &BlockStart, vector &Marks) { - if(!Statement) throwTokenNotExpected(); +static void fix_BlockStarts_Marks(vector &BlockStart, vector &Marks, int start, int diff) { + for(vector::iterator it = BlockStart.begin(); it != BlockStart.end(); ++it) + if(*it >= start) *it += diff; + for(vector::iterator it = Marks.begin(); it != Marks.end(); ++it) + if(*it >= start) *it += diff; +} + +enum { + TOKENIZE_FLAGS_canLabel = 1<<0, + TOKENIZE_FLAGS_canBreak = 1<<1, + TOKENIZE_FLAGS_canContinue = 1<<2, + TOKENIZE_FLAGS_canReturn = 1<<3, + TOKENIZE_FLAGS_asStatement = 1<<4, + TOKENIZE_FLAGS_forFor = 1<<5, + TOKENIZE_FLAGS_isAccessor = 1<<6, + TOKENIZE_FLAGS_callForNew = 1<<7, +}; +void CScriptTokenizer::tokenizeCatch(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { + Marks.push_back(pushToken(Tokens, LEX_R_CATCH)); // push Token & push tokenBeginIdx + pushToken(Tokens, '('); + pushToken(Tokens, LEX_ID); + if(l->tk == LEX_R_IF) { + pushToken(Tokens); + tokenizeExpression(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + } + pushToken(Tokens, ')'); + tokenizeBlock(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + setTokenSkip(Tokens, Marks); +} +void CScriptTokenizer::tokenizeTry(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { + bool isTry = l->tk == LEX_R_TRY; Marks.push_back(pushToken(Tokens)); // push Token & push tokenBeginIdx - Statement = false; - l->check('('); - tokenizeBlock(Tokens, Statement, BlockStart, Marks); + // inject LEX_T_LOOP_LABEL + if(isTry && Tokens.size()>=3) { + int label_count = 0; + for(TOKEN_VECT::reverse_iterator it = Tokens.rbegin()+1; it!=Tokens.rend(); ++it) { + if(it->token == ':' && (++it)->token == LEX_T_LABEL) { + ++label_count; + it->token = LEX_T_DUMMY_LABEL; + } else break; + } + for(int i=0; itk != LEX_R_FINALLY && isTry) { + l->check(LEX_R_CATCH, LEX_R_FINALLY); + while(l->tk == LEX_R_CATCH && isTry) + tokenizeCatch(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + } + if(l->tk == LEX_R_FINALLY && isTry) + tokenizeTry(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); +} +void CScriptTokenizer::tokenizeSwitch(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { + + Marks.push_back(pushToken(Tokens)); // push Token & push tokenBeginIdx + pushToken(Tokens, '('); + tokenizeExpression(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + pushToken(Tokens, ')'); - Statement = true; Marks.push_back(pushToken(Tokens, '{')); // push Token & push blockBeginIdx BlockStart.push_back(Tokens.size()); // push Block-Start (one Token after '{') + Flags |= TOKENIZE_FLAGS_canBreak; for(bool hasDefault=false;;) { if( l->tk == LEX_R_CASE || l->tk == LEX_R_DEFAULT) { if(l->tk == LEX_R_CASE) { Marks.push_back(pushToken(Tokens)); // push Token & push caseBeginIdx - Statement = false; - while(l->tk != ':' && l->tk != LEX_EOF ) - tokenizeToken(Tokens, Statement, BlockStart, Marks); + tokenizeExpression(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); setTokenSkip(Tokens, Marks); } else { // default - if(hasDefault) throw new CScriptException("more than one switch default", l->currentFile, l->currentLine, l->currentColumn()); + if(hasDefault) throw new CScriptException(SyntaxError, "more than one switch default", l->currentFile, l->currentLine, l->currentColumn()); hasDefault = true; pushToken(Tokens); } - + Marks.push_back(pushToken(Tokens, ':')); - Statement = true; while(l->tk != '}' && l->tk != LEX_R_CASE && l->tk != LEX_R_DEFAULT && l->tk != LEX_EOF ) - tokenizeStatement(Tokens, Statement, BlockStart, Marks); + tokenizeStatement(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); setTokenSkip(Tokens, Marks); } else if(l->tk == '}') { break; } else - throw new CScriptException("invalid switch statement", l->currentFile, l->currentLine, l->currentColumn()); + throw new CScriptException(SyntaxError, "invalid switch statement", l->currentFile, l->currentLine, l->currentColumn()); } pushToken(Tokens, '}'); BlockStart.pop_back(); - Statement = true; - - int tokenBeginIdx = Marks.back(); // get block begin - Marks.pop_back(); // clean-up Marks - Tokens[tokenBeginIdx].Int() = Tokens.size()-tokenBeginIdx; - tokenBeginIdx = Marks.back(); // get switch gegin - Marks.pop_back(); // clean-up Marks - Tokens[tokenBeginIdx].Int() = Tokens.size()-tokenBeginIdx; + setTokenSkip(Tokens, Marks); + setTokenSkip(Tokens, Marks); } -void CScriptTokenizer::tokenizeWith(TOKEN_VECT &Tokens, bool &Statement, vector &BlockStart, vector &Marks) { - if(!Statement) throwTokenNotExpected(); +void CScriptTokenizer::tokenizeWith(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { Marks.push_back(pushToken(Tokens)); // push Token & push tokenBeginIdx - Statement = false; - l->check('('); - tokenizeBlock(Tokens, Statement, BlockStart, Marks); - Statement = true; + pushToken(Tokens, '('); + tokenizeExpression(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + pushToken(Tokens, ')'); BlockStart.push_back(Tokens.size()); // set a blockStart - tokenizeStatement(Tokens, Statement, BlockStart, Marks); + tokenizeStatement(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); BlockStart.pop_back(); - int tokenBeginIdx = Marks.back(); - Marks.pop_back(); // clean-up Marks - Tokens[tokenBeginIdx].Int() = Tokens.size()-tokenBeginIdx; + setTokenSkip(Tokens, Marks); } -void CScriptTokenizer::tokenizeWhile(TOKEN_VECT &Tokens, bool &Statement, vector &BlockStart, vector &Marks) { - if(!Statement) throwTokenNotExpected(); +static inline int PushLoopLabels(TOKEN_VECT &Tokens, STRING_VECTOR_t *LoopLabels=0) { + int label_count = 0; + if(Tokens.size()>=3) { + for(TOKEN_VECT::reverse_iterator it = Tokens.rbegin()+1; it!=Tokens.rend(); ++it) { + if(it->token == ':' && (++it)->token == LEX_T_LABEL) { + ++label_count; + if(LoopLabels) LoopLabels->push_back(it->String()); + it->token = LEX_T_DUMMY_LABEL; + } else break; + } + for(int i=0; irbegin()+i))); + } + return label_count; +} +static inline void PopLoopLabels(int label_count, STRING_VECTOR_t &LoopLabels) { + while(label_count--) + LoopLabels.pop_back(); +} +void CScriptTokenizer::tokenizeWhile(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { Marks.push_back(pushToken(Tokens)); // push Token & push tokenBeginIdx - Statement = false; - l->check('('); - tokenizeBlock(Tokens, Statement, BlockStart, Marks); + + // inject & push LEX_T_LOOP_LABEL + int label_count = PushLoopLabels(Tokens, &LoopLabels); + + pushToken(Tokens, '('); + tokenizeExpression(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + pushToken(Tokens, ')'); Marks.push_back(Tokens.size()); // push skiperBeginIdx Tokens.push_back(CScriptToken(LEX_T_SKIP)); // skip - Statement = true; - BlockStart.push_back(Tokens.size()); // set a blockStart - tokenizeStatement(Tokens, Statement, BlockStart, Marks); + tokenizeStatement(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags | TOKENIZE_FLAGS_canBreak | TOKENIZE_FLAGS_canContinue); BlockStart.pop_back(); - int tokenBeginIdx = Marks.back(); // set skipper - Marks.pop_back(); // clean-up Marks - Tokens[tokenBeginIdx].Int() = Tokens.size()-tokenBeginIdx; + setTokenSkip(Tokens, Marks); - tokenBeginIdx = Marks.back(); - Marks.pop_back(); // clean-up Marks - Tokens[tokenBeginIdx].Int() = Tokens.size()-tokenBeginIdx; + // pop LEX_T_LOOP_LABEL + PopLoopLabels(label_count, LoopLabels); + + setTokenSkip(Tokens, Marks); } -void CScriptTokenizer::tokenizeDo(TOKEN_VECT &Tokens, bool &Statement, vector &BlockStart, vector &Marks) { - if(!Statement) throwTokenNotExpected(); +void CScriptTokenizer::tokenizeDo(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { Marks.push_back(pushToken(Tokens)); // push Token & push tokenBeginIdx + // inject & push LEX_T_LOOP_LABEL + int label_count = PushLoopLabels(Tokens, &LoopLabels); + BlockStart.push_back(Tokens.size()); // set a blockStart - tokenizeStatement(Tokens, Statement, BlockStart, Marks); + tokenizeStatement(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags | TOKENIZE_FLAGS_canBreak | TOKENIZE_FLAGS_canContinue); BlockStart.pop_back(); - pushToken(Tokens, LEX_R_WHILE); - Statement = false; - l->check('('); - tokenizeBlock(Tokens, Statement, BlockStart, Marks); + Marks.push_back(pushToken(Tokens, LEX_R_WHILE)); // push Token & push tokenBeginIdx + pushToken(Tokens, '('); + tokenizeExpression(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + pushToken(Tokens, ')'); pushToken(Tokens, ';'); - Statement = true; + setTokenSkip(Tokens, Marks); - int tokenBeginIdx = Marks.back(); - Marks.pop_back(); // clean-up Marks - Tokens[tokenBeginIdx].Int() = Tokens.size()-tokenBeginIdx; + // pop LEX_T_LOOP_LABEL + PopLoopLabels(label_count, LoopLabels); + + setTokenSkip(Tokens, Marks); } -void CScriptTokenizer::tokenizeIf(TOKEN_VECT &Tokens, bool &Statement, vector &BlockStart, vector &Marks) { - if(!Statement) throwTokenNotExpected(); +void CScriptTokenizer::tokenizeIf(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { Marks.push_back(pushToken(Tokens)); // push Token & push tokenBeginIdx - Statement = false; - l->check('('); - tokenizeBlock(Tokens, Statement, BlockStart, Marks); + pushToken(Tokens, '('); + tokenizeExpression(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + pushToken(Tokens, ')'); Marks.push_back(Tokens.size()); // push skiperBeginIdx Tokens.push_back(CScriptToken(LEX_T_SKIP)); // skip - Statement = true; BlockStart.push_back(Tokens.size()); // set a blockStart - tokenizeStatement(Tokens, Statement, BlockStart, Marks); + tokenizeStatement(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); BlockStart.pop_back(); - int tokenBeginIdx = Marks.back(); // set skipper - Marks.pop_back(); // clean-up Marks - Tokens[tokenBeginIdx].Int() = Tokens.size()-tokenBeginIdx; + setTokenSkip(Tokens, Marks); if(l->tk == LEX_R_ELSE) { Marks.push_back(pushToken(Tokens)); // push Token & push tokenBeginIdx - + BlockStart.push_back(Tokens.size()); // set a blockStart - tokenizeStatement(Tokens, Statement, BlockStart, Marks); + tokenizeStatement(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); BlockStart.pop_back(); - tokenBeginIdx = Marks.back(); - Marks.pop_back(); // clean-up Marks - Tokens[tokenBeginIdx].Int() = Tokens.size()-tokenBeginIdx; + setTokenSkip(Tokens, Marks); } - tokenBeginIdx = Marks.back(); - Marks.pop_back(); // clean-up Marks - Tokens[tokenBeginIdx].Int() = Tokens.size()-tokenBeginIdx; + setTokenSkip(Tokens, Marks); } -static void fix_BlockStarts_Marks(vector &BlockStart, vector &Marks, int start, int diff) { - for(vector::iterator it = BlockStart.begin(); it != BlockStart.end(); ++it) - if(*it >= start) *it += diff; - for(vector::iterator it = Marks.begin(); it != Marks.end(); ++it) - if(*it >= start) *it += diff; -} -void CScriptTokenizer::tokenizeFor(TOKEN_VECT &Tokens, bool &Statement, vector &BlockStart, vector &Marks) { - if(!Statement) throwTokenNotExpected(); +void CScriptTokenizer::tokenizeFor(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { const char *prev_pos=l->tokenStart; const char *prev_line_start=l->currentLineStart; @@ -1138,33 +1115,26 @@ void CScriptTokenizer::tokenizeFor(TOKEN_VECT &Tokens, bool &Statement, vectormatch('('); - int parentheses = 0; - while(l->tk != ')' || parentheses) - { - if(l->tk == '(') parentheses++; - else if(l->tk == ')') parentheses--; - else if(l->tk == LEX_R_IN) { + if(l->tk == LEX_R_VAR) + l->match(LEX_R_VAR); + else if(l->tk == LEX_R_LET) + l->match(LEX_R_LET); + if(l->tk == LEX_ID) { + l->match(LEX_ID); + if(l->tk == LEX_R_IN) for_in = true; - break; - } else if(l->tk==LEX_EOF) { - l->match(')'); - } - l->match(l->tk); } } l->reset(prev_pos, prev_line, prev_line_start); + Marks.push_back(pushToken(Tokens)); // push Token & push tokenBeginIdx + if(for_in) Tokens[Tokens.size()-1].token = for_each_in?LEX_T_FOR_EACH_IN:LEX_T_FOR_IN; + if(for_each_in) l->match(LEX_ID); - l->match(LEX_R_FOR); - if(for_each_in) { - l->match(LEX_ID); - } - - Marks.push_back(Tokens.size()); // push tokenBeginIdx - Tokens.push_back(CScriptToken(for_in?(for_each_in?LEX_T_FOR_EACH_IN:LEX_T_FOR_IN):LEX_R_FOR)); - + // inject & push LEX_T_LOOP_LABEL + int label_count = PushLoopLabels(Tokens, &LoopLabels); + BlockStart.push_back(pushToken(Tokens, '(')+1); // set BlockStart - no forwarde for(let ... - Statement = true; if(for_in) { if(l->tk == LEX_R_VAR) { l->match(LEX_R_VAR); @@ -1179,33 +1149,42 @@ void CScriptTokenizer::tokenizeFor(TOKEN_VECT &Tokens, bool &Statement, vectortk != ')' && l->tk != LEX_EOF ) { - tokenizeToken(Tokens, Statement, BlockStart, Marks); + } else { + if(l->tk == LEX_R_VAR) + tokenizeVar(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags | TOKENIZE_FLAGS_forFor); + else if(l->tk == LEX_R_LET) + tokenizeLet(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags | TOKENIZE_FLAGS_forFor | TOKENIZE_FLAGS_asStatement); + else if(l->tk != ';') + tokenizeExpression(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + l->check(';'); // no automatic ;-injection + pushToken(Tokens, ';'); + if(l->tk != ';') tokenizeExpression(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + l->check(';'); // no automatic ;-injection + pushToken(Tokens, ';'); } + if(l->tk != ')') tokenizeExpression(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); BlockStart.pop_back(); // pop_back / "no forwarde for(let ... "-prevention pushToken(Tokens, ')'); - Statement = true; + BlockStart.push_back(Tokens.size()); // set a blockStart - tokenizeStatement(Tokens, Statement, BlockStart, Marks); + tokenizeStatement(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags | TOKENIZE_FLAGS_canBreak | TOKENIZE_FLAGS_canContinue); BlockStart.pop_back(); - int tokenBeginIdx = Marks.back(); - Marks.pop_back(); // clean-up Marks - Tokens[tokenBeginIdx].Int() = Tokens.size()-tokenBeginIdx; + // pop LEX_T_LOOP_LABEL + PopLoopLabels(label_count, LoopLabels); + + setTokenSkip(Tokens, Marks); } -void CScriptTokenizer::tokenizeFunction(TOKEN_VECT &Tokens, bool &Statement, vector &BlockStart, vector &Marks) { +void CScriptTokenizer::tokenizeFunction(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { bool forward = false; + bool Statement = (Flags & TOKENIZE_FLAGS_asStatement) != 0; + bool Accessor = (Flags & TOKENIZE_FLAGS_isAccessor) != 0; + Flags &= ~(TOKENIZE_FLAGS_asStatement | TOKENIZE_FLAGS_isAccessor); + int tk = l->tk; - if(tk == LEX_ID && (l->tkStr=="get"||l->tkStr=="set")) { - string tkStr = l->tkStr; - tk = tkStr=="get"?LEX_T_GET:LEX_T_SET; - l->match(l->tk); - if(l->tk != LEX_ID) { // is not a getter or setter - tokens.push_back(CScriptToken(LEX_ID, tkStr)); - return; - } + if(Accessor) { + tk = Tokens.back().String()=="get"?LEX_T_GET:LEX_T_SET; + Tokens.pop_back(); } else { l->match(LEX_R_FUNCTION); if(!Statement) tk = LEX_T_FUNCTION_OPERATOR; @@ -1224,21 +1203,19 @@ void CScriptTokenizer::tokenizeFunction(TOKEN_VECT &Tokens, bool &Statement, vec l->match('('); while(l->tk != ')') { - FncData.parameter.push_back(l->tkStr); + FncData.arguments.push_back(l->tkStr); l->match(LEX_ID); - if (l->tk==',') { - l->match(','); - } + if (l->tk!=')') l->match(',',')'); } l->match(')'); FncData.file = l->currentFile; FncData.line = l->currentLine; vector functionBlockStart, marks; + STRING_VECTOR_t labels, loopLabels; functionBlockStart.push_back(FncData.body.size()+1); l->check('{'); - bool FncStatement = true; // functions-block starts always in Statement-Level - tokenizeBlock(FncData.body, FncStatement, functionBlockStart, marks); + tokenizeBlock(FncData.body, functionBlockStart, marks, labels, loopLabels, TOKENIZE_FLAGS_canReturn); if(forward) { int tokenInsertIdx = BlockStart.back(); @@ -1249,28 +1226,25 @@ void CScriptTokenizer::tokenizeFunction(TOKEN_VECT &Tokens, bool &Statement, vec Tokens.push_back(FncToken); } -void CScriptTokenizer::tokenizeLet(TOKEN_VECT &Tokens, bool &Statement, vector &BlockStart, vector &Marks) { - - bool forward = TOKEN_VECT::size_type(BlockStart.back()) != Tokens.size(); - bool let_is_Statement = Statement, expression=false; +void CScriptTokenizer::tokenizeLet(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { + bool forFor = (Flags & TOKENIZE_FLAGS_forFor)!=0; + bool Statement = (Flags & TOKENIZE_FLAGS_asStatement)!=0; Flags &= ~(TOKENIZE_FLAGS_forFor | TOKENIZE_FLAGS_asStatement); + bool forward = !forFor && TOKEN_VECT::size_type(BlockStart.back()) != Tokens.size(); + bool expression=false; Marks.push_back(pushToken(Tokens)); // push Token & push BeginIdx - Statement = false; - if(l->tk == '(' || !let_is_Statement) { + if(!forFor && (l->tk == '(' || !Statement)) { expression = true; pushToken(Tokens, '('); } - vector vars; + STRING_VECTOR_t vars; for(;;) { vars.push_back(l->tkStr); pushToken(Tokens, LEX_ID); if(l->tk=='=') { pushToken(Tokens); - for(;;) { - if(l->tk == (expression?')':';') || l->tk == ',' || l->tk == LEX_EOF) break; - tokenizeToken(Tokens, Statement, BlockStart, Marks); - } + tokenizeAssignment(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); } if(l->tk==',') pushToken(Tokens); @@ -1279,27 +1253,21 @@ void CScriptTokenizer::tokenizeLet(TOKEN_VECT &Tokens, bool &Statement, vector::iterator it = vars.begin()+1; it != vars.end(); ++it) { + for(STRING_VECTOR_it it = vars.begin()+1; it != vars.end(); ++it) { Tokens.insert(Tokens.begin()+tokenInsertIdx++, CScriptToken(',')); Tokens.insert(Tokens.begin()+tokenInsertIdx++, CScriptToken(LEX_ID, *it)); } @@ -1311,45 +1279,39 @@ void CScriptTokenizer::tokenizeLet(TOKEN_VECT &Tokens, bool &Statement, vector &BlockStart, vector &Marks) { - if(!Statement) throwTokenNotExpected(); +void CScriptTokenizer::tokenizeVar(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { + + bool forFor = (Flags & TOKENIZE_FLAGS_forFor)!=0; Flags &= ~TOKENIZE_FLAGS_forFor; bool forward = TOKEN_VECT::size_type(BlockStart.front()) != Tokens.size(); // forwarde only if the var-Statement not the first Statement of the Scope Marks.push_back(pushToken(Tokens)); // push Token & push BeginIdx - Statement = false; - vector vars; + STRING_VECTOR_t vars; for(;;) { vars.push_back(l->tkStr); pushToken(Tokens, LEX_ID); if(l->tk=='=') { pushToken(Tokens); - for(;;) { - if(l->tk == ';' || l->tk == ',' || l->tk == LEX_EOF) break; - tokenizeToken(Tokens, Statement, BlockStart, Marks); - } + tokenizeAssignment(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); } if(l->tk==',') pushToken(Tokens); else break; } - pushToken(Tokens, ';'); - Statement = true; + if(!forFor) pushToken(Tokens, ';'); - int tokenBeginIdx = Marks.back(); - Marks.pop_back(); // clean-up Marks - Tokens[tokenBeginIdx].Int() = Tokens.size()-tokenBeginIdx; + setTokenSkip(Tokens, Marks); if(forward) // copy a var-deklaration at the begin of the scope { - int tokenInsertIdx = tokenBeginIdx = BlockStart.front(); + int tokenBeginIdx = BlockStart.front(), tokenInsertIdx = tokenBeginIdx; Tokens.insert(Tokens.begin()+tokenInsertIdx++, CScriptToken(LEX_R_VAR)); Tokens.insert(Tokens.begin()+tokenInsertIdx++, CScriptToken(LEX_ID, vars.front())); - for(vector::iterator it = vars.begin()+1; it != vars.end(); ++it) { + for(STRING_VECTOR_it it = vars.begin()+1; it != vars.end(); ++it) { Tokens.insert(Tokens.begin()+tokenInsertIdx++, CScriptToken(',')); Tokens.insert(Tokens.begin()+tokenInsertIdx++, CScriptToken(LEX_ID, *it)); } @@ -1360,124 +1322,266 @@ void CScriptTokenizer::tokenizeVar(TOKEN_VECT &Tokens, bool &Statement, vector &BlockStart, vector &Marks) { - int tkEnd = 0; - bool Statement_at_end = false; - switch(l->tk) - { - case'{': tkEnd = '}'; break; - case'(': tkEnd = ')'; break; - case'[': tkEnd = ']'; break; - } - Marks.push_back(pushToken(Tokens)); // push Token & push BeginIdx - - if(tkEnd == '}') // { ... } Block - { - if( (Statement_at_end = Statement) ) // Statment-Level after '}' is the same Level as at start ('{') - BlockStart.push_back(Tokens.size()); // push Block-Start (one Token after '{') - } else - Statement = false; - while(l->tk != tkEnd && l->tk != LEX_EOF) tokenizeToken(Tokens, Statement, BlockStart, Marks); - pushToken(Tokens, tkEnd); - - if(tkEnd == '}' && Statement_at_end) { // { ... } Block - BlockStart.pop_back(); // clean-up BlockStarts - } - - int tokenBeginIdx = Marks.back(); - Marks.pop_back(); // clean-up Marks - Tokens[tokenBeginIdx].Int() = Tokens.size()-tokenBeginIdx; - - Statement = Statement_at_end; -} -void CScriptTokenizer::tokenizeStatement(TOKEN_VECT &Tokens, bool &Statement, vector &BlockStart, vector &Marks) { - do { - tokenizeToken(Tokens, Statement, BlockStart, Marks); - } while(Statement == false && l->tk != LEX_EOF ); // tokenize one Statement - Statement = true; -} -void CScriptTokenizer::tokenizeToken(TOKEN_VECT &Tokens, bool &Statement, vector &BlockStart, vector &Marks) { - switch(l->tk) - { - case '(': - case '[': - case '{': tokenizeBlock(Tokens, Statement, BlockStart, Marks); break; - case LEX_R_VAR: tokenizeVar(Tokens, Statement, BlockStart, Marks); break; - case LEX_R_LET: tokenizeLet(Tokens, Statement, BlockStart, Marks); break; - case LEX_R_FUNCTION: tokenizeFunction(Tokens, Statement, BlockStart, Marks); break; - case LEX_R_FOR: tokenizeFor(Tokens, Statement, BlockStart, Marks); break; - case LEX_R_IF: tokenizeIf(Tokens, Statement, BlockStart, Marks); break; - case LEX_R_DO: tokenizeDo(Tokens, Statement, BlockStart, Marks); break; - case LEX_R_WHILE: tokenizeWhile(Tokens, Statement, BlockStart, Marks); break; - case LEX_R_WITH: tokenizeWith(Tokens, Statement, BlockStart, Marks); break; - case LEX_R_SWITCH: tokenizeSwitch(Tokens, Statement, BlockStart, Marks); break; - case LEX_R_TRY: tokenizeTry(Tokens, Statement, BlockStart, Marks); break; - case LEX_ID: { - string str = l->tkStr; - if(!Statement && (str=="get"||str=="set")) { - tokenizeFunction(Tokens, Statement, BlockStart, Marks); - break; - } +void CScriptTokenizer::tokenizeLiteral(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { + switch(l->tk) { + case LEX_ID: + { + string label = l->tkStr; pushToken(Tokens); - if(Statement && l->tk==':') { // label + if(l->tk==':' && Flags & TOKENIZE_FLAGS_canLabel) { + if(find(Labels.begin(), Labels.end(), label) != Labels.end()) + throw new CScriptException(SyntaxError, "dublicate label '"+label+"'", l->currentFile, l->currentLine, l->currentColumn()-label.size()); Tokens[Tokens.size()-1].token = LEX_T_LABEL; // change LEX_ID to LEX_T_LABEL + Labels.push_back(label); + } + } + break; + case LEX_INT: + case LEX_FLOAT: + case LEX_STR: + case LEX_R_TRUE: + case LEX_R_FALSE: + case LEX_R_NULL: + pushToken(Tokens); + break; + case '{': + Marks.push_back(pushToken(Tokens)); // push Token & push BeginIdx + while (l->tk != '}') { + if(l->tk == LEX_STR) { + string id = l->tkStr; pushToken(Tokens); - break; + if(l->tk==LEX_ID && (id=="get" || id=="set")) + tokenizeFunction(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + else { + pushToken(Tokens, ':'); + tokenizeAssignment(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + } + } else if(l->tk == LEX_ID) { + pushToken(Tokens); + pushToken(Tokens, ':'); + tokenizeAssignment(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); } + if (l->tk != '}') pushToken(Tokens, ',', '}'); + } + pushToken(Tokens); + setTokenSkip(Tokens, Marks); + break; + case '[': + Marks.push_back(pushToken(Tokens)); // push Token & push BeginIdx + while (l->tk != ']') { + tokenizeAssignment(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + if (l->tk != ']') pushToken(Tokens, ',', ']'); } - Statement = false; + pushToken(Tokens); + setTokenSkip(Tokens, Marks); + break; + case LEX_R_LET: // let as expression + tokenizeLet(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + break; + case LEX_R_FUNCTION: + tokenizeFunction(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + break; + case LEX_R_NEW: + Marks.push_back(pushToken(Tokens)); // push Token & push BeginIdx + tokenizeFunctionCall(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags | TOKENIZE_FLAGS_callForNew); + setTokenSkip(Tokens, Marks); + break; + case '(': + Marks.push_back(pushToken(Tokens)); // push Token & push BeginIdx + tokenizeExpression(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + pushToken(Tokens, ')'); + setTokenSkip(Tokens, Marks); break; default: - Statement = l->tk==';'; // after ';' allways Statement-Level + l->check(LEX_EOF); + } +} +void CScriptTokenizer::tokenizeMember(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { + while(l->tk == '.' || l->tk == '[') { + if(l->tk == '.') { + pushToken(Tokens); + pushToken(Tokens , LEX_ID); + } else { + pushToken(Tokens); + tokenizeExpression(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + pushToken(Tokens, ']'); + } + } +} +void CScriptTokenizer::tokenizeFunctionCall(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { + bool for_new = (Flags & TOKENIZE_FLAGS_callForNew)!=0; Flags &= ~TOKENIZE_FLAGS_callForNew; + tokenizeLiteral(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + tokenizeMember(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + while(l->tk == '(') { + Marks.push_back(pushToken(Tokens)); // push Token & push BeginIdx + while(l->tk!=')') { + tokenizeAssignment(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + if (l->tk!=')') pushToken(Tokens, ',', ')' ); + } pushToken(Tokens); + setTokenSkip(Tokens, Marks); + if(for_new) break; + tokenizeMember(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); } -#if 0 - if(l->tk == '{' || l->tk == '(' || l->tk == '[') { // block - tokenizeBlock(Tokens, Statement, BlockStart, Marks); - } else if(l->tk == LEX_R_VAR) { - tokenizeVar(Tokens, Statement, BlockStart, Marks); - } else if(l->tk == LEX_R_FUNCTION) { - tokenizeFunction(Tokens, Statement, BlockStart, Marks); - } else if(l->tk == LEX_R_FOR) { - tokenizeFor(Tokens, Statement, BlockStart, Marks); - } else if(l->tk == LEX_R_IF) { - tokenizeIf(Tokens, Statement, BlockStart, Marks); - } else if(l->tk == LEX_R_DO) { - tokenizeDo(Tokens, Statement, BlockStart, Marks); - } else if(l->tk == LEX_R_WHILE) { - tokenizeWhile(Tokens, Statement, BlockStart, Marks); - } else if(l->tk == LEX_R_WITH) { - tokenizeWith(Tokens, Statement, BlockStart, Marks); - } else if(l->tk == LEX_R_TRY) { - tokenizeTry(Tokens, Statement, BlockStart, Marks); - } else if(l->tk == LEX_ID && (l->tkStr=="get"||l->tkStr=="set")) { - string::const_iterator prev_pos=l->tokenStart; - string::const_iterator prev_line_start=l->currentLineStart; - int prev_line = l->currentLine; - string tkStr = l->tkStr; - l->match(l->tk); - bool is_set_get = l->tk == LEX_ID; // true if set/get LEX_ID - l->reset(prev_pos, prev_line, prev_line_start); - if(is_set_get) { - l->tk = tkStr=="get"?LEX_T_GET:LEX_T_SET; - tokenizeFunction(Tokens, Statement, BlockStart, Marks); +} + +void CScriptTokenizer::tokenizeSubExpression(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { + static int Left2Right_begin[] = { + /* Precedence 5 */ '*', '/', '%', + /* Precedence 6 */ '+', '-', + /* Precedence 7 */ LEX_LSHIFT, LEX_RSHIFT, LEX_RSHIFTU, + /* Precedence 8 */ LEX_EQUAL, LEX_NEQUAL, LEX_TYPEEQUAL, LEX_NTYPEEQUAL, + /* Precedence 9 */ '<', LEX_LEQUAL, '>', LEX_GEQUAL, LEX_R_IN, LEX_R_INSTANCEOF, + /* Precedence 10-12 */ '&', '^', '|', + /* Precedence 13-14 */ LEX_ANDAND, LEX_OROR, + }; + static int *Left2Right_end = &Left2Right_begin[sizeof(Left2Right_begin)/sizeof(Left2Right_begin[0])]; + static bool Left2Right_sorted = false; + if(!Left2Right_sorted) Left2Right_sorted = (sort(Left2Right_begin, Left2Right_end), true); + + for(;;) { + bool right2left_end = false; + while(!right2left_end) { + switch(l->tk) { + case '-': + case '+': + case '!': + case '~': + case LEX_R_TYPEOF: + case LEX_R_VOID: + case LEX_R_DELETE: + Flags &= ~TOKENIZE_FLAGS_canLabel; + pushToken(Tokens); // Precedence 3 + break; + case LEX_PLUSPLUS: // pre-increment + case LEX_MINUSMINUS: // pre-decrement + Flags &= ~TOKENIZE_FLAGS_canLabel; + pushToken(Tokens); // Precedence 4 + default: + right2left_end = true; + } + } + tokenizeFunctionCall(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + + if (!l->lineBreakBeforeToken && (l->tk==LEX_PLUSPLUS || l->tk==LEX_MINUSMINUS)) { // post-in-/de-crement + pushToken(Tokens); // Precedence 4 } + int *found = lower_bound(Left2Right_begin, Left2Right_end, l->tk); + if(found != Left2Right_end && *found == l->tk) + pushToken(Tokens); // Precedence 5-14 else - pushToken(Tokens); - } else { - Statement = l->tk==';'; // after ';' allways Statement-Level + break; + } +} + +void CScriptTokenizer::tokenizeCondition(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { + tokenizeSubExpression(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + if(l->tk == '?') { pushToken(Tokens); + tokenizeCondition(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + pushToken(Tokens, ':'); + tokenizeCondition(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + } +} +void CScriptTokenizer::tokenizeAssignment(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { + tokenizeCondition(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + if (l->tk=='=' || (l->tk>=LEX_ASSIGNMENTS_BEGIN && l->tk<=LEX_ASSIGNMENTS_END) ) { + pushToken(Tokens); + tokenizeAssignment(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + } +} +void CScriptTokenizer::tokenizeExpression(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { + tokenizeAssignment(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + while(l->tk == ',') { + pushToken(Tokens); + tokenizeAssignment(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + } +} +void CScriptTokenizer::tokenizeBlock(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { + Marks.push_back(pushToken(Tokens, '{')); // push Token & push BeginIdx + BlockStart.push_back(Tokens.size()); // push Block-Start (one Token after '{') + + while(l->tk != '}' && l->tk != LEX_EOF) + tokenizeStatement(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + pushToken(Tokens, '}'); + + BlockStart.pop_back(); // clean-up BlockStarts + + setTokenSkip(Tokens, Marks); +} + + +void CScriptTokenizer::tokenizeStatement(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { + switch(l->tk) + { + case '{': tokenizeBlock(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); break; + case ';': pushToken(Tokens); break; + case LEX_R_VAR: tokenizeVar(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); break; + case LEX_R_LET: tokenizeLet(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags | TOKENIZE_FLAGS_asStatement); break; + case LEX_R_WITH: tokenizeWith(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); break; + case LEX_R_IF: tokenizeIf(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); break; + case LEX_R_SWITCH: tokenizeSwitch(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); break; + case LEX_R_DO: tokenizeDo(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); break; + case LEX_R_WHILE: tokenizeWhile(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); break; + case LEX_R_FOR: tokenizeFor(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); break; + case LEX_R_FUNCTION: tokenizeFunction(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags | TOKENIZE_FLAGS_asStatement); break; + case LEX_R_TRY: tokenizeTry(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); break; + case LEX_R_RETURN: + if( (Flags & TOKENIZE_FLAGS_canReturn)==0) + throw new CScriptException(SyntaxError, "'return' statement, but not in a function.", l->currentFile, l->currentLine, l->currentColumn()); + case LEX_R_THROW: + Marks.push_back(pushToken(Tokens)); // push Token & push BeginIdx + if(l->tk != ';' && !l->lineBreakBeforeToken) { + tokenizeExpression(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + } + pushToken(Tokens, ';'); // push ';' + setTokenSkip(Tokens, Marks); + break; + case LEX_R_BREAK: + case LEX_R_CONTINUE: + { + bool isBreak = l->tk == LEX_R_BREAK; + Marks.push_back(pushToken(Tokens)); // push Token & push BeginIdx + if(l->tk != ';' && !l->lineBreakBeforeToken) { + l->check(LEX_ID); + STRING_VECTOR_t &L = isBreak ? Labels : LoopLabels; + if(find(L.begin(), L.end(), l->tkStr) == L.end()) + throw new CScriptException(SyntaxError, "label '"+l->tkStr+"' not found", l->currentFile, l->currentLine, l->currentColumn()); + pushToken(Tokens); // push 'Label' + } else if((Flags & (isBreak ? TOKENIZE_FLAGS_canBreak : TOKENIZE_FLAGS_canContinue) )==0) + throw new CScriptException(SyntaxError, + isBreak ? "'break' must be inside loop or switch" : "'continue' must be inside loop", + l->currentFile, l->currentLine, l->currentColumn()); + pushToken(Tokens, ';'); // push ';' + setTokenSkip(Tokens, Marks); + } + break; + case LEX_ID: + { + STRING_VECTOR_t::size_type label_count = Labels.size(); + tokenizeExpression(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags | TOKENIZE_FLAGS_canLabel); + if(label_count < Labels.size() && l->tk == ':') { + pushToken(Tokens); // push ':' + tokenizeStatement(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + Labels.pop_back(); + } else + pushToken(Tokens, ';'); + } + break; + default: tokenizeExpression(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); pushToken(Tokens, ';'); break; } -#endif } -int CScriptTokenizer::pushToken(TOKEN_VECT &Tokens, int Match) { - Tokens.push_back(CScriptToken(l, Match)); + +int CScriptTokenizer::pushToken(TOKEN_VECT &Tokens, int Match, int Alternate) { + if(Match == ';' && l->tk != ';' && (l->lineBreakBeforeToken || l->tk=='}' || l->tk==LEX_EOF)) + Tokens.push_back(CScriptToken(';')); // inject ';' + else + Tokens.push_back(CScriptToken(l, Match, Alternate)); return Tokens.size()-1; } void CScriptTokenizer::throwTokenNotExpected() { - throw new CScriptException("'"+CScriptLex::getTokenStr(l->tk)+"' was not expected", l->currentFile, l->currentLine, l->currentColumn()); + throw new CScriptException(SyntaxError, "'"+CScriptLex::getTokenStr(l->tk)+"' was not expected", l->currentFile, l->currentLine, l->currentColumn()); } // ----------------------------------------------------------------------------------- CSCRIPTVARLINK @@ -1487,7 +1591,7 @@ CScriptVarLink::CScriptVarLink(const CScriptVarPtr &Var, const string &Name /*=T #if DEBUG_MEMORY mark_allocated(this); #endif - var = Var->ref(); + var = Var; } CScriptVarLink::CScriptVarLink(const CScriptVarLink &link) @@ -1496,57 +1600,25 @@ CScriptVarLink::CScriptVarLink(const CScriptVarLink &link) #if DEBUG_MEMORY mark_allocated(this); #endif - var = link.var->ref(); + var = link.var; } CScriptVarLink::~CScriptVarLink() { #if DEBUG_MEMORY mark_deallocated(this); #endif - var->unref(); -} - -CScriptVarPtr CScriptVarLink::getValue() { - if(var->isAccessor()) { - CScriptVarSmartLink getter = var->findChild(TINYJS_ACCESSOR_GET_VAR); - if(getter) { - vector Params; - bool execute; - ASSERT(this->owner); - return (*getter)->getContext()->callFunction(getter, Params, owner, execute); - } - return var->newScriptVar(Undefined); - } else { - return var; - } } void CScriptVarLink::replaceWith(const CScriptVarPtr &newVar) { if(!newVar) ASSERT(0);//newVar = new CScriptVar(); - if(var->isAccessor()) { - CScriptVarSmartLink setter = var->findChild(TINYJS_ACCESSOR_SET_VAR); - if(setter) { - vector Params(1, newVar); - bool execute; - ASSERT(this->owner); - (*setter)->getContext()->callFunction(setter, Params, owner, execute); - } - } else if(var){ - CScriptVar *oldVar = var; - var = newVar->ref(); - oldVar->unref(); - } -} - -void CScriptVarLink::replaceWith(CScriptVarLink *newVar) { - if (newVar) - replaceWith(newVar->getValue()); - else - ASSERT(0);//replaceWith(new CScriptVar()); + var = newVar; } // ----------------------------------------------------------------------------------- CSCRIPTVAR -CScriptVar::CScriptVar(CTinyJS *Context, const CScriptVarPtr &Prototype) : context(Context) , temporaryID(0) { + +CScriptVar::CScriptVar(CTinyJS *Context, const CScriptVarPtr &Prototype) { + context = Context; + temporaryID = 0; if(context->first) { next = context->first; next->prev = this; @@ -1557,14 +1629,14 @@ CScriptVar::CScriptVar(CTinyJS *Context, const CScriptVarPtr &Prototype) : conte prev = 0; refs = 0; if(Prototype) - addChild(TINYJS___PROTO___VAR, Prototype, 0); - else if(context->objectPrototype) - addChild(TINYJS___PROTO___VAR, context->objectPrototype, 0); + addChild(TINYJS___PROTO___VAR, Prototype, SCRIPTVARLINK_WRITABLE); #if DEBUG_MEMORY mark_allocated(this); #endif } -CScriptVar::CScriptVar(const CScriptVar &Copy) : context(Copy.context), temporaryID(0) { +CScriptVar::CScriptVar(const CScriptVar &Copy) { + context = Copy.context; + temporaryID = 0; if(context->first) { next = context->first; next->prev = this; @@ -1576,7 +1648,7 @@ CScriptVar::CScriptVar(const CScriptVar &Copy) : context(Copy.context), temporar refs = 0; SCRIPTVAR_CHILDS_cit it; for(it = Copy.Childs.begin(); it!= Copy.Childs.end(); ++it) { - addChild((*it)->name, (*it), (*it)->flags); + addChild((*it)->getName(), (*it), (*it)->getFlags()); } #if DEBUG_MEMORY @@ -1597,21 +1669,24 @@ CScriptVar::~CScriptVar(void) { } /// Type + +bool CScriptVar::isObject() {return false;} +bool CScriptVar::isError() {return false;} +bool CScriptVar::isArray() {return false;} +bool CScriptVar::isAccessor() {return false;} +bool CScriptVar::isNull() {return false;} +bool CScriptVar::isUndefined() {return false;} +bool CScriptVar::isNaN() {return false;} +bool CScriptVar::isString() {return false;} bool CScriptVar::isInt() {return false;} bool CScriptVar::isBool() {return false;} +int CScriptVar::isInfinity() { return 0; } ///< +1==POSITIVE_INFINITY, -1==NEGATIVE_INFINITY, 0==is not an InfinityVar bool CScriptVar::isDouble() {return false;} -bool CScriptVar::isString() {return false;} bool CScriptVar::isNumber() {return false;} bool CScriptVar::isNumeric() {return false;} +bool CScriptVar::isPrimitive() {return true;} bool CScriptVar::isFunction() {return false;} -bool CScriptVar::isObject() {return false;} -bool CScriptVar::isArray() {return false;} bool CScriptVar::isNative() {return false;} -bool CScriptVar::isUndefined() {return false;} -bool CScriptVar::isNull() {return false;} -bool CScriptVar::isNaN() {return false;} -int CScriptVar::isInfinity() { return 0; } ///< +1==POSITIVE_INFINITY, -1==NEGATIVE_INFINITY, 0==is not an InfinityVar -bool CScriptVar::isAccessor() {return false;} /// Value @@ -1624,31 +1699,70 @@ string CScriptVar::getString() {return "";} string CScriptVar::getParsableString(const string &indentString, const string &indent) { return getString(); } -/* -string CScriptVar::getVarType() { - if(this->isBool()) - return "boolean"; - else if(this->isFunction()) - return "function"; - else if(this->isNumeric() || this->isInfinity() || this->isNaN()) - return "number"; - else if(this->isString()) - return "string"; - else if(this->isUndefined()) - return "undefined"; - return "object"; // Object / Array / null -} -*/ -CScriptVarPtr CScriptVar::getNumericVar() { - return newScriptVar(NaN); -} +CScriptVarPtr CScriptVar::getNumericVar() { + return constScriptVar(NaN); +} +CScriptVarPtr CScriptVar::getPrimitivVar() { + bool execute=true; + CScriptVarPtr var = getPrimitivVar(execute); +// if(!execute) TODO + return var; +} +CScriptVarPtr CScriptVar::getPrimitivVar(bool execute) { + if(execute) { + if(!isPrimitive()) { + CScriptVarPtr ret = valueOf(execute); + if(execute && !ret->isPrimitive()) { + ret = toString(execute); + if(execute && !ret->isPrimitive()) + do{}while(0); // TODO error can't convert in primitive type + } + return ret; + } + return this; + } + return constScriptVar(Undefined); +} + +CScriptVarPtr CScriptVar::valueOf(bool execute) { + if(execute) { + CScriptVarPtr FncValueOf = findChildWithPrototypeChain("valueOf"); + if(FncValueOf != context->objectPrototype_valueOf) { // custom valueOf in JavaScript + vector Params; + return context->callFunction(execute, FncValueOf, Params, this); + } else { + return _valueOf(execute); + } + } + return this; +} +CScriptVarPtr CScriptVar::_valueOf(bool execute) { + return this; +} + +CScriptVarPtr CScriptVar::toString(bool execute, int radix/*=0*/) { + if(execute) { + CScriptVarPtr FncToString = findChildWithPrototypeChain("toString"); + if(FncToString != context->objectPrototype_toString) { // custom valueOf in JavaScript + vector Params; + Params.push_back(newScriptVar(radix)); + return context->callFunction(execute, FncToString, Params, this); + } else { + return _toString(execute, radix); + } + } + return this; +} +CScriptVarPtr CScriptVar::_toString(bool execute, int radix/*=0*/) { + return this; +} ////// Childs /// find static bool compare_child_name(CScriptVarLink *Link, const string &Name) { - return Link->name < Name; + return Link->getName() < Name; } CScriptVarLink *CScriptVar::findChild(const string &childName) { @@ -1656,10 +1770,36 @@ CScriptVarLink *CScriptVar::findChild(const string &childName) { SCRIPTVAR_CHILDS_it it = lower_bound(Childs.begin(), Childs.end(), childName.c_str(), compare_child_name); - if(it != Childs.end() && (*it)->name == childName) + if(it != Childs.end() && (*it)->getName() == childName) return *it; return 0; } + +CScriptVarLink *CScriptVar::findChildInPrototypeChain(const string &childName) { + unsigned int uniqueID = context->getUniqueID(); + // Look for links to actual parent classes + CScriptVarPtr object = this; + CScriptVarLink *__proto__; + while( object->getTempraryID() != uniqueID && (__proto__ = object->findChild(TINYJS___PROTO___VAR)) ) { + CScriptVarLink *implementation = (*__proto__)->findChild(childName); + if (implementation) return implementation; + object->setTemporaryID(uniqueID); // prevents recursions + object = __proto__; + } + return 0; +} + +CScriptVarLink *CScriptVar::findChildWithPrototypeChain(const string &childName) { + unsigned int uniqueID = context->getUniqueID(); + CScriptVarPtr object = this; + while( object && object->getTempraryID() != uniqueID) { + CScriptVarLink *implementation = object->findChild(childName); + if (implementation) return implementation; + object->setTemporaryID(uniqueID); // prevents recursions + object = object->findChild(TINYJS___PROTO___VAR); + } + return 0; +} CScriptVarLink *CScriptVar::findChildByPath(const string &path) { string::size_type p = path.find('.'); CScriptVarLink *child; @@ -1673,7 +1813,7 @@ CScriptVarLink *CScriptVar::findChildByPath(const string &path) { CScriptVarLink *CScriptVar::findChildOrCreate(const string &childName/*, int varFlags*/) { CScriptVarLink *l = findChild(childName); if (l) return l; - return addChild(childName, newScriptVar(Undefined)); + return addChild(childName, constScriptVar(Undefined)); // return addChild(childName, new CScriptVar(context, TINYJS_BLANK_DATA, varFlags)); } @@ -1687,49 +1827,63 @@ CScriptVarLink *CScriptVar::findChildOrCreateByPath(const string &path) { return (*l)->findChildOrCreateByPath(path.substr(p+1)); } +void CScriptVar::keys(std::set &Keys, bool OnlyEnumerable/*=true*/, uint32_t ID/*=0*/) +{ + setTemporaryID(ID); + for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it) { + if(!OnlyEnumerable || (*it)->isEnumerable()) + Keys.insert((*it)->getName()); + } + CScriptVarLink *__proto__ = 0; + if( ID && (__proto__ = findChild(TINYJS___PROTO___VAR)) && (*__proto__)->getTempraryID() != ID ) + (*__proto__)->keys(Keys, OnlyEnumerable, ID); +} + /// add & remove CScriptVarLink *CScriptVar::addChild(const string &childName, const CScriptVarPtr &child, int linkFlags /*= SCRIPTVARLINK_DEFAULT*/) { +CScriptVarLink *link = 0; + SCRIPTVAR_CHILDS_it it = lower_bound(Childs.begin(), Childs.end(), childName, compare_child_name); + if(it == Childs.end() || (*it)->getName() != childName) { + link = new CScriptVarLink(child?child:constScriptVar(Undefined), childName, linkFlags); + link->setOwner(this); + link->setOwned(true); + Childs.insert(it, link); #ifdef _DEBUG - if(findChild(childName)) - ASSERT(0); // addCild - the child exists + } else { + ASSERT(0); // addChild - the child exists #endif - CScriptVarLink *link = new CScriptVarLink(child?child:newScriptVar(Undefined), childName, linkFlags); - link->owner = this; - link->SetOwned(true); - SCRIPTVAR_CHILDS_it it = lower_bound(Childs.begin(), Childs.end(), childName, compare_child_name); - if(it == Childs.end() || (*it)->name != childName) { - it = Childs.insert(it, link); } return link; } CScriptVarLink *CScriptVar::addChildNoDup(const string &childName, const CScriptVarPtr &child, int linkFlags /*= SCRIPTVARLINK_DEFAULT*/) { - // if no child supplied, create one - - CScriptVarLink *link = findChild(childName); - if (link) { - if(link->operator->() != child.operator->()) link->replaceWith(child); + SCRIPTVAR_CHILDS_it it = lower_bound(Childs.begin(), Childs.end(), childName, compare_child_name); + if(it == Childs.end() || (*it)->getName() != childName) { + CScriptVarLink *link = new CScriptVarLink(child, childName, linkFlags); + link->setOwner(this); + link->setOwned(true); + Childs.insert(it, link); + return link; } else { - link = new CScriptVarLink(child, childName, linkFlags); - link->owner = this; - link->SetOwned(true); - SCRIPTVAR_CHILDS_it it = lower_bound(Childs.begin(), Childs.end(), childName, compare_child_name); - if(it == Childs.end() || (*it)->name != childName) { - it = Childs.insert(it, link); - } + (*it)->replaceWith(child); + return (*it); } +} - return link; +bool CScriptVar::removeLink(CScriptVarLinkPtr &Link) { + CScriptVarLink *link = Link.getRealLink(); + Link.clear(); + return removeLink(link); } bool CScriptVar::removeLink(CScriptVarLink *&link) { if (!link) return false; + SCRIPTVAR_CHILDS_it it = lower_bound(Childs.begin(), Childs.end(), link->getName(), compare_child_name); + if(it != Childs.end() && (*it) == link) { + Childs.erase(it); #ifdef _DEBUG - if(findChild(link->name) != link) + } else { ASSERT(0); // removeLink - the link is not atached to this var #endif - SCRIPTVAR_CHILDS_it it = lower_bound(Childs.begin(), Childs.end(), link->name, compare_child_name); - if(it != Childs.end() && (*it)->name == link->name) { - Childs.erase(it); } delete link; link = 0; @@ -1743,35 +1897,10 @@ void CScriptVar::removeAllChildren() { Childs.clear(); } - -/// funcions for FUNCTION - -void CScriptVar::setReturnVar(const CScriptVarPtr &var) { - CScriptVarLink *l = findChild(TINYJS_RETURN_VAR); - if (l) l->replaceWith(var); - else addChild(TINYJS_RETURN_VAR, var); -} - -CScriptVarPtr CScriptVar::getParameter(const string &name) { - return findChildOrCreate(name); -} - -CScriptVarPtr CScriptVar::getParameter(int Idx) { - CScriptVarLink *arguments = findChildOrCreate(TINYJS_ARGUMENTS_VAR); - return (*arguments)->findChildOrCreate(int2string(Idx)); -} -int CScriptVar::getParameterLength() { - CScriptVarLink *arguments = findChildOrCreate(TINYJS_ARGUMENTS_VAR); - return (*(*arguments)->findChildOrCreate("length"))->getInt(); -} - - - - CScriptVarPtr CScriptVar::getArrayIndex(int idx) { CScriptVarLink *link = findChild(int2string(idx)); if (link) return link; - else return newScriptVar(Undefined); // undefined + else return constScriptVar(Undefined); // undefined } void CScriptVar::setArrayIndex(int idx, const CScriptVarPtr &value) { @@ -1794,185 +1923,27 @@ int CScriptVar::getArrayLength() { if (!isArray()) return 0; for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it) { - if (::isNumber((*it)->name)) { - int val = atoi((*it)->name.c_str()); + if (::isNumber((*it)->getName())) { + int val = atoi((*it)->getName().c_str()); if (val > highest) highest = val; } } return highest+1; } -template -CScriptVarPtr DoMaths(CScriptVarPtr &a, CScriptVarPtr &b, int op) -{ - int dai = a->getInt();// use int when needed - int dbi = b->getInt(); - T da = (T)(a->isDouble()?a->getDouble():dai); - T db = (T)(b->isDouble()?b->getDouble():dbi); - switch (op) { - case '+': return a->newScriptVar(da+db); - case '-': return a->newScriptVar(da-db); - case '*': return a->newScriptVar(da*db); - case '/': - if(db==0) { - if(da==0) return a->newScriptVar(NaN); // 0/0 = NaN - else return a->newScriptVar(Infinity(da<0 ? -1 : 1)); // /0 = Infinity - } else return a->newScriptVar(da/db); - case '%': - if(db==0) return a->newScriptVar(NaN); // 0/0 = NaN - else return a->newScriptVar(dai%dbi); - case '&': return a->newScriptVar(dai&dbi); - case '|': return a->newScriptVar(dai|dbi); - case '^': return a->newScriptVar(dai^dbi); - case '~': return a->newScriptVar(~dai); - case LEX_LSHIFT: return a->newScriptVar(dai<newScriptVar(dai>>dbi); - case LEX_RSHIFTU: return a->newScriptVar((int)(((unsigned int)dai)>>dbi)); - case LEX_EQUAL: return a->newScriptVar(da==db); - case LEX_NEQUAL: return a->newScriptVar(da!=db); - case '<': return a->newScriptVar(danewScriptVar(da<=db); - case '>': return a->newScriptVar(da>db); - case LEX_GEQUAL: return a->newScriptVar(da>=db); - default: throw new CScriptException("This operation not supported on the int datatype"); - } -} -#define RETURN_NAN return newScriptVar(NaN) CScriptVarPtr CScriptVar::mathsOp(const CScriptVarPtr &b, int op) { - CScriptVarPtr a = this; - // TODO Equality checks on classes/structures - // Type equality check - if (op == LEX_TYPEEQUAL || op == LEX_NTYPEEQUAL) { - // check type first, then call again to check data - bool eql = false; - if(!a->isNaN() && !b->isNaN() && typeid(*a.getVar()) == typeid(*b.getVar()) ) { - CScriptVarPtr e = a->mathsOp(b, LEX_EQUAL); - eql = e->getBool(); - } - if (op == LEX_TYPEEQUAL) - return newScriptVar(eql); - else - return newScriptVar(!eql); - } - // do maths... - bool a_isString = a->isString(); - bool b_isString = b->isString(); - // special for strings and string '+' - // both a String or one a String and op='+' - if( (a_isString && b_isString) || ((a_isString || b_isString) && op == '+')) { - string da = a->getString(); - string db = b->getString(); - switch (op) { - case '+': return newScriptVar(da+db); - case LEX_EQUAL: return newScriptVar(da==db); - case LEX_NEQUAL: return newScriptVar(da!=db); - case '<': return newScriptVar(da': return newScriptVar(da>db); - case LEX_GEQUAL: return newScriptVar(da>=db); - default: RETURN_NAN; - } - } - // special for undefined and null - else if( (a->isUndefined() || a->isNull()) && (b->isUndefined() || b->isNull()) ) { - switch (op) { - case LEX_NEQUAL: return newScriptVar( !( ( a->isUndefined() || a->isNull() ) && ( b->isUndefined() || b->isNull() ) ) ); - case LEX_EQUAL: return newScriptVar( ( a->isUndefined() || a->isNull() ) && ( b->isUndefined() || b->isNull() ) ); - case LEX_GEQUAL: - case LEX_LEQUAL: - case '<': - case '>': return newScriptVar(false); - default: RETURN_NAN; - } - } - if (a->isArray() && b->isArray()) { - /* Just check pointers */ - switch (op) { - case LEX_EQUAL: return newScriptVar(a==b); - case LEX_NEQUAL: return newScriptVar(a!=b); - } - } else if (a->isObject() && b->isObject()) { - /* Just check pointers */ - switch (op) { - case LEX_EQUAL: return newScriptVar(a==b); - case LEX_NEQUAL: return newScriptVar(a!=b); - } - } - - // gets only an Integer, a Double, in Infinity or a NaN - CScriptVarPtr a_l = a->getNumericVar(); - CScriptVarPtr b_l = b->getNumericVar(); - { - CScriptVarPtr a = a_l; - CScriptVarPtr b = b_l; - - if( a->isNaN() || b->isNaN() ) { - switch (op) { - case LEX_NEQUAL: return newScriptVar(true); - case LEX_EQUAL: - case LEX_GEQUAL: - case LEX_LEQUAL: - case '<': - case '>': return newScriptVar(false); - default: RETURN_NAN; - } - } - else if((a->isInfinity() || b->isInfinity())) { - int tmp = 0; - int a_i=a->isInfinity(), a_sig=a->getInt()>0?1:-1; - int b_i=b->isInfinity(), b_sig=a->getInt()>0?1:-1; - switch (op) { - case LEX_EQUAL: return newScriptVar(a_i == b_i); - case LEX_GEQUAL: - case '>': return newScriptVar(a_i >= b_i); - case LEX_LEQUAL: - case '<': return newScriptVar(a_i <= b_i); - case LEX_NEQUAL: return newScriptVar(a_i != b_i); - case '+': if(a_i && b_i && a_i != b_i) RETURN_NAN; - return newScriptVar(Infinity(b_i?b_i:a_i)); - case '-': if(a_i && a_i == b_i) RETURN_NAN; - return newScriptVar(Infinity(b_i?-b_i:a_i)); - case '*': tmp = a->getInt() * b->getInt(); - if(tmp == 0) RETURN_NAN; - return newScriptVar(Infinity(tmp)); - case '/': if(a_i && b_i) RETURN_NAN; - if(b_i) return newScriptVar(0); - return newScriptVar(Infinity(a_sig*b_sig)); - case '%': if(a_i) RETURN_NAN; - return newScriptVar(Infinity(a_sig)); - case '&': return newScriptVar( 0); - case '|': - case '^': if(a_i && b_i) return newScriptVar( 0); - return newScriptVar(a_i?b->getInt():a->getInt()); - case LEX_LSHIFT: - case LEX_RSHIFT: - case LEX_RSHIFTU: if(a_i) return newScriptVar(0); - return newScriptVar(a->getInt()); - default: throw new CScriptException("This operation not supported on the int datatype"); - } - } else { - if (!a->isDouble() && !b->isDouble()) { - // use ints - return DoMaths(a, b, op); - } else { - // use doubles - return DoMaths(a, b, op); - } - } - } - - ASSERT(0); - return CScriptVarPtr(); + bool execute = true; + return context->mathsOp(execute, this, b, op); } void CScriptVar::trace(const string &name) { string indentStr; - int uniqueID = context->getUniqueID(); + uint32_t uniqueID = context->getUniqueID(); trace(indentStr, uniqueID, name); } -void CScriptVar::trace(string &indentStr, int uniqueID, const string &name) { +void CScriptVar::trace(string &indentStr, uint32_t uniqueID, const string &name) { string indent = " "; - char *extra=""; + const char *extra=""; if(temporaryID == uniqueID) extra = " recursion detected"; TRACE("%s'%s' = '%s' %s%s\n", @@ -1985,8 +1956,8 @@ void CScriptVar::trace(string &indentStr, int uniqueID, const string &name) { temporaryID = uniqueID; indentStr+=indent; for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it) { - if((*it)->IsEnumerable()) - (*(*it))->trace(indentStr, uniqueID, (*it)->name); + if((*it)->isEnumerable()) + (*(*it))->trace(indentStr, uniqueID, (*it)->getName()); } indentStr = indentStr.substr(0, indentStr.length()-2); } @@ -2016,10 +1987,10 @@ void CScriptVar::getJSON(ostringstream &destination, const string linePrefix) { int count = Childs.size(); for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it) { destination << indentedLinePrefix; - if (isAlphaNum((*it)->name)) - destination << (*it)->name; + if (isAlphaNum((*it)->getName())) + destination << (*it)->getName(); else - destination << getJSString((*it)->name); + destination << getJSString((*it)->getName()); destination << " : "; (*(*it))->getJSON(destination, indentedLinePrefix); if (--count) destination << ",\n"; @@ -2060,24 +2031,97 @@ int CScriptVar::getRefs() { return refs; } -void CScriptVar::setTempraryIDrecursive(int ID) { +void CScriptVar::setTemporaryID_recursive(uint32_t ID) { if(temporaryID != ID) { temporaryID = ID; for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it) { - (*(*it))->setTempraryIDrecursive(ID); + (*(*it))->setTemporaryID_recursive(ID); } } } +////////////////////////////////////////////////////////////////////////// +/// CScriptVarLinkPtr +////////////////////////////////////////////////////////////////////////// + +CScriptVarLinkPtr &CScriptVarLinkPtr::operator()(CScriptVarLink *Link) { + bool execute = true; + (*this)(execute, Link); + return *this; +} +CScriptVarLinkPtr &CScriptVarLinkPtr::operator()(bool &execute, CScriptVarLink *Link) { + ASSERT(!Link || Link->owner); // no add not Owned Links + link = Link; + if(link && (*link)->isAccessor()) { + CScriptVarLink *getter = (*link)->findChild(TINYJS_ACCESSOR_GET_VAR); + if(getter) { + std::vector Params; + ASSERT(link->owner); + set_tmp_link((*getter)->getContext()->callFunction(execute, **getter, Params, link->owner)); + } else + set_tmp_link((*link)->constScriptVar(Undefined)); + }else + clear_tmp_link(); + return *this; +} -////////////////////////////////////////////////////////////////////////// CScriptVarObject +void CScriptVarLinkPtr::replaceVar(bool &execute, const CScriptVarPtr &Var) { + if(link && (*link)->isAccessor()) { + CScriptVarLink *setter = (*link)->findChild(TINYJS_ACCESSOR_SET_VAR); + if(setter) { + std::vector Params; + Params.push_back(Var); + bool execute; + ASSERT(link->owner); + (*setter)->getContext()->callFunction(execute, **setter, Params, link->owner); + } + } else + getLink()->var = Var; +} + +void CScriptVarLinkPtr::swap(CScriptVarLinkPtr &Link){ + CScriptVarLink *_link = link; link = Link.link; Link.link = _link; + CScriptVarLinkTmpPtr _tmp_link = tmp_link; tmp_link = Link.tmp_link; Link.tmp_link = _tmp_link; +} + +void CScriptVarLinkPtr::set_tmp_link(const CScriptVarPtr &Var, const std::string &Name /*= TINYJS_TEMP_NAME*/, int Flags /*= SCRIPTVARLINK_DEFAULT*/) { + // refs()==1 makes more like a pointer + // if refs==1 then only this LinkPtr owns the tmp_link + // otherwise we creates a new one + if(tmp_link.refs()==1) { + tmp_link->name = Name; + tmp_link->owner = 0; + tmp_link->flags = Flags&~SCRIPTVARLINK_OWNED; + tmp_link->var = Var; + } else if(tmp_link.refs()==0) + tmp_link = CScriptVarLinkTmpPtr(Var, Name, Flags); + else + tmp_link = CScriptVarLinkTmpPtr(Var, Name, Flags); +} + +void CScriptVarLinkPtr::clear_tmp_link(){ + if(tmp_link.refs()==1) + tmp_link->var.clear(); + else if(tmp_link.refs()>1) + tmp_link = CScriptVarLinkTmpPtr(); +} + +////////////////////////////////////////////////////////////////////////// +/// CScriptVarObject +////////////////////////////////////////////////////////////////////////// declare_dummy_t(Object); -//CScriptVarObject::CScriptVarObject(CTinyJS *Context, bool withPrototype=true) : CScriptVar(Context, withPrototype?Context->objectPrototype:CScriptVarPtr()) {} +CScriptVarObject::CScriptVarObject(CTinyJS *Context) : CScriptVar(Context, Context->objectPrototype) { } CScriptVarObject::~CScriptVarObject() {} CScriptVarPtr CScriptVarObject::clone() { return new CScriptVarObject(*this); } bool CScriptVarObject::isObject() { return true; } -string CScriptVarObject::getString() { return "[ Object ]"; }; +bool CScriptVarObject::isPrimitive() { return false; } + +int CScriptVarObject::getInt() { return getPrimitivVar()->getInt(); } +bool CScriptVarObject::getBool() { return getPrimitivVar()->getBool(); } +double CScriptVarObject::getDouble() { return getPrimitivVar()->getDouble(); } +std::string CScriptVarObject::getString() { return getPrimitivVar()->getString(); } + string CScriptVarObject::getParsableString(const string &indentString, const string &indent) { ostringstream destination; string nl = indent.size() ? "\n" : " "; @@ -2088,12 +2132,12 @@ string CScriptVarObject::getParsableString(const string &indentString, const str int count = 0; destination << nl; for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it) { - if((*it)->IsEnumerable()) { + if((*it)->isEnumerable()) { if (count++) destination << "," << nl; - if (isAlphaNum((*it)->name)) - destination << new_indentString << (*it)->name; + if (isAlphaNum((*it)->getName())) + destination << new_indentString << (*it)->getName(); else - destination << new_indentString << "\"" << getJSString((*it)->name) << "\""; + destination << new_indentString << "\"" << getJSString((*it)->getName()) << "\""; destination << " : "; destination << (*(*it))->getParsableString(new_indentString, indent); } @@ -2104,14 +2148,74 @@ string CScriptVarObject::getParsableString(const string &indentString, const str return destination.str(); } string CScriptVarObject::getVarType() { return "object"; } +CScriptVarPtr CScriptVarObject::_toString(bool execute, int radix) { return newScriptVar("[ Object ]"); }; + +////////////////////////////////////////////////////////////////////////// +/// CScriptVarObjectWrap +////////////////////////////////////////////////////////////////////////// + +declare_dummy_t(ObjectWrap); + +CScriptVarObjectWrap::CScriptVarObjectWrap(CTinyJS *Context, const CScriptVarPtr &Value) : CScriptVarObject(Context), value(Value) { + ASSERT(value); + CScriptVarLink *proto = Value->findChild(TINYJS___PROTO___VAR); + this->addChildNoDup(TINYJS___PROTO___VAR, proto, SCRIPTVARLINK_WRITABLE); +} +CScriptVarObjectWrap::~CScriptVarObjectWrap(){} +CScriptVarPtr CScriptVarObjectWrap::clone() { return new CScriptVarObjectWrap(*this); } +CScriptVarPtr CScriptVarObjectWrap::_valueOf(bool execute) { return value; } +CScriptVarPtr CScriptVarObjectWrap::_toString(bool execute, int radix/*=0*/) { return value; } +std::string CScriptVarObjectWrap::getParsableString( const std::string &indentString, const std::string &indent ) { + return value->getParsableString(indentString, indent); +} +void CScriptVarObjectWrap::setTemporaryID_recursive(uint32_t ID) { + CScriptVar::setTemporaryID_recursive(ID); + value->setTemporaryID_recursive(ID); +} + +////////////////////////////////////////////////////////////////////////// +/// CScriptVarError +////////////////////////////////////////////////////////////////////////// +const char *ERROR_NAME[] = {"Error", "EvalError", "RangeError", "ReferenceError", "SyntaxError", "TypeError"}; + +CScriptVarError::CScriptVarError(CTinyJS *Context, ERROR_TYPES type, const char *message, const char *file, int line, int column) : CScriptVarObject(Context, Context->getErrorPrototype(type)) { + if(message && *message) addChild("message", newScriptVar(message)); + if(file && *file) addChild("fileName", newScriptVar(file)); + if(line>=0) addChild("lineNumber", newScriptVar(line+1)); + if(column>=0) addChild("column", newScriptVar(column+1)); +} + +CScriptVarError::~CScriptVarError() {} +CScriptVarPtr CScriptVarError::clone() { return new CScriptVarError(*this); } +bool CScriptVarError::isError() { return true; } + +CScriptVarPtr CScriptVarError::_toString(bool execute, int radix) { + CScriptVarLink *link; + string name = ERROR_NAME[Error]; + link = findChildWithPrototypeChain("name"); if(link) name = (*link)->getString(); + string message; link = findChildWithPrototypeChain("message"); if(link) + message = (*link)->getString(); + string fileName; link = findChildWithPrototypeChain("fileName"); if(link) fileName = (*link)->getString(); + int lineNumber=-1; link = findChildWithPrototypeChain("lineNumber"); if(link) lineNumber = (*link)->getInt(); + int column=-1; link = findChildWithPrototypeChain("column"); if(link) column = (*link)->getInt(); + ostringstream msg; + msg << name << ": " << message; + if(lineNumber >= 0) msg << " at Line:" << lineNumber+1; + if(column >=0) msg << " Column:" << column+1; + if(fileName.length()) msg << " in " << fileName; + return newScriptVar(msg.str()); +} - -////////////////////////////////////////////////////////////////////////// CScriptVarAccessor +////////////////////////////////////////////////////////////////////////// +/// CScriptVarAccessor +////////////////////////////////////////////////////////////////////////// declare_dummy_t(Accessor); +CScriptVarAccessor::CScriptVarAccessor(CTinyJS *Context) : CScriptVar(Context, Context->objectPrototype) { } CScriptVarAccessor::~CScriptVarAccessor() {} CScriptVarPtr CScriptVarAccessor::clone() { return new CScriptVarAccessor(*this); } bool CScriptVarAccessor::isAccessor() { return true; } +bool CScriptVarAccessor::isPrimitive() { return false; } string CScriptVarAccessor::getString() { return "[ Object ]"; }; string CScriptVarAccessor::getParsableString(const string &indentString, const string &indent) { @@ -2126,10 +2230,10 @@ string CScriptVarAccessor::getParsableString(const string &indentString, const s destination << nl; for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it) { - if (isAlphaNum((*it)->name)) - destination << new_indentString << (*it)->name; + if (isAlphaNum((*it)->getName())) + destination << new_indentString << (*it)->getName(); else - destination << new_indentString << "\"" << getJSString((*it)->name) << "\""; + destination << new_indentString << "\"" << getJSString((*it)->getName()) << "\""; destination << " : "; destination << (*(*it))->getParsableString(new_indentString, indent); if (--count) destination << "," << nl; @@ -2158,6 +2262,7 @@ CScriptVarArray::CScriptVarArray(CTinyJS *Context) : CScriptVar(Context, Context CScriptVarArray::~CScriptVarArray() {} CScriptVarPtr CScriptVarArray::clone() { return new CScriptVarArray(*this); } bool CScriptVarArray::isArray() { return true; } +bool CScriptVarArray::isPrimitive() { return false; } string CScriptVarArray::getString() { ostringstream destination; int len = getArrayLength(); @@ -2189,15 +2294,17 @@ string CScriptVarArray::getParsableString(const string &indentString, const stri void CScriptVarArray::native_Length(const CFunctionsScopePtr &c, void *data) { - c->setReturnVar(newScriptVar(c->getParameter("this")->getArrayLength())); + c->setReturnVar(newScriptVar(c->getArgument("this")->getArrayLength())); } ////////////////////////////////////////////////////////////////////////// CScriptVarNull declare_dummy_t(Null); +CScriptVarNull::CScriptVarNull(CTinyJS *Context) : CScriptVar(Context, Context->objectPrototype) { } CScriptVarNull::~CScriptVarNull() {} CScriptVarPtr CScriptVarNull::clone() { return new CScriptVarNull(*this); } bool CScriptVarNull::isNull() { return true; } +bool CScriptVarNull::isNumeric() {return true; } string CScriptVarNull::getString() { return "null"; }; string CScriptVarNull::getVarType() { return "null"; } CScriptVarPtr CScriptVarNull::getNumericVar() { return newScriptVar(0); } @@ -2206,9 +2313,11 @@ CScriptVarPtr CScriptVarNull::getNumericVar() { return newScriptVar(0); } ////////////////////////////////////////////////////////////////////////// CScriptVarUndefined declare_dummy_t(Undefined); +CScriptVarUndefined::CScriptVarUndefined(CTinyJS *Context) : CScriptVar(Context, Context->objectPrototype) { } CScriptVarUndefined::~CScriptVarUndefined() {} CScriptVarPtr CScriptVarUndefined::clone() { return new CScriptVarUndefined(*this); } bool CScriptVarUndefined::isUndefined() { return true; } +bool CScriptVarUndefined::isNumeric() {return true; } string CScriptVarUndefined::getString() { return "undefined"; }; string CScriptVarUndefined::getVarType() { return "undefined"; } @@ -2216,9 +2325,11 @@ string CScriptVarUndefined::getVarType() { return "undefined"; } ////////////////////////////////////////////////////////////////////////// CScriptVarNaN declare_dummy_t(NaN); +CScriptVarNaN::CScriptVarNaN(CTinyJS *Context) : CScriptVar(Context, Context->numberPrototype) {} CScriptVarNaN::~CScriptVarNaN() {} CScriptVarPtr CScriptVarNaN::clone() { return new CScriptVarNaN(*this); } bool CScriptVarNaN::isNaN() { return true; } +bool CScriptVarNaN::isNumeric() {return true; } string CScriptVarNaN::getString() { return "NaN"; }; string CScriptVarNaN::getVarType() { return "number"; } @@ -2262,8 +2373,9 @@ void CScriptVarString::native_Length(const CFunctionsScopePtr &c, void *data) { ////////////////////////////////////////////////////////////////////////// CScriptVarIntegerBase -CScriptVarIntegerBase::CScriptVarIntegerBase(CTinyJS *Context, int Data) : CScriptVar(Context, Context->numberPrototype), data(Data) {} +CScriptVarIntegerBase::CScriptVarIntegerBase(CTinyJS *Context, const CScriptVarPtr &Prototype, int Data) : CScriptVar(Context, Prototype), data(Data) {} CScriptVarIntegerBase::~CScriptVarIntegerBase() {} +bool CScriptVarIntegerBase::isNumeric() {return true; } int CScriptVarIntegerBase::getInt() {return data; } bool CScriptVarIntegerBase::getBool() {return data!=0;} double CScriptVarIntegerBase::getDouble() {return data;} @@ -2274,14 +2386,71 @@ CScriptVarPtr CScriptVarIntegerBase::getNumericVar() { return this; } ////////////////////////////////////////////////////////////////////////// CScriptVarInteger +CScriptVarInteger::CScriptVarInteger(CTinyJS *Context, int Data) : CScriptVarIntegerBase(Context, Context->numberPrototype, Data) {} CScriptVarInteger::~CScriptVarInteger() {} CScriptVarPtr CScriptVarInteger::clone() { return new CScriptVarInteger(*this); } bool CScriptVarInteger::isNumber() { return true; } bool CScriptVarInteger::isInt() { return true; } +static char *tiny_ltoa(long val, unsigned radix) { + char *buf, *buf_end, *p, *firstdig, temp; + unsigned digval; + + buf = (char*)malloc(64); + if(!buf) return 0; + buf_end = buf+64-1; // -1 for '\0' + + p = buf; + if (val < 0) { + *p++ = '-'; + val = -val; + } + + do { + digval = (unsigned) (val % radix); + val /= radix; + *p++ = (char) (digval + (digval > 9 ? ('a'-10) : '0')); + if(p==buf_end) { + char *new_buf = (char *)realloc(buf, buf_end-buf+16+1); // for '\0' + if(!new_buf) { free(buf); return 0; } + p = new_buf + (buf_end - buf); + buf_end = p + 16; + buf = new_buf; + } + } while (val > 0); + + // We now have the digit of the number in the buffer, but in reverse + // order. Thus we reverse them now. + *p-- = '\0'; + firstdig = buf; + do { + temp = *p; + *p = *firstdig; + *firstdig = temp; + p--; + firstdig++; + } while (firstdig < p); + return buf; +} + + +CScriptVarPtr CScriptVarInteger::_toString(bool execute, int radix) { + if(2 <= radix && radix <= 36) + ; + else + radix = 10; // todo error; + char *str = tiny_ltoa(data, radix); + if(str) { + CScriptVarPtr val = newScriptVar(str); + free(str); + return val; + } + return constScriptVar(Undefined); // TODO throw Error +} ////////////////////////////////////////////////////////////////////////// CScriptVarBool +CScriptVarBool::CScriptVarBool(CTinyJS *Context, bool Data) : CScriptVarIntegerBase(Context, Context->booleanPrototype, Data?1:0) {} CScriptVarBool::~CScriptVarBool() {} CScriptVarPtr CScriptVarBool::clone() { return new CScriptVarBool(*this); } bool CScriptVarBool::isBool() { return true; } @@ -2294,6 +2463,7 @@ CScriptVarPtr CScriptVarBool::getNumericVar() { return newScriptVar(data); } Infinity InfinityPositive(1); Infinity InfinityNegative(-1); +CScriptVarInfinity::CScriptVarInfinity(CTinyJS *Context, int Data) : CScriptVarIntegerBase(Context, Context->numberPrototype, Data<0?-1:1) {} CScriptVarInfinity::~CScriptVarInfinity() {} CScriptVarPtr CScriptVarInfinity::clone() { return new CScriptVarInfinity(*this); } int CScriptVarInfinity::isInfinity() { return data; } @@ -2305,8 +2475,9 @@ string CScriptVarInfinity::getString() {return data<0?"-Infinity":"Infinity";} CScriptVarDouble::CScriptVarDouble(CTinyJS *Context, double Data) : CScriptVar(Context, Context->numberPrototype), data(Data) {} CScriptVarDouble::~CScriptVarDouble() {} CScriptVarPtr CScriptVarDouble::clone() { return new CScriptVarDouble(*this); } -bool CScriptVarDouble::isNumber() { return true; } bool CScriptVarDouble::isDouble() { return true; } +bool CScriptVarDouble::isNumber() { return true; } +bool CScriptVarDouble::isNumeric() {return true; } int CScriptVarDouble::getInt() {return (int)data; } bool CScriptVarDouble::getBool() {return data!=0.0;} @@ -2315,19 +2486,105 @@ string CScriptVarDouble::getString() {return float2string(data);} string CScriptVarDouble::getVarType() { return "number"; } CScriptVarPtr CScriptVarDouble::getNumericVar() { return this; } +static char *tiny_dtoa(double val, unsigned radix) { + char *buf, *buf_end, *p, temp; + unsigned digval; + + buf = (char*)malloc(64); + if(!buf) return 0; + buf_end = buf+64-2; // -1 for '.' , -1 for '\0' + + p = buf; + if (val < 0.0) { + *p++ = '-'; + val = -val; + } + + double val_1 = floor(val); + double val_2 = val - val_1; + + + do { + double tmp = val_1 / radix; + val_1 = floor(tmp); + digval = (unsigned)((tmp - val_1) * radix); + + *p++ = (char) (digval + (digval > 9 ? ('a'-10) : '0')); + if(p==buf_end) { + char *new_buf = (char *)realloc(buf, buf_end-buf+16+2); // +2 for '.' + '\0' + if(!new_buf) { free(buf); return 0; } + p = new_buf + (buf_end - buf); + buf_end = p + 16; + buf = new_buf; + } + } while (val_1 > 0.0); + + // We now have the digit of the number in the buffer, but in reverse + // order. Thus we reverse them now. + char *p1 = buf; + char *p2 = p-1; + do { + temp = *p2; + *p2-- = *p1; + *p1++ = temp; + } while (p1 < p2); + + if(val_2) { + *p++ = '.'; + do { + val_2 *= radix; + digval = (unsigned)(val_2); + val_2 -= digval; + + *p++ = (char) (digval + (digval > 9 ? ('a'-10) : '0')); + if(p==buf_end) { + char *new_buf = (char *)realloc(buf, buf_end-buf+16); + if(!new_buf) { free(buf); return 0; } + p = new_buf + (buf_end - buf); + buf_end = p + 16; + buf = new_buf; + } + } while (val_2 > 0.0); + + } + *p = '\0'; + return buf; +} + + +CScriptVarPtr CScriptVarDouble::_toString(bool execute, int radix) { + if(2 <= radix && radix <= 36) + ; + else + radix = 10; // todo error; + if(radix == 10) + return newScriptVar(float2string(data)); + else { + char *str = tiny_dtoa(data, radix); + if(str) { + CScriptVarPtr val = newScriptVar(str); + free(str); + return val; + } + return constScriptVar(Undefined); // TODO throw Error + } +} + ////////////////////////////////////////////////////////////////////////// CScriptVarFunction -CScriptVarFunction::CScriptVarFunction(CTinyJS *Context, CScriptTokenDataFnc *Data) : CScriptVar(Context, Context->functionPrototype), data(0) { +CScriptVarFunction::CScriptVarFunction(CTinyJS *Context, CScriptTokenDataFnc *Data) : CScriptVarObject(Context, Context->functionPrototype), data(0) { setFunctionData(Data); } CScriptVarFunction::~CScriptVarFunction() { setFunctionData(0); } CScriptVarPtr CScriptVarFunction::clone() { return new CScriptVarFunction(*this); } +bool CScriptVarFunction::isObject() { return true; } bool CScriptVarFunction::isFunction() { return true; } +bool CScriptVarFunction::isPrimitive() { return false; } -string CScriptVarFunction::getString() {return "[ Function ]";} +//string CScriptVarFunction::getString() {return "[ Function ]";} string CScriptVarFunction::getVarType() { return "function"; } -string CScriptVarFunction::getParsableBlockString(TOKEN_VECT::iterator &it, TOKEN_VECT::iterator end, const string indentString, const string indent) { +string CScriptVarFunction::getParsableBlockString(TOKEN_VECT_it &it, TOKEN_VECT_it end, const string indentString, const string indent) { ostringstream destination; string nl = indent.size() ? "\n" : " "; string my_indentString = indentString; @@ -2339,7 +2596,9 @@ string CScriptVarFunction::getParsableBlockString(TOKEN_VECT::iterator &it, TOKE if(add_nl) OutString.append(nl).append(my_indentString); bool old_block_start = block_start; add_nl = block_start = false; - if(LEX_TOKEN_DATA_STRING(it->token)) + if(it->token == LEX_T_LOOP_LABEL) { + // ignore BLIND_LABLE + }else if(LEX_TOKEN_DATA_STRING(it->token)) OutString.append(it->String()).append(" "); else if(LEX_TOKEN_DATA_FLOAT(it->token)) OutString.append(float2string(it->Float())).append(" "); @@ -2350,13 +2609,13 @@ string CScriptVarFunction::getParsableBlockString(TOKEN_VECT::iterator &it, TOKE if(it->token == LEX_R_FUNCTION) OutString.append(data->name); OutString.append("("); - if(data->parameter.size()) { - OutString.append(data->parameter.front()); - for(vector::iterator it=data->parameter.begin()+1; it!=data->parameter.end(); ++it) + if(data->arguments.size()) { + OutString.append(data->arguments.front()); + for(STRING_VECTOR_it it=data->arguments.begin()+1; it!=data->arguments.end(); ++it) OutString.append(", ").append(*it); } OutString.append(") "); - TOKEN_VECT::iterator it=data->body.begin(); + TOKEN_VECT_it it=data->body.begin(); OutString += getParsableBlockString(it, data->body.end(), indentString, indent); } else if(it->token == '{') { @@ -2388,9 +2647,9 @@ string CScriptVarFunction::getParsableString(const string &indentString, const s destination << "function "<name<<"("; // get list of parameters - if(data->parameter.size()) { - destination << data->parameter.front(); - for(vector::iterator it = data->parameter.begin()+1; it != data->parameter.end(); ++it) { + if(data->arguments.size()) { + destination << data->arguments.front(); + for(STRING_VECTOR_it it = data->arguments.begin()+1; it != data->arguments.end(); ++it) { destination << ", " << *it; } } @@ -2402,18 +2661,26 @@ string CScriptVarFunction::getParsableString(const string &indentString, const s if(isNative()) { destination << "{ /* native Code */ }"; } else { - TOKEN_VECT::iterator it=data->body.begin(); + TOKEN_VECT_it it=data->body.begin(); destination << getParsableBlockString(it, data->body.end(), indentString, indent); } return destination.str(); } + +CScriptVarPtr CScriptVarFunction::_toString(bool execute, int radix){ + string indent; + return newScriptVar(getParsableString("", indent)); +} + + + CScriptTokenDataFnc *CScriptVarFunction::getFunctionData() { return data; } void CScriptVarFunction::setFunctionData(CScriptTokenDataFnc *Data) { if(data) { data->unref(); data = 0; } if(Data) { data = Data; data->ref(); - addChildNoDup("length", newScriptVar((int)data->parameter.size()), 0); + addChildNoDup("length", newScriptVar((int)data->arguments.size()), 0); } } @@ -2460,6 +2727,35 @@ CScriptVarLink *CScriptVarScopeFnc::findInScopes(const string &childName) { return ret; } +void CScriptVarScopeFnc::setReturnVar(const CScriptVarPtr &var) { + addChildNoDup(TINYJS_RETURN_VAR, var); +} + +CScriptVarPtr CScriptVarScopeFnc::getParameter(const string &name) { + return getArgument(name); +} + +CScriptVarPtr CScriptVarScopeFnc::getParameter(int Idx) { + return getArgument(Idx); +} +CScriptVarPtr CScriptVarScopeFnc::getArgument(const string &name) { + return findChildOrCreate(name); +} +CScriptVarPtr CScriptVarScopeFnc::getArgument(int Idx) { + CScriptVarLink *arguments = findChildOrCreate(TINYJS_ARGUMENTS_VAR); + if(arguments) arguments = (*arguments)->findChild(int2string(Idx)); + return arguments ? arguments : constScriptVar(Undefined); +} +int CScriptVarScopeFnc::getParameterLength() { + return getArgumentsLength(); +} +int CScriptVarScopeFnc::getArgumentsLength() { + CScriptVarLink *arguments = findChild(TINYJS_ARGUMENTS_VAR); + if(arguments) arguments = (*arguments)->findChild("length"); + return arguments ? (*arguments)->getInt() : 0; +} + + ////////////////////////////////////////////////////////////////////////// CScriptVarScopeLet @@ -2471,7 +2767,7 @@ CScriptVarScopeLet::~CScriptVarScopeLet() {} CScriptVarPtr CScriptVarScopeLet::scopeVar() { // to create var like: var a = ... return getParent()->scopeVar(); } -CScriptVarScopePtr CScriptVarScopeLet::getParent() { return parent; } +CScriptVarScopePtr CScriptVarScopeLet::getParent() { return (CScriptVarPtr)parent; } CScriptVarLink *CScriptVarScopeLet::findInScopes(const string &childName) { CScriptVarLink *ret = findChild(childName); if( !ret ) ret = getParent()->findInScopes(childName); @@ -2486,6 +2782,7 @@ CScriptVarPtr CScriptVarScopeWith::scopeLet() { // to create var like: le return getParent()->scopeLet(); } CScriptVarLink *CScriptVarScopeWith::findInScopes(const string &childName) { + if(childName == "this") return with; CScriptVarLink * ret = (*with)->findChild(childName); if( !ret ) ret = getParent()->findInScopes(childName); return ret; @@ -2503,63 +2800,133 @@ CTinyJS::CTinyJS() { ////////////////////////////////////////////////////////////////////////// // Object-Prototype + // must be created as first object because this prototype is the base of all objects objectPrototype = newScriptVar(Object); - // must be created as first object because the first object becomes not a prototype (see constructor of CScriptVar) + + // all objects have a prototype. Also the prototype of prototypes + objectPrototype->addChild(TINYJS___PROTO___VAR, objectPrototype, 0); + + ////////////////////////////////////////////////////////////////////////// + // Function-Prototype + // must be created as second object because this is the base of all functions (also constructors) + functionPrototype = newScriptVar(Object); + ////////////////////////////////////////////////////////////////////////// // Scopes root = ::newScriptVar(this, Scope); scopes.push_back(root); - ////////////////////////////////////////////////////////////////////////// - // global built-in vars - root->addChild("NaN", newScriptVar(NaN), SCRIPTVARLINK_ENUMERABLE); - root->addChild("Infinity", newScriptVar(InfinityPositive), SCRIPTVARLINK_ENUMERABLE); - root->addChild("undefined", newScriptVar(Undefined), SCRIPTVARLINK_ENUMERABLE); - - ////////////////////////////////////////////////////////////////////////// // Add built-in classes - - ////////////////////////////////////////////////////////////////////////// // Object var = addNative("function Object()", this, &CTinyJS::native_Object); - var->addChild(TINYJS_PROTOTYPE_CLASS, objectPrototype); - addNative("function Object.prototype.hasOwnProperty(prop)", this, &CTinyJS::native_Object_hasOwnProperty); // execute the given string and return the result - + objectPrototype = var->findChild(TINYJS_PROTOTYPE_CLASS); + addNative("function Object.getPrototypeOf(obj)", this, &CTinyJS::native_Object_getPrototypeOf); + addNative("function Object.prototype.hasOwnProperty(prop)", this, &CTinyJS::native_Object_hasOwnProperty); + objectPrototype_valueOf = addNative("function Object.prototype.valueOf()", this, &CTinyJS::native_Object_valueOf); + objectPrototype_toString = addNative("function Object.prototype.toString(radix)", this, &CTinyJS::native_Object_toString); + pseudo_refered.push_back(&objectPrototype); + pseudo_refered.push_back(&objectPrototype_valueOf); + pseudo_refered.push_back(&objectPrototype_toString); + ////////////////////////////////////////////////////////////////////////// // Array - arrayPrototype = newScriptVar(Object); var = addNative("function Array()", this, &CTinyJS::native_Array); - var->addChild(TINYJS_PROTOTYPE_CLASS, arrayPrototype); + arrayPrototype = var->findChild(TINYJS_PROTOTYPE_CLASS); + arrayPrototype->addChild("valueOf", objectPrototype_valueOf); + arrayPrototype->addChild("toString", objectPrototype_toString); + pseudo_refered.push_back(&arrayPrototype); ////////////////////////////////////////////////////////////////////////// // String - stringPrototype = newScriptVar(Object); var = addNative("function String()", this, &CTinyJS::native_String); - var->addChild(TINYJS_PROTOTYPE_CLASS, stringPrototype); + stringPrototype = var->findChild(TINYJS_PROTOTYPE_CLASS); + stringPrototype->addChild("valueOf", objectPrototype_valueOf); + stringPrototype->addChild("toString", objectPrototype_toString); + pseudo_refered.push_back(&stringPrototype); ////////////////////////////////////////////////////////////////////////// // Number - numberPrototype = newScriptVar(Object); var = addNative("function Number()", this, &CTinyJS::native_Number); - var->addChild(TINYJS_PROTOTYPE_CLASS, numberPrototype); - var->addChild("NaN", newScriptVar(NaN), SCRIPTVARLINK_ENUMERABLE); - var->addChild("POSITIVE_INFINITY", newScriptVar(InfinityPositive), SCRIPTVARLINK_ENUMERABLE); - var->addChild("NEGATIVE_INFINITY", newScriptVar(InfinityNegative), SCRIPTVARLINK_ENUMERABLE); + var->addChild("NaN", constNaN = newScriptVarNaN(this), SCRIPTVARLINK_ENUMERABLE); + var->addChild("POSITIVE_INFINITY", constInfinityPositive = newScriptVarInfinity(this, InfinityPositive), SCRIPTVARLINK_ENUMERABLE); + var->addChild("NEGATIVE_INFINITY", constInfinityNegative = newScriptVarInfinity(this, InfinityNegative), SCRIPTVARLINK_ENUMERABLE); + numberPrototype = var->findChild(TINYJS_PROTOTYPE_CLASS); + numberPrototype->addChild("valueOf", objectPrototype_valueOf); + numberPrototype->addChild("toString", objectPrototype_toString); + pseudo_refered.push_back(&numberPrototype); + pseudo_refered.push_back(&constNaN); + pseudo_refered.push_back(&constInfinityPositive); + pseudo_refered.push_back(&constInfinityNegative); + + ////////////////////////////////////////////////////////////////////////// + // Boolean + var = addNative("function Boolean()", this, &CTinyJS::native_Boolean); + booleanPrototype = var->findChild(TINYJS_PROTOTYPE_CLASS); + booleanPrototype->addChild("valueOf", objectPrototype_valueOf); + booleanPrototype->addChild("toString", objectPrototype_toString); + pseudo_refered.push_back(&booleanPrototype); ////////////////////////////////////////////////////////////////////////// // Function - functionPrototype = newScriptVar(Object); - var = addNative("function Function(params, body)", this, &CTinyJS::native_Function); // execute the given string and return the result - var->addChild(TINYJS_PROTOTYPE_CLASS, functionPrototype); - addNative("function Function.prototype.call(objc)", this, &CTinyJS::native_Function_call); // execute the given string and return the result - addNative("function Function.prototype.apply(objc, args)", this, &CTinyJS::native_Function_apply); // execute the given string and return the result + var = addNative("function Function(params, body)", this, &CTinyJS::native_Function); + var->addChildNoDup(TINYJS_PROTOTYPE_CLASS, functionPrototype); + addNative("function Function.prototype.call(objc)", this, &CTinyJS::native_Function_call); + addNative("function Function.prototype.apply(objc, args)", this, &CTinyJS::native_Function_apply); + functionPrototype->addChild("valueOf", objectPrototype_valueOf); + functionPrototype->addChild("toString", objectPrototype_toString); + pseudo_refered.push_back(&functionPrototype); + + ////////////////////////////////////////////////////////////////////////// + // Error + var = addNative("function Error(message, fileName, lineNumber, column)", this, &CTinyJS::native_Error); + errorPrototypes[Error] = var->findChild(TINYJS_PROTOTYPE_CLASS); + errorPrototypes[Error]->addChild("message", newScriptVar("")); + errorPrototypes[Error]->addChild("name", newScriptVar("Error")); + errorPrototypes[Error]->addChild("fileName", newScriptVar("")); + errorPrototypes[Error]->addChild("lineNumber", newScriptVar(-1)); // -1 means not viable + errorPrototypes[Error]->addChild("column", newScriptVar(-1)); // -1 means not viable + + var = addNative("function EvalError(message, fileName, lineNumber, column)", this, &CTinyJS::native_EvalError); + errorPrototypes[EvalError] = var->findChild(TINYJS_PROTOTYPE_CLASS); + errorPrototypes[EvalError]->addChildNoDup(TINYJS___PROTO___VAR, errorPrototypes[Error], SCRIPTVARLINK_WRITABLE); + errorPrototypes[EvalError]->addChild("name", newScriptVar("EvalError")); + + var = addNative("function RangeError(message, fileName, lineNumber, column)", this, &CTinyJS::native_RangeError); + errorPrototypes[RangeError] = var->findChild(TINYJS_PROTOTYPE_CLASS); + errorPrototypes[RangeError]->addChildNoDup(TINYJS___PROTO___VAR, errorPrototypes[Error], SCRIPTVARLINK_WRITABLE); + errorPrototypes[RangeError]->addChild("name", newScriptVar("RangeError")); + + var = addNative("function ReferenceError(message, fileName, lineNumber, column)", this, &CTinyJS::native_ReferenceError); + errorPrototypes[ReferenceError] = var->findChild(TINYJS_PROTOTYPE_CLASS); + errorPrototypes[ReferenceError]->addChildNoDup(TINYJS___PROTO___VAR, errorPrototypes[Error], SCRIPTVARLINK_WRITABLE); + errorPrototypes[ReferenceError]->addChild("name", newScriptVar("ReferenceError")); + + var = addNative("function SyntaxError(message, fileName, lineNumber, column)", this, &CTinyJS::native_SyntaxError); + errorPrototypes[SyntaxError] = var->findChild(TINYJS_PROTOTYPE_CLASS); + errorPrototypes[SyntaxError]->addChildNoDup(TINYJS___PROTO___VAR, errorPrototypes[Error], SCRIPTVARLINK_WRITABLE); + errorPrototypes[SyntaxError]->addChild("name", newScriptVar("SyntaxError")); + + var = addNative("function TypeError(message, fileName, lineNumber, column)", this, &CTinyJS::native_TypeError); + errorPrototypes[TypeError] = var->findChild(TINYJS_PROTOTYPE_CLASS); + errorPrototypes[TypeError]->addChildNoDup(TINYJS___PROTO___VAR, errorPrototypes[Error], SCRIPTVARLINK_WRITABLE); + errorPrototypes[TypeError]->addChild("name", newScriptVar("TypeError")); ////////////////////////////////////////////////////////////////////////// - // global functions - addNative("function eval(jsCode)", this, &CTinyJS::native_eval); // execute the given string and return the result + // add global built-in vars & constants + root->addChild("undefined", constUndefined = newScriptVarUndefined(this), SCRIPTVARLINK_ENUMERABLE); + pseudo_refered.push_back(&constUndefined); + root->addChild("NaN", constNaN, SCRIPTVARLINK_ENUMERABLE); + root->addChild("Infinity", constInfinityPositive, SCRIPTVARLINK_ENUMERABLE); + constFalse = newScriptVarBool(this, false); pseudo_refered.push_back(&constFalse); + constTrue = newScriptVarBool(this, true); pseudo_refered.push_back(&constTrue); + constZero = newScriptVar(0); pseudo_refered.push_back(&constZero); + constOne = newScriptVar(1); pseudo_refered.push_back(&constOne); + ////////////////////////////////////////////////////////////////////////// + // add global functions + addNative("function eval(jsCode)", this, &CTinyJS::native_eval); addNative("function isNaN(objc)", this, &CTinyJS::native_isNAN); addNative("function isFinite(objc)", this, &CTinyJS::native_isFinite); addNative("function parseInt(string, radix)", this, &CTinyJS::native_parseInt); @@ -2572,45 +2939,54 @@ CTinyJS::CTinyJS() { CTinyJS::~CTinyJS() { ASSERT(!t); + for(vector::iterator it = pseudo_refered.begin(); it!=pseudo_refered.end(); ++it) + **it = CScriptVarPtr(); + for(int i=Error; iremoveAllChildren(); - ClearLostVars(); + scopes.clear(); + ClearUnreferedVars(); + root = CScriptVarPtr(); #ifdef _DEBUG for(CScriptVar *p = first; p; p=p->next) - { - if(objectPrototype != CScriptVarPtr(p) && - arrayPrototype != CScriptVarPtr(p) && - numberPrototype != CScriptVarPtr(p) && - stringPrototype != CScriptVarPtr(p) && - functionPrototype != CScriptVarPtr(p) && - root != CScriptVarPtr(p) - ) - printf("%p\n", p); - } + printf("%p\n", p); #endif #if DEBUG_MEMORY show_allocated(); #endif } -void CTinyJS::throwError(bool &execute, const string &message) { - if(execute && (runtimeFlags & RUNTIME_CANTHROW)) { - exceptionVar = newScriptVar(message); +////////////////////////////////////////////////////////////////////////// +/// throws an Error & Exception +////////////////////////////////////////////////////////////////////////// + +void CTinyJS::throwError(bool &execute, ERROR_TYPES ErrorType, const std::string &message ) { + if(execute && (runtimeFlags & RUNTIME_CAN_THROW)) { + exceptionVar = newScriptVarError(this, ErrorType, message.c_str(), t->currentFile.c_str(), t->currentLine(), t->currentColumn()); runtimeFlags |= RUNTIME_THROW; execute = false; return; } - throw new CScriptException(message, t->currentFile, t->currentLine(), t->currentColumn()); + throw new CScriptException(ErrorType, message, t->currentFile, t->currentLine(), t->currentColumn()); +} +void CTinyJS::throwException(ERROR_TYPES ErrorType, const std::string &message ) { + throw new CScriptException(ErrorType, message, t->currentFile, t->currentLine(), t->currentColumn()); } -void CTinyJS::throwError(bool &execute, const string &message, CScriptTokenizer::ScriptTokenPosition &Pos) { - if(execute && (runtimeFlags & RUNTIME_CANTHROW)) { - exceptionVar = newScriptVar(message); +void CTinyJS::throwError(bool &execute, ERROR_TYPES ErrorType, const std::string &message, CScriptTokenizer::ScriptTokenPosition &Pos ){ + if(execute && (runtimeFlags & RUNTIME_CAN_THROW)) { + exceptionVar = newScriptVarError(this, ErrorType, message.c_str(), t->currentFile.c_str(), Pos.currentLine(), Pos.currentColumn()); runtimeFlags |= RUNTIME_THROW; execute = false; return; } throw new CScriptException(message, t->currentFile, Pos.currentLine(), Pos.currentColumn()); } +void CTinyJS::throwException(ERROR_TYPES ErrorType, const std::string &message, CScriptTokenizer::ScriptTokenPosition &Pos ){ + throw new CScriptException(message, t->currentFile, Pos.currentLine(), Pos.currentColumn()); +} + +////////////////////////////////////////////////////////////////////////// void CTinyJS::trace() { root->trace(); @@ -2629,7 +3005,7 @@ void CTinyJS::execute(const string &Code, const string &File, int Line, int Colu } CScriptVarLink CTinyJS::evaluateComplex(CScriptTokenizer &Tokenizer) { - CScriptVarSmartLink v; + CScriptVarLinkPtr v; t = &Tokenizer; try { bool execute = true; @@ -2637,23 +3013,18 @@ CScriptVarLink CTinyJS::evaluateComplex(CScriptTokenizer &Tokenizer) { v = execute_statement(execute); while (t->tk==';') t->match(';'); // skip empty statements } while (t->tk!=LEX_EOF); - } catch (CScriptException *e) { + } catch (...) { runtimeFlags = 0; // clean up runtimeFlags - ostringstream msg; - msg << "Error " << e->text; - if(e->line >= 0) msg << " at Line:" << e->line+1 << " Column:" << e->column+1; - if(e->file.length()) msg << " in " << e->file; - delete e; - t=0; - throw new CScriptException(msg.str(),""); + t=0; // clean up Tokenizer + throw; // } t=0; - ClearLostVars(v); + ClearUnreferedVars(v); - int UniqueID = getUniqueID(); - root->setTempraryIDrecursive(UniqueID); - if(v) (*v)->setTempraryIDrecursive(UniqueID); + uint32_t UniqueID = getUniqueID(); + setTemporaryID_recursive(UniqueID); + if(v) v->getVarPtr()->setTemporaryID_recursive(UniqueID); for(CScriptVar *p = first; p; p=p->next) { if(p->temporaryID != UniqueID) @@ -2661,11 +3032,10 @@ CScriptVarLink CTinyJS::evaluateComplex(CScriptTokenizer &Tokenizer) { } if (v) { - CScriptVarLink r = *v; - return r; + return CScriptVarLink(v->getVarPtr()); } // return undefined... - return CScriptVarLink(newScriptVar(Undefined)); + return CScriptVarLink(constScriptVar(Undefined)); } CScriptVarLink CTinyJS::evaluateComplex(const char *Code, const string &File, int Line, int Column) { CScriptTokenizer Tokenizer(Code, File, Line, Column); @@ -2686,11 +3056,11 @@ string CTinyJS::evaluate(const string &Code, const string &File, int Line, int C return evaluate(Code.c_str(), File, Line, Column); } -CScriptVarFunctionNativePtr CTinyJS::addNative(const string &funcDesc, JSCallback ptr, void *userdata) { - return addNative(funcDesc, ::newScriptVar(this, ptr, userdata)); +CScriptVarFunctionNativePtr CTinyJS::addNative(const string &funcDesc, JSCallback ptr, void *userdata, int LinkFlags) { + return addNative(funcDesc, ::newScriptVar(this, ptr, userdata), LinkFlags); } -CScriptVarFunctionNativePtr CTinyJS::addNative(const string &funcDesc, CScriptVarFunctionNativePtr Var) { +CScriptVarFunctionNativePtr CTinyJS::addNative(const string &funcDesc, CScriptVarFunctionNativePtr Var, int LinkFlags) { CScriptLex lex(funcDesc.c_str()); CScriptVarPtr base = root; @@ -2711,119 +3081,82 @@ CScriptVarFunctionNativePtr CTinyJS::addNative(const string &funcDesc, CScriptVa auto_ptr pFunctionData(new CScriptTokenDataFnc); lex.match('('); while (lex.tk!=')') { - pFunctionData->parameter.push_back(lex.tkStr); + pFunctionData->arguments.push_back(lex.tkStr); lex.match(LEX_ID); - if (lex.tk!=')') lex.match(','); + if (lex.tk!=')') lex.match(',',')'); } lex.match(')'); Var->setFunctionData(pFunctionData.release()); - base->addChild(funcName, Var); + Var->addChild(TINYJS_PROTOTYPE_CLASS, newScriptVar(Object), SCRIPTVARLINK_WRITABLE); + + base->addChild(funcName, Var, LinkFlags); return Var; } -CScriptVarSmartLink CTinyJS::parseFunctionDefinition(CScriptToken &FncToken) { +CScriptVarLinkPtr CTinyJS::parseFunctionDefinition(CScriptToken &FncToken) { CScriptTokenDataFnc &Fnc = FncToken.Fnc(); string fncName = (FncToken.token == LEX_T_FUNCTION_OPERATOR) ? TINYJS_TEMP_NAME : Fnc.name; - CScriptVarSmartLink funcVar = new CScriptVarLink(newScriptVar(&Fnc), fncName); + CScriptVarLinkPtr funcVar(newScriptVar(&Fnc), fncName); if(scope() != root) - funcVar->getVarPtr()->addChild(TINYJS_FUNCTION_CLOSURE_VAR, scope(), 0); + (*funcVar)->addChild(TINYJS_FUNCTION_CLOSURE_VAR, scope(), 0); + (*funcVar)->addChild(TINYJS_PROTOTYPE_CLASS, newScriptVar(Object), SCRIPTVARLINK_WRITABLE); return funcVar; } -/* -CScriptVarSmartLink CTinyJS::parseFunctionDefinition() { - CScriptTokenDataFnc &Fnc = t->getToken().Fnc(); - string fncName = (t->tk == LEX_T_FUNCTION_FORCE_ANONYMOUS) ? TINYJS_TEMP_NAME : Fnc.name; - CScriptVarSmartLink funcVar = new CScriptVarLink(new CScriptVar(Fnc), fncName); - (*funcVar)->addChild("length", new CScriptVar((int)Fnc.parameter.size())); - return funcVar; -} -*/ -CScriptVarSmartLink CTinyJS::parseFunctionsBodyFromString(const string &Parameter, const string &FncBody) { - string Fnc = "function ("+Parameter+"){"+FncBody+"}"; +CScriptVarLinkPtr CTinyJS::parseFunctionsBodyFromString(const string &ArgumentList, const string &FncBody) { + string Fnc = "function ("+ArgumentList+"){"+FncBody+"}"; CScriptTokenizer tokenizer(Fnc.c_str()); return parseFunctionDefinition(tokenizer.getToken()); } -class CScriptEvalException { -public: - int runtimeFlags; - CScriptEvalException(int RuntimeFlags) : runtimeFlags(RuntimeFlags){} -}; - -CScriptVarPtr CTinyJS::callFunction(const CScriptVarPtr &Function, std::vector &Arguments, const CScriptVarPtr& This, bool &execute) { +CScriptVarPtr CTinyJS::callFunction(bool &execute, const CScriptVarFunctionPtr &Function, std::vector &Arguments, const CScriptVarPtr &This, CScriptVarPtr *newThis) { //CScriptVarSmartLink CTinyJS::callFunction(CScriptVarSmartLink &Function, vector &Arguments, const CScriptVarPtr& This, bool &execute) { - ASSERT(Function->isFunction()); + ASSERT(Function && Function->isFunction()); - CScriptVarScopeFncPtr functionRoot(::newScriptVar(this, ScopeFnc, Function->findChild(TINYJS_FUNCTION_CLOSURE_VAR))); + CScriptVarScopeFncPtr functionRoot(::newScriptVar(this, ScopeFnc, CScriptVarPtr(Function->findChild(TINYJS_FUNCTION_CLOSURE_VAR)))); functionRoot->addChild("this", This); - CScriptVarSmartLink arguments = functionRoot->addChild(TINYJS_ARGUMENTS_VAR, newScriptVar(Object)); + CScriptVarPtr arguments = functionRoot->addChild(TINYJS_ARGUMENTS_VAR, newScriptVar(Object)); CScriptTokenDataFnc *Fnc = Function->getFunctionData(); if(Fnc->name.size()) functionRoot->addChild(Fnc->name, Function); - int length_proto = Fnc->parameter.size(); + int length_proto = Fnc->arguments.size(); int length_arguments = Arguments.size(); int length = max(length_proto, length_arguments); for(int arguments_idx = 0; arguments_idxaddChild(arguments_idx_str, Arguments[arguments_idx]); + value = arguments->addChild(arguments_idx_str, Arguments[arguments_idx]); } else { - value = newScriptVar(Undefined); + value = constScriptVar(Undefined); } if(arguments_idx < length_proto) { - functionRoot->addChildNoDup(Fnc->parameter[arguments_idx], value); + functionRoot->addChildNoDup(Fnc->arguments[arguments_idx], value); } } - (*arguments)->addChild("length", newScriptVar(length_arguments)); - CScriptVarSmartLink returnVar; + arguments->addChild("length", newScriptVar(length_arguments)); + CScriptVarLinkPtr returnVar; int old_function_runtimeFlags = runtimeFlags; // save runtimeFlags - runtimeFlags &= ~RUNTIME_LOOP_MASK; // clear LOOP-Flags + runtimeFlags &= ~RUNTIME_LOOP_MASK; // clear LOOP-Flags because we can't break or continue a loop from functions-body // execute function! // add the function's execute space to the symbol table so we can recurse - // scopes.push_back(SCOPES::SCOPE_TYPE_FUNCTION, functionRoot->get()); CScopeControl ScopeControl(this); ScopeControl.addFncScope(functionRoot); if (Function->isNative()) { try { CScriptVarFunctionNativePtr(Function)->callFunction(functionRoot); -// ((CScriptVarFunctionNative*)*Function.getVar())->callFunction(functionRoot.getVar()); runtimeFlags = old_function_runtimeFlags | (runtimeFlags & RUNTIME_THROW); // restore runtimeFlags - if(runtimeFlags & RUNTIME_THROW) { + if(runtimeFlags & RUNTIME_THROW) execute = false; - } - } catch (CScriptEvalException *e) { - int exeption_runtimeFlags = e->runtimeFlags; - delete e; - runtimeFlags = old_function_runtimeFlags; // restore runtimeFlags - if(exeption_runtimeFlags & RUNTIME_BREAK) { - if(runtimeFlags & RUNTIME_CANBREAK) { - runtimeFlags |= RUNTIME_BREAK; - execute = false; - } - else - throw new CScriptException("'break' must be inside loop or switch"); - } else if(exeption_runtimeFlags & RUNTIME_CONTINUE) { - if(runtimeFlags & RUNTIME_CANCONTINUE) { - runtimeFlags |= RUNTIME_CONTINUE; - execute = false; - } - else - throw new CScriptException("'continue' must be inside loop"); - } } catch (CScriptVarPtr v) { - if(runtimeFlags & RUNTIME_CANTHROW) { + if(runtimeFlags & RUNTIME_CAN_THROW) { runtimeFlags |= RUNTIME_THROW; execute = false; exceptionVar = v; - } - else { - string e = "uncaught exception: '"+v->getString()+"' in: "+Function->getFunctionData()->name+"()"; - throw new CScriptException(e); - } + } else + throw new CScriptException(SyntaxError, "uncaught exception: '"+v->getString()+"' in: native function '"+Function->getFunctionData()->name+"'"); } } else { /* we just want to execute the block, but something could @@ -2842,14 +3175,172 @@ CScriptVarPtr CTinyJS::callFunction(const CScriptVarPtr &Function, std::vectorfindChild("this"); /* get the real return var before we remove it from our function */ - if(returnVar = functionRoot->findChild(TINYJS_RETURN_VAR)) - return returnVar->getVarPtr(); + if(execute && (returnVar = functionRoot->findChild(TINYJS_RETURN_VAR))) + return returnVar; else - return newScriptVar(Undefined); + return constScriptVar(Undefined); } +template +CScriptVarPtr DoMaths(CScriptVarPtr &a, CScriptVarPtr &b, int op) +{ + int dai = a->getInt();// use int when needed + int dbi = b->getInt(); + T da = (T)(a->isDouble()?a->getDouble():dai); + T db = (T)(b->isDouble()?b->getDouble():dbi); + switch (op) { + case '+': return a->newScriptVar(da+db); + case '-': return a->newScriptVar(da-db); + case '*': return a->newScriptVar(da*db); + case '/': + if(db==0) { + if(da==0) return a->constScriptVar(NaN); // 0/0 = NaN + else return a->constScriptVar(Infinity(da<0 ? -1 : 1)); // /0 = Infinity + } else return a->newScriptVar(da/db); + case '%': + if(db==0) return a->constScriptVar(NaN); // 0/0 = NaN + else return a->newScriptVar(dai%dbi); + case '&': return a->newScriptVar(dai&dbi); + case '|': return a->newScriptVar(dai|dbi); + case '^': return a->newScriptVar(dai^dbi); + case '~': return a->newScriptVar(~dai); + case LEX_LSHIFT: return a->newScriptVar(dai<newScriptVar(dai>>dbi); + case LEX_RSHIFTU: return a->newScriptVar((int)(((unsigned int)dai)>>dbi)); + case LEX_EQUAL: return a->constScriptVar(da==db); + case LEX_NEQUAL: return a->constScriptVar(da!=db); + case '<': return a->constScriptVar(daconstScriptVar(da<=db); + case '>': return a->constScriptVar(da>db); + case LEX_GEQUAL: return a->constScriptVar(da>=db); + default: throw new CScriptException("This operation not supported on the int datatype"); + } +} + +#define RETURN_NAN return constScriptVar(NaN) + +CScriptVarPtr CTinyJS::mathsOp(bool &execute, const CScriptVarPtr &A, const CScriptVarPtr &B, int op) { + if(!execute) return constUndefined; + if (op == LEX_TYPEEQUAL || op == LEX_NTYPEEQUAL) { + // check type first, then call again to check data + if(A->isNaN() || B->isNaN()) return constFalse; + if( (typeid(*A.getVar()) == typeid(*B.getVar())) ^ (op != LEX_TYPEEQUAL) ) + return mathsOp(execute, A, B, op == LEX_TYPEEQUAL ? LEX_EQUAL : LEX_NEQUAL); + return constFalse; + } + if (!A->isPrimitive() && !B->isPrimitive()) { // Objects + /* Just check pointers */ + switch (op) { + case LEX_EQUAL: return constScriptVar(A==B); + case LEX_NEQUAL: return constScriptVar(A!=B); + } + } + + CScriptVarPtr a = A->getPrimitivVar(execute); + CScriptVarPtr b = B->getPrimitivVar(execute); + if(!execute) return constUndefined; + // do maths... + bool a_isString = a->isString(); + bool b_isString = b->isString(); + // special for strings and string '+' + // both a String or one a String and op='+' + if( (a_isString && b_isString) || ((a_isString || b_isString) && op == '+')) { + string da = a->getString(); + string db = b->getString(); + switch (op) { + case '+': return newScriptVar(da+db); + case LEX_EQUAL: return constScriptVar(da==db); + case LEX_NEQUAL: return constScriptVar(da!=db); + case '<': return constScriptVar(da': return constScriptVar(da>db); + case LEX_GEQUAL: return constScriptVar(da>=db); + default: RETURN_NAN; + } + } + // special for undefined and null + else if( (a->isUndefined() || a->isNull()) && (b->isUndefined() || b->isNull()) ) { + switch (op) { + case LEX_NEQUAL: return constScriptVar( !( ( a->isUndefined() || a->isNull() ) && ( b->isUndefined() || b->isNull() ) ) ); + case LEX_EQUAL: return constScriptVar( ( a->isUndefined() || a->isNull() ) && ( b->isUndefined() || b->isNull() ) ); + case LEX_GEQUAL: + case LEX_LEQUAL: + case '<': + case '>': return constScriptVar(false); + default: RETURN_NAN; + } + } + + // gets only an Integer, a Double, in Infinity or a NaN + CScriptVarPtr a_l = a->getNumericVar(); + CScriptVarPtr b_l = b->getNumericVar(); + { + CScriptVarPtr a = a_l; + CScriptVarPtr b = b_l; + + if( a->isNaN() || b->isNaN() ) { + switch (op) { + case LEX_NEQUAL: return constScriptVar(true); + case LEX_EQUAL: + case LEX_GEQUAL: + case LEX_LEQUAL: + case '<': + case '>': return constScriptVar(false); + default: RETURN_NAN; + } + } + else if((a->isInfinity() || b->isInfinity())) { + int tmp = 0; + int a_i=a->isInfinity(), a_sig=a->getInt()>0?1:-1; + int b_i=b->isInfinity(), b_sig=a->getInt()>0?1:-1; + switch (op) { + case LEX_EQUAL: return constScriptVar(a_i == b_i); + case LEX_GEQUAL: + case '>': return constScriptVar(a_i >= b_i); + case LEX_LEQUAL: + case '<': return constScriptVar(a_i <= b_i); + case LEX_NEQUAL: return constScriptVar(a_i != b_i); + case '+': if(a_i && b_i && a_i != b_i) RETURN_NAN; + return constScriptVar(Infinity(b_i?b_i:a_i)); + case '-': if(a_i && a_i == b_i) RETURN_NAN; + return constScriptVar(Infinity(b_i?-b_i:a_i)); + case '*': tmp = a->getInt() * b->getInt(); + if(tmp == 0) RETURN_NAN; + return constScriptVar(Infinity(tmp)); + case '/': if(a_i && b_i) RETURN_NAN; + if(b_i) return newScriptVar(0); + return constScriptVar(Infinity(a_sig*b_sig)); + case '%': if(a_i) RETURN_NAN; + return constScriptVar(Infinity(a_sig)); + case '&': return newScriptVar( 0); + case '|': + case '^': if(a_i && b_i) return newScriptVar( 0); + return newScriptVar(a_i?b->getInt():a->getInt()); + case LEX_LSHIFT: + case LEX_RSHIFT: + case LEX_RSHIFTU: if(a_i) return newScriptVar(0); + return newScriptVar(a->getInt()); + default: throw new CScriptException("This operation not supported on the int datatype"); + } + } else { + if (!a->isDouble() && !b->isDouble()) { + // use ints + return DoMaths(a, b, op); + } else { + // use doubles + return DoMaths(a, b, op); + } + } + } + + ASSERT(0); + return constUndefined; +} + /* CScriptVarSmartLink CTinyJS::setValue( CScriptVarSmartLink Var ) { @@ -2869,37 +3360,49 @@ CScriptVarSmartLink CTinyJS::getValue( CScriptVarSmartLink Var, bool execute ) return Var; } */ -CScriptVarSmartLink CTinyJS::execute_literals(bool &execute) { - if(t->tk == LEX_ID) { - CScriptVarSmartLink a; +CScriptVarLinkPtr CTinyJS::execute_literals(bool &execute) { + switch(t->tk) { + case LEX_ID: if(execute) { - a = findInScopes(t->tkStr()); + CScriptVarLinkPtr a; + a(execute, findInScopes(t->tkStr())); if (!a) { /* Variable doesn't exist! JavaScript says we should create it * (we won't add it here. This is done in the assignment operator)*/ if(t->tkStr() == "this") { - a = new CScriptVarLink(root); // fake this + a = root; // fake this } else { - a = new CScriptVarLink(newScriptVar(Undefined), t->tkStr()); + a(constScriptVar(Undefined), t->tkStr()); } - } - t->match(t->tk); + } else if(t->tkStr() == "this") + a(a->getVarPtr()); // prevent assign to this + t->match(LEX_ID); return a; } - t->match(t->tk); - } else if (t->tk==LEX_INT) { - CScriptVarPtr a = newScriptVar(t->getToken().Int()); - t->match(t->tk); - return new CScriptVarLink(a); - } else if (t->tk==LEX_FLOAT) { - CScriptVarPtr a = newScriptVar(t->getToken().Float()); - t->match(t->tk); - return new CScriptVarLink(a); - } else if (t->tk==LEX_STR) { - CScriptVarPtr a = newScriptVar(t->getToken().String()); - t->match(LEX_STR); - return new CScriptVarLink(a); - } else if (t->tk=='{') { + t->match(LEX_ID); + break; + case LEX_INT: + { + CScriptVarPtr a = newScriptVar(t->getToken().Int()); + t->match(LEX_INT); + return a; + } + break; + case LEX_FLOAT: + { + CScriptVarPtr a = newScriptVar(t->getToken().Float()); + t->match(LEX_FLOAT); + return a; + } + break; + case LEX_STR: + { + CScriptVarPtr a = newScriptVar(t->getToken().String()); + t->match(LEX_STR); + return a; + } + break; + case '{': if(execute) { CScriptVarPtr contents(newScriptVar(Object)); /* JSON-style object definition */ @@ -2910,14 +3413,14 @@ CScriptVarSmartLink CTinyJS::execute_literals(bool &execute) { string id = t->tkStr(); t->match(t->tk); t->match(':'); - CScriptVarSmartLink a = execute_assignment(execute); + CScriptVarLinkPtr a = execute_assignment(execute); if (execute) { - contents->addChildNoDup(id, a->getValue()); + contents->addChildNoDup(id, a); } } else if(t->tk==LEX_T_GET || t->tk==LEX_T_SET) { CScriptTokenDataFnc &Fnc = t->getToken().Fnc(); - if((t->tk == LEX_T_GET && Fnc.parameter.size()==0) || (t->tk == LEX_T_SET && Fnc.parameter.size()==1)) { - CScriptVarSmartLink funcVar = parseFunctionDefinition(t->getToken()); + if((t->tk == LEX_T_GET && Fnc.arguments.size()==0) || (t->tk == LEX_T_SET && Fnc.arguments.size()==1)) { + CScriptVarLinkPtr funcVar = parseFunctionDefinition(t->getToken()); CScriptVarLink *child = contents->findChild(Fnc.name); if(child && !(*child)->isAccessor()) child = 0; if(!child) child = contents->addChildNoDup(Fnc.name, newScriptVar(Accessor)); @@ -2933,19 +3436,20 @@ CScriptVarSmartLink CTinyJS::execute_literals(bool &execute) { } t->match('}'); - return new CScriptVarLink(contents); + return contents; } else t->skip(t->getToken().Int()); - } else if (t->tk=='[') { + break; + case '[': if(execute) { CScriptVarPtr contents(newScriptVar(Array)); /* JSON-style array */ t->match('['); int idx = 0; while (t->tk != ']') { - CScriptVarSmartLink a = execute_assignment(execute); + CScriptVarLinkPtr a = execute_assignment(execute); if (execute) { - contents->addChild(int2string(idx), a->getValue()); + contents->addChild(int2string(idx), a); } // no need to clean here, as it will definitely be used if (t->tk != ']') t->match(','); @@ -2953,26 +3457,26 @@ CScriptVarSmartLink CTinyJS::execute_literals(bool &execute) { } t->match(']'); // return new CScriptVarLink(contents.release()); - return new CScriptVarLink(contents); + return contents; } else t->skip(t->getToken().Int()); - } else if (t->tk==LEX_R_LET) { // let as expression + break; + case LEX_R_LET: // let as expression if(execute) { t->match(LEX_R_LET); t->match('('); CScopeControl ScopeControl(this); ScopeControl.addLetScope(); for(;;) { - CScriptVarSmartLink a; + CScriptVarLinkPtr a; string var = t->tkStr(); t->match(LEX_ID); a = scope()->scopeLet()->findChildOrCreate(var); - a->SetDeletable(false); + a->setDeletable(false); // sort out initialiser if (t->tk == '=') { t->match('='); - CScriptVarSmartLink var = execute_assignment(execute); - a << var; + a.replaceVar(execute, execute_assignment(execute)); } if (t->tk == ',') t->match(','); @@ -2980,86 +3484,90 @@ CScriptVarSmartLink CTinyJS::execute_literals(bool &execute) { break; } t->match(')'); - return new CScriptVarLink(execute_base(execute)); + return execute_base(execute); } else { t->skip(t->getToken().Int()); execute_base(execute); } - } else if (t->tk==LEX_R_FUNCTION) { - ASSERT(0); // no function as operator - } else if (t->tk==LEX_T_FUNCTION_OPERATOR) { + break; + case LEX_T_FUNCTION_OPERATOR: if(execute) { - CScriptVarSmartLink a = parseFunctionDefinition(t->getToken()); - t->match(t->tk); + CScriptVarLinkPtr a = parseFunctionDefinition(t->getToken()); + t->match(LEX_T_FUNCTION_OPERATOR); return a; } - t->match(t->tk); - } else if (t->tk==LEX_R_NEW) { - // new -> create a new object - t->match(LEX_R_NEW); - CScriptVarSmartLink parent = execute_literals(execute); - CScriptVarSmartLink objClass = execute_member(parent, execute); + t->match(LEX_T_FUNCTION_OPERATOR); + break; + case LEX_R_NEW: // new -> create a new object if (execute) { - if((*objClass)->isFunction()) { - CScriptVarPtr obj(newScriptVar(Object)); - CScriptVarLink *prototype = (*objClass)->findChild(TINYJS_PROTOTYPE_CLASS); - if(prototype) obj->addChildNoDup(TINYJS___PROTO___VAR, prototype); - vector arguments; - if (t->tk == '(') { - t->match('('); - while(t->tk!=')') { - CScriptVarPtr value = execute_assignment(execute); - if (execute) { - arguments.push_back(value); + t->match(LEX_R_NEW); + CScriptVarLinkPtr parent = execute_literals(execute); + CScriptVarLinkPtr objClass = execute_member(parent, execute); + if (execute) { + if(objClass->getVarPtr()->isFunction()) { + CScriptVarPtr obj(newScriptVar(Object)); + CScriptVarLink *prototype = (*objClass)->findChild(TINYJS_PROTOTYPE_CLASS); + if(!prototype) prototype = (*objClass)->addChild(TINYJS_PROTOTYPE_CLASS, newScriptVar(Object), SCRIPTVARLINK_WRITABLE); + obj->addChildNoDup(TINYJS___PROTO___VAR, prototype, SCRIPTVARLINK_WRITABLE); + vector arguments; + if (t->tk == '(') { + t->match('('); + while(t->tk!=')') { + CScriptVarPtr value = execute_assignment(execute); + if (execute) + arguments.push_back(value); + if (t->tk!=')') t->match(',', ')'); } - if (t->tk!=')') { t->match(','); } + t->match(')'); } - t->match(')'); - } - if(execute) { - CScriptVarPtr returnVar = callFunction(objClass->getVarPtr(), arguments, obj, execute); - if(returnVar->isArray() || returnVar->isObject()) - return CScriptVarSmartLink(returnVar); - return CScriptVarSmartLink(obj); - } - } else { - throwError(execute, objClass->name + " is not a constructor"); - } - } else { - if (t->tk == '(') { - t->match('('); - while(t->tk != ')') execute_base(execute); - t->match(')'); - } - } - } else if (t->tk==LEX_R_TRUE) { + if(execute) { + CScriptVarPtr returnVar = callFunction(execute, objClass->getVarPtr(), arguments, obj, &obj); + if(!returnVar->isPrimitive()) + return CScriptVarLinkPtr(returnVar); + return CScriptVarLinkPtr(obj); + } + } else + throwError(execute, TypeError, objClass->getName() + " is not a constructor"); + } else + if (t->tk == '(') t->skip(t->getToken().Int()); + } else + t->skip(t->getToken().Int()); + break; + case LEX_R_TRUE: t->match(LEX_R_TRUE); - return new CScriptVarLink(newScriptVar(true)); - } else if (t->tk==LEX_R_FALSE) { + return constScriptVar(true); + case LEX_R_FALSE: t->match(LEX_R_FALSE); - return new CScriptVarLink(newScriptVar(false)); - } else if (t->tk==LEX_R_NULL) { + return constScriptVar(false); + case LEX_R_NULL: t->match(LEX_R_NULL); - return new CScriptVarLink(newScriptVar(Null)); - } else if (t->tk=='(') { - t->match('('); - CScriptVarSmartLink a = execute_base(execute); - t->match(')'); - return a; - } else + return newScriptVar(Null); + case '(': + if(execute) { + t->match('('); + CScriptVarLinkPtr a = execute_base(execute); + t->match(')'); + return a; + } else + t->skip(t->getToken().Int()); + break; + default: t->match(LEX_EOF); - return new CScriptVarLink(newScriptVar(Undefined)); + break; + } + return constScriptVar(Undefined); } -CScriptVarSmartLink CTinyJS::execute_member(CScriptVarSmartLink &parent, bool &execute) { - CScriptVarSmartLink a = parent; +CScriptVarLinkPtr CTinyJS::execute_member(CScriptVarLinkPtr &parent, bool &execute) { + CScriptVarLinkPtr a; + parent.swap(a); if(t->tk == '.' || t->tk == '[') { // string path = a->name; // CScriptVar *parent = 0; while(t->tk == '.' || t->tk == '[') { if(execute && ((*a)->isUndefined() || (*a)->isNull())) { - throwError(execute, a->name + " is " + (*a)->getString()); + throwError(execute, TypeError, a->getName() + " is " + (*a)->getString()); } string name; if(t->tk == '.') { @@ -3068,17 +3576,21 @@ CScriptVarSmartLink CTinyJS::execute_member(CScriptVarSmartLink &parent, bool &e // path += "."+name; t->match(LEX_ID); } else { - t->match('['); - CScriptVarSmartLink index = execute_expression(execute); - name = (*index)->getString(); -// path += "["+name+"]"; - t->match(']'); + if(execute) { + t->match('['); + CScriptVarLinkPtr index = execute_expression(execute); + name = (*index)->getString(); +// path += "["+name+"]"; + t->match(']'); + } else + t->skip(t->getToken().Int()); } if (execute) { - bool first_in_prototype = false; - CScriptVarLink *child = (*a)->findChild(name); - if ( !child && (child = findInPrototypeChain(a, name)) ) - first_in_prototype = true; + bool in_prototype = false; + CScriptVarLink *child = (*a)->findChildWithPrototypeChain(name); +// if ( !child && (child = findInPrototypeChain(a, name)) ) + if ( child && child->getOwner() != a->getVarPtr().getVar() ) + in_prototype = true; if (!child) { /* if we haven't found this defined yet, use the built-in 'length' properly */ @@ -3094,15 +3606,15 @@ CScriptVarSmartLink CTinyJS::execute_member(CScriptVarSmartLink &parent, bool &e } if(child) { parent = a; - if(first_in_prototype) { - a = new CScriptVarLink(child->getVarPtr(), child->name); - a->owner = parent->operator->(); // fake owner - but not set Owned -> for assignment stuff + if(in_prototype) { + a(child->getVarPtr(), child->getName()); + a->setOwner(parent->getVarPtr().getVar()); // fake owner - but not set Owned -> for assignment stuff } else a = child; } else { - CScriptVar *owner = a->operator->(); - a = new CScriptVarLink(newScriptVar(Undefined), name); - a->owner = owner; // fake owner - but not set Owned -> for assignment stuff + CScriptVar *owner = a->getVarPtr().getVar(); + a(constScriptVar(Undefined), name); + a->setOwner(owner); // fake owner - but not set Owned -> for assignment stuff } } } @@ -3110,14 +3622,14 @@ CScriptVarSmartLink CTinyJS::execute_member(CScriptVarSmartLink &parent, bool &e return a; } -CScriptVarSmartLink CTinyJS::execute_function_call(bool &execute) { - CScriptVarSmartLink parent = execute_literals(execute); - CScriptVarSmartLink a = execute_member(parent, execute); +CScriptVarLinkPtr CTinyJS::execute_function_call(bool &execute) { + CScriptVarLinkPtr parent = execute_literals(execute); + CScriptVarLinkPtr a = execute_member(parent, execute); while (t->tk == '(') { if (execute) { - if (!a->getValue()->isFunction()) { + if (!(*a)->isFunction()) { string errorMsg = "Expecting '"; - errorMsg = errorMsg + a->name + "' to be a function"; + errorMsg = errorMsg + a->getName() + "' to be a function"; throw new CScriptException(errorMsg.c_str()); } t->match('('); // path += '('; @@ -3125,26 +3637,28 @@ CScriptVarSmartLink CTinyJS::execute_function_call(bool &execute) { // grab in all parameters vector arguments; while(t->tk!=')') { - CScriptVarSmartLink value = execute_assignment(execute); + CScriptVarLinkPtr value = execute_assignment(execute); // path += (*value)->getString(); if (execute) { - arguments.push_back(value->getValue()); + arguments.push_back(value); } - if (t->tk!=')') { t->match(','); /*path+=',';*/ } + if (t->tk!=')') { t->match(',',')'); /*path+=',';*/ } } t->match(')'); //path+=')'; // setup a return variable - CScriptVarSmartLink returnVar; + CScriptVarLinkPtr returnVar; if(execute) { + if (!parent) + parent = findInScopes("this"); // if no parent use the root-scope CScriptVarPtr This(parent ? parent->getVarPtr() : (CScriptVarPtr )root); - a = callFunction(a->getValue(), arguments, This, execute); + a = callFunction(execute, **a, arguments, This); } } else { // function, but not executing - just parse args and be done t->match('('); while (t->tk != ')') { - CScriptVarSmartLink value = execute_base(execute); + CScriptVarLinkPtr value = execute_base(execute); // if (t->tk!=')') t->match(','); } t->match(')'); @@ -3155,90 +3669,98 @@ CScriptVarSmartLink CTinyJS::execute_function_call(bool &execute) { } // R->L: Precedence 3 (in-/decrement) ++ -- // R<-L: Precedence 4 (unary) ! ~ + - typeof void delete -CScriptVarSmartLink CTinyJS::execute_unary(bool &execute) { - CScriptVarSmartLink a; - if (t->tk=='-') { +CScriptVarLinkPtr CTinyJS::execute_unary(bool &execute) { + CScriptVarLinkPtr a; + switch(t->tk) { + case '-': t->match('-'); a = execute_unary(execute); if (execute) { CheckRightHandVar(execute, a); CScriptVarPtr zero = newScriptVar(0); - a = zero->mathsOp(a->getValue(), '-'); + a = mathsOp(execute, zero, a, '-'); } - } else if (t->tk=='+') { + break; + case '+': t->match('+'); a = execute_unary(execute); CheckRightHandVar(execute, a); - } else if (t->tk=='!') { + break; + case '!': t->match('!'); // binary not a = execute_unary(execute); if (execute) { CheckRightHandVar(execute, a); - CScriptVarPtr zero = newScriptVar(0); - a = a->getValue()->mathsOp(zero, LEX_EQUAL); + a = mathsOp(execute, a, constZero, LEX_EQUAL); } - } else if (t->tk=='~') { + break; + case '~': t->match('~'); // binary neg a = execute_unary(execute); if (execute) { CheckRightHandVar(execute, a); - CScriptVarPtr zero = newScriptVar(0); - a = a->getValue()->mathsOp(zero, '~'); + a = mathsOp(execute, a, constZero, '~'); } - } else if (t->tk==LEX_R_TYPEOF) { + break; + case LEX_R_TYPEOF: t->match(LEX_R_TYPEOF); // void a = execute_unary(execute); if (execute) { - // !!! no right-hand-check by delete - a = new CScriptVarLink(newScriptVar(a->getValue()->getVarType())); + CheckRightHandVar(execute, a); + a(newScriptVar((*a)->getVarType())); } - } else if (t->tk==LEX_R_VOID) { + break; + case LEX_R_VOID: t->match(LEX_R_VOID); // void a = execute_unary(execute); if (execute) { CheckRightHandVar(execute, a); - a = new CScriptVarLink(newScriptVar(Undefined)); + a(constScriptVar(Undefined)); } - } else if (t->tk==LEX_R_DELETE) { + break; + case LEX_R_DELETE: t->match(LEX_R_DELETE); // delete a = execute_unary(execute); if (execute) { // !!! no right-hand-check by delete - if(a->IsOwned() && a->IsDeletable()) { - a->owner->removeLink(a.getLink()); // removes the link from owner - a = new CScriptVarLink(newScriptVar(true)); + if(a->isOwned() && a->isDeletable()) { + a->getOwner()->removeLink(a); // removes the link from owner + a(constScriptVar(true)); } else - a = new CScriptVarLink(newScriptVar(false)); + a(constScriptVar(false)); } - } else if (t->tk==LEX_PLUSPLUS || t->tk==LEX_MINUSMINUS) { - int op = t->tk; - t->match(t->tk); // pre increment/decrement - a = execute_function_call(execute); - if (execute) { - if(a->IsOwned() && a->IsWritable()) - { - CScriptVarPtr one = newScriptVar(1); - CScriptVarPtr res = a->getValue()->mathsOp(one, op==LEX_PLUSPLUS ? '+' : '-'); - // in-place add/subtract - a->replaceWith(res); - a = res; + case LEX_PLUSPLUS: + case LEX_MINUSMINUS: + { + int op = t->tk; + t->match(t->tk); // pre increment/decrement + a = execute_function_call(execute); + if (execute) { + if(a->isOwned() && a->isWritable()) + { + CScriptVarPtr res = mathsOp(execute, a, constOne, op==LEX_PLUSPLUS ? '+' : '-'); + // in-place add/subtract + a->replaceWith(res); + a = res; + } } } - } else { + break; + default: a = execute_function_call(execute); + break; } // post increment/decrement if (t->tk==LEX_PLUSPLUS || t->tk==LEX_MINUSMINUS) { int op = t->tk; t->match(t->tk); if (execute) { - if(a->IsOwned() && a->IsWritable()) + if(a->isOwned() && a->isWritable()) { // TRACE("post-increment of %s and a is %sthe owner\n", a->name.c_str(), a->owned?"":"not "); - CScriptVarPtr one = newScriptVar(1); - CScriptVarPtr res = a->getVarPtr(); - CScriptVarPtr new_a = a->getValue()->mathsOp(one, op==LEX_PLUSPLUS ? '+' : '-'); + CScriptVarPtr res = a; + CScriptVarPtr new_a = mathsOp(execute, a, constOne, op==LEX_PLUSPLUS ? '+' : '-'); a->replaceWith(new_a); a = res; } @@ -3248,17 +3770,17 @@ CScriptVarSmartLink CTinyJS::execute_unary(bool &execute) { } // L->R: Precedence 5 (term) * / % -CScriptVarSmartLink CTinyJS::execute_term(bool &execute) { - CScriptVarSmartLink a = execute_unary(execute); +CScriptVarLinkPtr CTinyJS::execute_term(bool &execute) { + CScriptVarLinkPtr a = execute_unary(execute); if (t->tk=='*' || t->tk=='/' || t->tk=='%') { CheckRightHandVar(execute, a); while (t->tk=='*' || t->tk=='/' || t->tk=='%') { int op = t->tk; t->match(t->tk); - CScriptVarSmartLink b = execute_unary(execute); // L->R + CScriptVarLinkPtr b = execute_unary(execute); // L->R if (execute) { CheckRightHandVar(execute, b); - a = a->getValue()->mathsOp(b->getValue(), op); + a = mathsOp(execute, a, b, op); } } } @@ -3266,18 +3788,18 @@ CScriptVarSmartLink CTinyJS::execute_term(bool &execute) { } // L->R: Precedence 6 (addition/subtraction) + - -CScriptVarSmartLink CTinyJS::execute_expression(bool &execute) { - CScriptVarSmartLink a = execute_term(execute); +CScriptVarLinkPtr CTinyJS::execute_expression(bool &execute) { + CScriptVarLinkPtr a = execute_term(execute); if (t->tk=='+' || t->tk=='-') { CheckRightHandVar(execute, a); while (t->tk=='+' || t->tk=='-') { int op = t->tk; t->match(t->tk); - CScriptVarSmartLink b = execute_term(execute); // L->R + CScriptVarLinkPtr b = execute_term(execute); // L->R if (execute) { CheckRightHandVar(execute, b); // not in-place, so just replace - a = a->getValue()->mathsOp(b->getValue(), op); + a = mathsOp(execute, a, b, op); } } } @@ -3285,42 +3807,55 @@ CScriptVarSmartLink CTinyJS::execute_expression(bool &execute) { } // L->R: Precedence 7 (bitwise shift) << >> >>> -CScriptVarSmartLink CTinyJS::execute_binary_shift(bool &execute) { - CScriptVarSmartLink a = execute_expression(execute); +CScriptVarLinkPtr CTinyJS::execute_binary_shift(bool &execute) { + CScriptVarLinkPtr a = execute_expression(execute); if (t->tk==LEX_LSHIFT || t->tk==LEX_RSHIFT || t->tk==LEX_RSHIFTU) { CheckRightHandVar(execute, a); while (t->tk>=LEX_SHIFTS_BEGIN && t->tk<=LEX_SHIFTS_END) { int op = t->tk; t->match(t->tk); - CScriptVarSmartLink b = execute_expression(execute); // L->R + CScriptVarLinkPtr b = execute_expression(execute); // L->R if (execute) { CheckRightHandVar(execute, a); // not in-place, so just replace - a = a->getValue()->mathsOp(b->getValue(), op); + a = mathsOp(execute, a, b, op); } } } return a; } -// L->R: Precedence 8 (relational) < <= > <= in (TODO: instanceof) +// L->R: Precedence 8 (relational) < <= > <= in instanceof // L->R: Precedence 9 (equality) == != === !=== -CScriptVarSmartLink CTinyJS::execute_relation(bool &execute, int set, int set_n) { - CScriptVarSmartLink a = set_n ? execute_relation(execute, set_n, 0) : execute_binary_shift(execute); +CScriptVarLinkPtr CTinyJS::execute_relation(bool &execute, int set, int set_n) { + CScriptVarLinkPtr a = set_n ? execute_relation(execute, set_n, 0) : execute_binary_shift(execute); if ((set==LEX_EQUAL && t->tk>=LEX_RELATIONS_1_BEGIN && t->tk<=LEX_RELATIONS_1_END) - || (set=='<' && (t->tk==LEX_LEQUAL || t->tk==LEX_GEQUAL || t->tk=='<' || t->tk=='>' || t->tk == LEX_R_IN))) { + || (set=='<' && (t->tk==LEX_LEQUAL || t->tk==LEX_GEQUAL || t->tk=='<' || t->tk=='>' || t->tk == LEX_R_IN || t->tk == LEX_R_INSTANCEOF))) { CheckRightHandVar(execute, a); while ((set==LEX_EQUAL && t->tk>=LEX_RELATIONS_1_BEGIN && t->tk<=LEX_RELATIONS_1_END) - || (set=='<' && (t->tk==LEX_LEQUAL || t->tk==LEX_GEQUAL || t->tk=='<' || t->tk=='>' || t->tk == LEX_R_IN))) { + || (set=='<' && (t->tk==LEX_LEQUAL || t->tk==LEX_GEQUAL || t->tk=='<' || t->tk=='>' || t->tk == LEX_R_IN || t->tk == LEX_R_INSTANCEOF))) { int op = t->tk; t->match(t->tk); - CScriptVarSmartLink b = set_n ? execute_relation(execute, set_n, 0) : execute_binary_shift(execute); // L->R + CScriptVarLinkPtr b = set_n ? execute_relation(execute, set_n, 0) : execute_binary_shift(execute); // L->R if (execute) { CheckRightHandVar(execute, b); if(op == LEX_R_IN) { - a = new CScriptVarLink(newScriptVar( findInPrototypeChain(b->getValue(), a->getValue()->getString())!= 0 )); + a(constScriptVar( (*b)->findChildWithPrototypeChain((*a)->getString())!= 0 )); + } else if(op == LEX_R_INSTANCEOF) { + CScriptVarLink *prototype = (*b)->findChild(TINYJS_PROTOTYPE_CLASS); + if(!prototype) + throwError(execute, TypeError, "invalid 'instanceof' operand "+b->getName()); + else { + unsigned int uniqueID = getUniqueID(); + CScriptVarPtr object = (*a)->findChild(TINYJS___PROTO___VAR); + while( object && object!=prototype->getVarPtr() && object->getTempraryID() != uniqueID) { + object->setTemporaryID(uniqueID); // prevents recursions + object = object->findChild(TINYJS___PROTO___VAR); + } + a(constScriptVar(object && object==prototype->getVarPtr())); + } } else - a = a->getValue()->mathsOp(b->getValue(), op); + a = mathsOp(execute, a, b, op); } } } @@ -3330,16 +3865,16 @@ CScriptVarSmartLink CTinyJS::execute_relation(bool &execute, int set, int set_n) // L->R: Precedence 10 (bitwise-and) & // L->R: Precedence 11 (bitwise-xor) ^ // L->R: Precedence 12 (bitwise-or) | -CScriptVarSmartLink CTinyJS::execute_binary_logic(bool &execute, int op, int op_n1, int op_n2) { - CScriptVarSmartLink a = op_n1 ? execute_binary_logic(execute, op_n1, op_n2, 0) : execute_relation(execute); +CScriptVarLinkPtr CTinyJS::execute_binary_logic(bool &execute, int op, int op_n1, int op_n2) { + CScriptVarLinkPtr a = op_n1 ? execute_binary_logic(execute, op_n1, op_n2, 0) : execute_relation(execute); if (t->tk==op) { CheckRightHandVar(execute, a); while (t->tk==op) { t->match(t->tk); - CScriptVarSmartLink b = op_n1 ? execute_binary_logic(execute, op_n1, op_n2, 0) : execute_relation(execute); // L->R + CScriptVarLinkPtr b = op_n1 ? execute_binary_logic(execute, op_n1, op_n2, 0) : execute_relation(execute); // L->R if (execute) { CheckRightHandVar(execute, b); - a = a->getValue()->mathsOp(b->getValue(), op); + a = mathsOp(execute, a, b, op); } } } @@ -3347,53 +3882,53 @@ CScriptVarSmartLink CTinyJS::execute_binary_logic(bool &execute, int op, int op_ } // L->R: Precedence 13 ==> (logical-or) && // L->R: Precedence 14 ==> (logical-or) || -CScriptVarSmartLink CTinyJS::execute_logic(bool &execute, int op, int op_n) { - CScriptVarSmartLink a = op_n ? execute_logic(execute, op_n, 0) : execute_binary_logic(execute); +CScriptVarLinkPtr CTinyJS::execute_logic(bool &execute, int op /*= LEX_OROR*/, int op_n /*= LEX_ANDAND*/) { + CScriptVarLinkPtr a = op_n ? execute_logic(execute, op_n, 0) : execute_binary_logic(execute); if (t->tk==op) { - CheckRightHandVar(execute, a); - while (t->tk==op) { + if(execute) { CheckRightHandVar(execute, a); - int bop = t->tk; - t->match(t->tk); + CScriptVarPtr _a = a; + CScriptVarLinkPtr b=a; + bool _a_bool = _a->getBool(); bool shortCircuit = false; - bool boolean = false; - // if we have short-circuit ops, then if we know the outcome - // we don't bother to execute the other op. Even if not - // we need to tell mathsOp it's an & or | - if (op==LEX_ANDAND) { - bop = '&'; - shortCircuit = !a->getValue()->getBool(); - boolean = true; - } else if (op==LEX_OROR) { - bop = '|'; - shortCircuit = a->getValue()->getBool(); - boolean = true; - } - CScriptVarSmartLink b = op_n ? execute_logic(shortCircuit ? noexecute : execute, op_n, 0) : execute_binary_logic(shortCircuit ? noexecute : execute); // L->R - CheckRightHandVar(execute, b); - if (execute && !shortCircuit) { - if (boolean) { + while (t->tk==op) { + int binary_op = t->tk; + t->match(t->tk); + if (op==LEX_ANDAND) { + binary_op = '&'; + shortCircuit = !_a_bool; + } else { + binary_op = '|'; + shortCircuit = _a_bool; + } + b = op_n ? execute_logic(shortCircuit ? noexecute : execute, op_n, 0) : execute_binary_logic(shortCircuit ? noexecute : execute); // L->R + CheckRightHandVar(execute, b); + if (execute && !shortCircuit) { CheckRightHandVar(execute, b); - a = newScriptVar(a->getValue()->getBool()); - b = newScriptVar(b->getValue()->getBool()); + _a = mathsOp(execute, constScriptVar(_a_bool), constScriptVar((*b)->getBool()), binary_op); + _a_bool = _a->getBool(); } - a = a->getValue()->mathsOp(b->getValue(), bop); } - } + if (_a_bool && ( (op==LEX_ANDAND && !shortCircuit) || (op==LEX_OROR) ) ) + return b; + else + return constFalse; + } else + op_n ? execute_logic(execute, op_n, 0) : execute_binary_logic(execute); // L->R } return a; } // L<-R: Precedence 15 (condition) ?: -CScriptVarSmartLink CTinyJS::execute_condition(bool &execute) +CScriptVarLinkPtr CTinyJS::execute_condition(bool &execute) { - CScriptVarSmartLink a = execute_logic(execute); + CScriptVarLinkPtr a = execute_logic(execute); if (t->tk=='?') { CheckRightHandVar(execute, a); - t->match(t->tk); - bool cond = execute && a->getValue()->getBool(); - CScriptVarSmartLink b; + t->match('?'); + bool cond = execute && (*a)->getBool(); + CScriptVarLinkPtr b; a = execute_condition(cond ? execute : noexecute ); // L<-R // CheckRightHandVar(execute, a); t->match(':'); @@ -3406,52 +3941,63 @@ CScriptVarSmartLink CTinyJS::execute_condition(bool &execute) } // L<-R: Precedence 16 (assignment) = += -= *= /= %= <<= >>= >>>= &= |= ^= -CScriptVarSmartLink CTinyJS::execute_assignment(bool &execute) { - CScriptVarSmartLink lhs = execute_condition(execute); +CScriptVarLinkPtr CTinyJS::execute_assignment(bool &execute) { + CScriptVarLinkPtr lhs = execute_condition(execute); if (t->tk=='=' || (t->tk>=LEX_ASSIGNMENTS_BEGIN && t->tk<=LEX_ASSIGNMENTS_END) ) { int op = t->tk; CScriptTokenizer::ScriptTokenPosition leftHandPos = t->getPos(); t->match(t->tk); - CScriptVarSmartLink rhs = execute_assignment(execute); // L<-R + CScriptVarLinkPtr rhs = execute_assignment(execute); // L<-R if (execute) { - if (!lhs->IsOwned() && lhs->name.length()==0) { + bool lhs_is_accessor = lhs.getLink() != lhs.getRealLink() && lhs.getRealLink() && (*lhs.getRealLink())->isAccessor(); + if (!lhs->isOwned() && lhs->getName().empty() && !lhs_is_accessor) { throw new CScriptException("invalid assignment left-hand side", t->currentFile, leftHandPos.currentLine(), leftHandPos.currentColumn()); - } else if (op != '=' && !lhs->IsOwned()) { - throwError(execute, lhs->name + " is not defined"); + } else if (op != '=' && !lhs->isOwned() && !lhs_is_accessor) { + throwError(execute, ReferenceError, lhs->getName() + " is not defined"); } - else if(lhs->IsWritable()) { + else if(lhs->isWritable()) { if (op=='=') { - if (!lhs->IsOwned()) { + if (!lhs->isOwned() && !lhs_is_accessor) { CScriptVarLink *realLhs; - if(lhs->owner) - realLhs = lhs->owner->addChildNoDup(lhs->name, lhs); + if(lhs->isOwner()) + realLhs = lhs->getOwner()->addChildNoDup(lhs->getName(), lhs); else - realLhs = root->addChildNoDup(lhs->name, lhs); + realLhs = root->addChildNoDup(lhs->getName(), lhs); lhs = realLhs; } - lhs << rhs; - } else if (op==LEX_PLUSEQUAL) - lhs->replaceWith(lhs->getValue()->mathsOp(rhs->getValue(), '+')); - else if (op==LEX_MINUSEQUAL) - lhs->replaceWith(lhs->getValue()->mathsOp(rhs->getValue(), '-')); - else if (op==LEX_ASTERISKEQUAL) - lhs->replaceWith(lhs->getValue()->mathsOp(rhs->getValue(), '*')); - else if (op==LEX_SLASHEQUAL) - lhs->replaceWith(lhs->getValue()->mathsOp(rhs->getValue(), '/')); - else if (op==LEX_PERCENTEQUAL) - lhs->replaceWith(lhs->getValue()->mathsOp(rhs->getValue(), '%')); - else if (op==LEX_LSHIFTEQUAL) - lhs->replaceWith(lhs->getValue()->mathsOp(rhs->getValue(), LEX_LSHIFT)); - else if (op==LEX_RSHIFTEQUAL) - lhs->replaceWith(lhs->getValue()->mathsOp(rhs->getValue(), LEX_RSHIFT)); - else if (op==LEX_RSHIFTUEQUAL) - lhs->replaceWith(lhs->getValue()->mathsOp(rhs->getValue(), LEX_RSHIFTU)); - else if (op==LEX_ANDEQUAL) - lhs->replaceWith(lhs->getValue()->mathsOp(rhs->getValue(), '&')); - else if (op==LEX_OREQUAL) - lhs->replaceWith(lhs->getValue()->mathsOp(rhs->getValue(), '|')); - else if (op==LEX_XOREQUAL) - lhs->replaceWith(lhs->getValue()->mathsOp(rhs->getValue(), '^')); + lhs.replaceVar(execute, rhs); + return rhs->getVarPtr(); + } else { + CScriptVarPtr result; + static int assignments[] = {'+', '-', '*', '/', '%', LEX_LSHIFT, LEX_RSHIFT, LEX_RSHIFTU, '&', '|', '^'}; + result = mathsOp(execute, lhs, rhs, assignments[op-LEX_PLUSEQUAL]); +/* + if (op==LEX_PLUSEQUAL) + result = mathsOp(execute, lhs, rhs, '+'); + else if (op==LEX_MINUSEQUAL) + result = mathsOp(execute, lhs, rhs, '-'); + else if (op==LEX_ASTERISKEQUAL) + result = mathsOp(execute, lhs, rhs, '*'); + else if (op==LEX_SLASHEQUAL) + result = mathsOp(execute, lhs, rhs, '/'); + else if (op==LEX_PERCENTEQUAL) + result = mathsOp(execute, lhs, rhs, '%'); + else if (op==LEX_LSHIFTEQUAL) + result = mathsOp(execute, lhs, rhs, LEX_LSHIFT); + else if (op==LEX_RSHIFTEQUAL) + result = mathsOp(execute, lhs, rhs, LEX_RSHIFT); + else if (op==LEX_RSHIFTUEQUAL) + result = mathsOp(execute, lhs, rhs, LEX_RSHIFTU); + else if (op==LEX_ANDEQUAL) + result = mathsOp(execute, lhs, rhs, '&'); + else if (op==LEX_OREQUAL) + result = mathsOp(execute, lhs, rhs, '|'); + else if (op==LEX_XOREQUAL) + result = mathsOp(execute, lhs, rhs, '^'); +*/ + lhs->replaceWith(result); + return result; + } } } } @@ -3460,8 +4006,8 @@ CScriptVarSmartLink CTinyJS::execute_assignment(bool &execute) { return lhs; } // L->R: Precedence 17 (comma) , -CScriptVarSmartLink CTinyJS::execute_base(bool &execute) { - CScriptVarSmartLink a; +CScriptVarLinkPtr CTinyJS::execute_base(bool &execute) { + CScriptVarLinkPtr a; for(;;) { a = execute_assignment(execute); // L->R @@ -3473,32 +4019,34 @@ CScriptVarSmartLink CTinyJS::execute_base(bool &execute) { return a; } void CTinyJS::execute_block(bool &execute) { - if(t->tk == '{') { - if(execute) { - t->match('{'); - CScopeControl ScopeControl(this); + if(execute) { + t->match('{'); + CScopeControl ScopeControl(this); + if(scope()->scopeLet() != scope()) // add a LetScope only if needed ScopeControl.addLetScope(); - while (t->tk && t->tk!='}') - execute_statement(execute); - t->match('}'); - // scopes.pop_back(); - } - else - t->skip(t->getToken().Int()); + while (t->tk && t->tk!='}') + execute_statement(execute); + t->match('}'); + // scopes.pop_back(); } + else + t->skip(t->getToken().Int()); } -CScriptVarSmartLink CTinyJS::execute_statement(bool &execute) { - CScriptVarSmartLink ret; - if (t->tk=='{') { - /* A block of code */ +CScriptVarLinkPtr CTinyJS::execute_statement(bool &execute) { + CScriptVarLinkPtr ret; + switch(t->tk) { + case '{': /* A block of code */ execute_block(execute); - } else if (t->tk==';') { - /* Empty statement - to allow things like ;;; */ + break; + case ';': /* Empty statement - to allow things like ;;; */ t->match(';'); - } else if (t->tk==LEX_ID) { + break; + case LEX_ID: ret = execute_base(execute); t->match(';'); - } else if (t->tk==LEX_R_VAR || t->tk==LEX_R_LET) { + break; + case LEX_R_VAR: + case LEX_R_LET: if(execute) { bool let = t->tk==LEX_R_LET, let_ext=false; @@ -3506,21 +4054,20 @@ CScriptVarSmartLink CTinyJS::execute_statement(bool &execute) { CScopeControl ScopeControl(this); if(let && t->tk=='(') { let_ext = true; - t->match(t->tk); + t->match('('); ScopeControl.addLetScope(); } CScriptVarPtr in_scope = let ? scope()->scopeLet() : scope()->scopeVar(); for(;;) { - CScriptVarSmartLink a; + CScriptVarLinkPtr a; string var = t->tkStr(); t->match(LEX_ID); a = in_scope->findChildOrCreate(var); - a->SetDeletable(false); + a->setDeletable(false); // sort out initialiser if (t->tk == '=') { t->match('='); - CScriptVarSmartLink var = execute_assignment(execute); - a << var; + a.replaceVar(execute, execute_assignment(execute)); } if (t->tk == ',') t->match(','); @@ -3534,22 +4081,24 @@ CScriptVarSmartLink CTinyJS::execute_statement(bool &execute) { t->match(';'); } else t->skip(t->getToken().Int()); - } else if (t->tk==LEX_R_WITH) { + break; + case LEX_R_WITH: if(execute) { t->match(LEX_R_WITH); t->match('('); - CScriptVarSmartLink var = execute_base(execute); + CScriptVarLinkPtr var = execute_base(execute); t->match(')'); CScopeControl ScopeControl(this); ScopeControl.addWithScope(var); ret = execute_statement(execute); } else t->skip(t->getToken().Int()); - } else if (t->tk==LEX_R_IF) { + break; + case LEX_R_IF: if(execute) { t->match(LEX_R_IF); t->match('('); - bool cond = execute_base(execute)->getValue()->getBool(); + bool cond = (*execute_base(execute))->getBool(); t->match(')'); if(cond && execute) { t->match(LEX_T_SKIP); @@ -3568,110 +4117,105 @@ CScriptVarSmartLink CTinyJS::execute_statement(bool &execute) { } } else t->skip(t->getToken().Int()); - } else if (t->tk==LEX_R_DO) { + break; + case LEX_R_DO: if(execute) { t->match(LEX_R_DO); + STRING_VECTOR_t myLabels; + while(t->tk == LEX_T_LOOP_LABEL) { + myLabels.push_back(t->tkStr()); + t->match(LEX_T_LOOP_LABEL); + } CScriptTokenizer::ScriptTokenPosition loopStart = t->getPos(); - int old_loop_runtimeFlags = runtimeFlags & RUNTIME_LOOP_MASK; - runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | RUNTIME_CANBREAK | RUNTIME_CANCONTINUE; bool loopCond = true; - int loopCount = TINYJS_LOOP_MAX_ITERATIONS; - while (loopCond && execute && loopCount-->0) { + while (loopCond && execute) { t->setPos(loopStart); execute_statement(execute); if(!execute) { - // break or continue - if(runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE)) - { + bool Continue = false; + if(runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE) + && + (label.empty() || find(myLabels.begin(), myLabels.end(), label) != myLabels.end())) { + label.clear(); execute = true; - bool Break = (runtimeFlags & RUNTIME_BREAK) != 0; + Continue = (runtimeFlags & RUNTIME_CONTINUE) != 0; runtimeFlags &= ~(RUNTIME_BREAK | RUNTIME_CONTINUE); - if(Break) { - t->match(LEX_R_WHILE); - t->check('('); - t->skip(t->getToken().Int()); - t->match(';'); - break; - } } - // other stuff e.g return, throw + if(!Continue) { + t->skip(t->getToken().Int()); + break; + } } t->match(LEX_R_WHILE); - if(execute) { - t->match('('); - loopCond = execute_base(execute)->getValue()->getBool(); - t->match(')'); - } else { - t->check('('); - t->skip(t->getToken().Int()); - } + t->match('('); + loopCond = (*execute_base(execute))->getBool(); + t->match(')'); t->match(';'); } - runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | old_loop_runtimeFlags; - if (loopCount<=0) { - ostringstream errorString; - errorString << "'for'-Loop exceeded " << TINYJS_LOOP_MAX_ITERATIONS << " iterations"; - throw new CScriptException(errorString.str(), t->currentFile, loopStart.currentLine(), loopStart.currentColumn()); - } } else t->skip(t->getToken().Int()); - } else if (t->tk==LEX_R_WHILE) { + break; + case LEX_R_WHILE: if(execute) { t->match(LEX_R_WHILE); + STRING_VECTOR_t myLabels; + while(t->tk == LEX_T_LOOP_LABEL) { + myLabels.push_back(t->tkStr()); + t->match(LEX_T_LOOP_LABEL); + } bool loopCond; t->match('('); CScriptTokenizer::ScriptTokenPosition condStart = t->getPos(); - loopCond = execute_base(execute)->getValue()->getBool(); + loopCond = (*execute_base(execute))->getBool(); t->match(')'); if(loopCond && execute) { t->match(LEX_T_SKIP); CScriptTokenizer::ScriptTokenPosition loopStart = t->getPos(); CScriptTokenizer::ScriptTokenPosition loopEnd = loopStart; - int old_loop_runtimeFlags = runtimeFlags & RUNTIME_LOOP_MASK; - runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | RUNTIME_CANBREAK | RUNTIME_CANCONTINUE; - int loopCount = TINYJS_LOOP_MAX_ITERATIONS; - while (loopCond && execute && loopCount-->0) { + while (loopCond && execute) { t->setPos(loopStart); execute_statement(execute); if(loopEnd == loopStart) // first loop-pass loopEnd = t->getPos(); if(!execute) { - // break or continue - if(runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE)) - { + bool Continue = false; + if(runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE) + && + (label.empty() || find(myLabels.begin(), myLabels.end(), label) != myLabels.end())) { + label.clear(); execute = true; - bool Break = (runtimeFlags & RUNTIME_BREAK) != 0; + Continue = (runtimeFlags & RUNTIME_CONTINUE) != 0; runtimeFlags &= ~(RUNTIME_BREAK | RUNTIME_CONTINUE); - if(Break) break; } - // other stuff e.g return, throw + if(!Continue) break; } if(execute) { t->setPos(condStart); - loopCond = execute_base(execute)->getValue()->getBool(); + loopCond = (*execute_base(execute))->getBool(); } } t->setPos(loopEnd); - runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | old_loop_runtimeFlags; - if (loopCount<=0) { - ostringstream errorString; - errorString << "'for'-Loop exceeded " << TINYJS_LOOP_MAX_ITERATIONS << " iterations"; - throw new CScriptException(errorString.str(), t->currentFile, loopStart.currentLine(), loopStart.currentColumn()); - } } else { t->check(LEX_T_SKIP); t->skip(t->getToken().Int()); } } else t->skip(t->getToken().Int()); - } else if (t->tk==LEX_T_FOR_IN || t->tk == LEX_T_FOR_EACH_IN) { + break; + case LEX_T_FOR_IN: + case LEX_T_FOR_EACH_IN: if(execute) { bool for_each = t->tk == LEX_T_FOR_EACH_IN; t->match(t->tk); + STRING_VECTOR_t myLabels; + while(t->tk == LEX_T_LOOP_LABEL) { + myLabels.push_back(t->tkStr()); + t->match(LEX_T_LOOP_LABEL); + } t->match('('); - CScriptVarSmartLink for_var; - CScriptVarSmartLink for_in_var; + CScriptVarLinkPtr for_var; + CScriptVarLinkPtr for_in_var; CScopeControl ScopeControl(this); ScopeControl.addLetScope(); @@ -3690,52 +4234,57 @@ CScriptVarSmartLink CTinyJS::execute_statement(bool &execute) { for_in_var = execute_function_call(execute); CheckRightHandVar(execute, for_in_var); t->match(')'); - if( (*for_in_var)->Childs.size() ) { - if(!for_var->IsOwned()) { + STRING_SET_t keys; + (*for_in_var)->keys(keys, true, getUniqueID()); + if( keys.size() ) { + if(!for_var->isOwned()) { CScriptVarLink *real_for_var; - if(for_var->owner) - real_for_var = for_var->owner->addChildNoDup(for_var->name, for_var); + if(for_var->isOwner()) + real_for_var = for_var->getOwner()->addChildNoDup(for_var->getName(), for_var); else - real_for_var = root->addChildNoDup(for_var->name, for_var); + real_for_var = root->addChildNoDup(for_var->getName(), for_var); for_var = real_for_var; } CScriptTokenizer::ScriptTokenPosition loopStart = t->getPos(); - int old_loop_runtimeFlags = runtimeFlags & RUNTIME_LOOP_MASK; - runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | RUNTIME_CANBREAK | RUNTIME_CANCONTINUE; - for(SCRIPTVAR_CHILDS_it it = (*for_in_var)->Childs.begin(); execute && it != (*for_in_var)->Childs.end(); ++it) { - if (for_each) - for_var->replaceWith(*it); - else - for_var->replaceWith(newScriptVar((*it)->name)); + for(STRING_SET_it it = keys.begin(); execute && it != keys.end(); ++it) { + CScriptVarLink *link = for_var.getLink(); + if(link) { + if (for_each) + link->replaceWith((*for_in_var)->findChildWithPrototypeChain(*it)); + else + link->replaceWith(newScriptVar(*it)); + } else ASSERT(0); t->setPos(loopStart); execute_statement(execute); - if(!execute) - { - // break or continue - if(runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE)) - { + if(!execute) { + bool Continue = false; + if(runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE) + && + (label.empty() || find(myLabels.begin(), myLabels.end(), label) != myLabels.end())) { + label.clear(); execute = true; - bool Break = (runtimeFlags & RUNTIME_BREAK)!=0; + Continue = (runtimeFlags & RUNTIME_CONTINUE)!=0; runtimeFlags &= ~(RUNTIME_BREAK | RUNTIME_CONTINUE); - if(Break) break; } - // other stuff e.g return, throw + if(!Continue) break; } } - runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | old_loop_runtimeFlags; - } else { + } else execute_statement(noexecute); - } - } else { + } else t->skip(t->getToken().Int()); - execute_statement(execute); - } - } else if (t->tk==LEX_R_FOR) { + break; + case LEX_R_FOR: if(execute) { t->match(LEX_R_FOR); + STRING_VECTOR_t myLabels; + while(t->tk == LEX_T_LOOP_LABEL) { + myLabels.push_back(t->tkStr()); + t->match(LEX_T_LOOP_LABEL); + } t->match('('); CScopeControl ScopeControl(this); if(t->tk == LEX_R_LET) @@ -3746,7 +4295,7 @@ CScriptVarSmartLink CTinyJS::execute_statement(bool &execute) { bool loopCond = execute; // Empty Condition -->always true if(t->tk != ';') { cond_empty = false; - loopCond = execute && execute_base(execute)->getValue()->getBool(); + loopCond = execute && (*execute_base(execute))->getBool(); } t->match(';'); CScriptTokenizer::ScriptTokenPosition iterStart = t->getPos(); @@ -3761,23 +4310,22 @@ CScriptVarSmartLink CTinyJS::execute_statement(bool &execute) { CScriptTokenizer::ScriptTokenPosition loopStart = t->getPos(); CScriptTokenizer::ScriptTokenPosition loopEnd = t->getPos(); - int old_loop_runtimeFlags = runtimeFlags & RUNTIME_LOOP_MASK; - runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | RUNTIME_CANBREAK | RUNTIME_CANCONTINUE; - int loopCount = TINYJS_LOOP_MAX_ITERATIONS; - while (loopCond && execute && loopCount-->0) { + while (loopCond && execute) { t->setPos(loopStart); execute_statement(execute); if(loopEnd == loopStart) // first loop-pass loopEnd = t->getPos(); if(!execute) { - // break or continue - if(runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE)) { + bool Continue; + if(runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE) + && + (label.empty() || find(myLabels.begin(), myLabels.end(), label) != myLabels.end())) { + label.clear(); execute = true; - bool Break = (runtimeFlags & RUNTIME_BREAK)!=0; + Continue = (runtimeFlags & RUNTIME_CONTINUE)!=0; runtimeFlags &= ~(RUNTIME_BREAK | RUNTIME_CONTINUE); - if(Break) break; } - // other stuff e.g return, throw + if(!Continue) break; } if(execute) { if(!iter_empty) { @@ -3786,142 +4334,150 @@ CScriptVarSmartLink CTinyJS::execute_statement(bool &execute) { } if(!cond_empty) { t->setPos(conditionStart); - loopCond = execute_base(execute)->getValue()->getBool(); + loopCond = (*execute_base(execute))->getBool(); } } } t->setPos(loopEnd); - runtimeFlags = (runtimeFlags & ~RUNTIME_LOOP_MASK) | old_loop_runtimeFlags; - if (loopCount<=0) { - ostringstream errorString; - errorString << "'for'-Loop exceeded " << TINYJS_LOOP_MAX_ITERATIONS << " iterations"; - throw new CScriptException(errorString.str(), t->currentFile, loopStart.currentLine(), loopStart.currentColumn()); - } - } else { + } else execute_statement(noexecute); - } } else t->skip(t->getToken().Int()); - } else if (t->tk==LEX_R_BREAK) { - t->match(LEX_R_BREAK); - if(execute) { - if(runtimeFlags & RUNTIME_CANBREAK) - { - runtimeFlags |= RUNTIME_BREAK; - execute = false; - } - else - throw new CScriptException("'break' must be inside loop or switch", t->currentFile, t->currentLine(), t->currentColumn()); - } - t->match(';'); - } else if (t->tk==LEX_R_CONTINUE) { - t->match(LEX_R_CONTINUE); + break; + case LEX_R_BREAK: + case LEX_R_CONTINUE: if(execute) { - if(runtimeFlags & RUNTIME_CANCONTINUE) - { - runtimeFlags |= RUNTIME_CONTINUE; - execute = false; + runtimeFlags |= t->tk==LEX_R_BREAK ? RUNTIME_BREAK : RUNTIME_CONTINUE; + execute = false; + t->match(t->tk); + if(t->tk == LEX_ID) { + if(execute) label = t->tkStr(); + t->match(LEX_ID); } - else - throw new CScriptException("'continue' must be inside loop or switch", t->currentFile, t->currentLine(), t->currentColumn()); - } - t->match(';'); - } else if (t->tk==LEX_R_RETURN) { + t->match(';'); + } else + t->skip(t->getToken().Int()); + break; + case LEX_R_RETURN: if (execute) { - if (IS_RUNTIME_CANRETURN) { - t->match(LEX_R_RETURN); - CScriptVarPtr result; - if (t->tk != ';') - result = execute_base(execute)->getValue(); - if(result) scope()->scopeVar()->setReturnVar(result); - } else - throw new CScriptException("'return' statement, but not in a function.", t->currentFile, t->currentLine(), t->currentColumn()); - execute = false; - } - else { t->match(LEX_R_RETURN); + CScriptVarPtr result; if (t->tk != ';') - execute_base(execute); - } - t->match(';'); - } else if (t->tk == LEX_T_FUNCTION_OPERATOR) { - // ignore force anonymous at statement-level - ASSERT(0); // no functions-operator statement-level - t->match(t->tk); - } else if (t->tk == LEX_R_FUNCTION) { + result = execute_base(execute); + t->match(';'); + if(result) scope()->scopeVar()->addChildNoDup(TINYJS_RETURN_VAR, result); + execute = false; + } else + t->skip(t->getToken().Int()); + break; + case LEX_R_FUNCTION: if(execute) { CScriptTokenDataFnc &Fnc = t->getToken().Fnc(); if(!Fnc.name.length()) throw new CScriptException("Functions defined at statement-level are meant to have a name."); else { - CScriptVarSmartLink funcVar = parseFunctionDefinition(t->getToken()); - scope()->scopeVar()->addChildNoDup(funcVar->name, funcVar)->SetDeletable(false); + CScriptVarLinkPtr funcVar = parseFunctionDefinition(t->getToken()); + scope()->scopeVar()->addChildNoDup(funcVar->getName(), funcVar)->setDeletable(false); } } - t->match(t->tk); - } else if (t->tk==LEX_R_TRY) { - t->match(LEX_R_TRY); - bool old_execute = execute; - // save runtimeFlags - int old_throw_runtimeFlags = runtimeFlags & RUNTIME_THROW_MASK; - // set runtimeFlags - runtimeFlags = (runtimeFlags & ~RUNTIME_THROW_MASK) | RUNTIME_CANTHROW; - - execute_block(execute); -//try{print("try1");try{ print("try2");try{print("try3");throw 3;}catch(a){print("catch3");throw 3;}finally{print("finalli3");}print("after3");}catch(a){print("catch2");}finally{print("finalli2");}print("after2");}catch(a){print("catch1");}finally{print("finalli1");}print("after1"); - bool isThrow = (runtimeFlags & RUNTIME_THROW) != 0; - if(isThrow) execute = old_execute; - - // restore runtimeFlags - runtimeFlags = (runtimeFlags & ~RUNTIME_THROW_MASK) | old_throw_runtimeFlags; + t->match(LEX_R_FUNCTION); + break; + case LEX_R_TRY: + if(execute) { + t->match(LEX_R_TRY); + STRING_VECTOR_t myLabels; + while(t->tk == LEX_T_LOOP_LABEL) { + myLabels.push_back(t->tkStr()); + t->match(LEX_T_LOOP_LABEL); + } + // save runtimeFlags + int old_throw_runtimeFlags = runtimeFlags & RUNTIME_THROW_MASK; + // set runtimeFlags + runtimeFlags = runtimeFlags | RUNTIME_CAN_THROW; + + execute_block(execute); + CScriptVarPtr exceptionVar = this->exceptionVar; // remember exceptionVar + this->exceptionVar = CScriptVarPtr(); // clear exeptionVar; + + bool isThrow = (runtimeFlags & RUNTIME_THROW) != 0; + if(isThrow) execute = true; + if(runtimeFlags & RUNTIME_BREAK && find(myLabels.begin(), myLabels.end(), label) != myLabels.end()) { + label.clear(); + execute = true; + runtimeFlags &= ~RUNTIME_BREAK; + } + // restore runtimeFlags + runtimeFlags = (runtimeFlags & ~RUNTIME_THROW_MASK) | old_throw_runtimeFlags; - if(t->tk != LEX_R_FINALLY) // expect catch - { - t->match(LEX_R_CATCH); - t->match('('); - string exception_var_name = t->tkStr(); - t->match(LEX_ID); - t->match(')'); - if(isThrow) { - CScriptVarScopeFncPtr catchScope(::newScriptVar(this, ScopeFnc, (CScriptVar*)0)); - catchScope->addChild(exception_var_name, exceptionVar); - CScopeControl ScopeControl(this); - ScopeControl.addFncScope(catchScope); - execute_block(execute); - } else { - execute_block(noexecute); + while(t->tk == LEX_R_CATCH) // expect catch, finally + { + if(execute && isThrow) { // when a catch-block is found or no throw thens skip all followed + t->match(LEX_R_CATCH); + t->match('('); + string exception_var_name = t->tkStr(); + t->match(LEX_ID); + CScopeControl ScopeControl(this); + ScopeControl.addLetScope(); + scope()->scopeLet()->addChild(exception_var_name, exceptionVar); + bool condition = true; + if(t->tk == LEX_R_IF) { + t->match(LEX_R_IF); + condition = (*execute_base(execute))->getPrimitivVar(execute)->getBool(); + } + t->match(')'); + if(execute && condition) { + isThrow = false; + execute_block(execute); + } else + execute_block(noexecute); + } else + t->skip(t->getToken().Int()); } - } - if(t->tk == LEX_R_FINALLY) { - t->match(LEX_R_FINALLY); - bool finally_execute = true; - execute_block(isThrow ? finally_execute : execute); - } - } else if (t->tk==LEX_R_THROW) { - CScriptTokenizer::ScriptTokenPosition tokenPos = t->getPos(); -// int tokenStart = t->getToken().pos; - t->match(LEX_R_THROW); - CScriptVarPtr a = execute_base(execute)->getValue(); + if(t->tk == LEX_R_FINALLY) { + t->match(LEX_R_FINALLY); + bool finally_execute = true; // alway execute finally-block + execute_block(finally_execute); + } + if(isThrow && (runtimeFlags & RUNTIME_THROW)==0) { // no catch-block found and no new exception + if(runtimeFlags & RUNTIME_CAN_THROW) { // throw again + runtimeFlags |= RUNTIME_THROW; + execute = false; + this->exceptionVar = exceptionVar; + } else + throw new CScriptException("uncaught exception: ", t->currentFile, t->currentLine(), t->currentColumn()); + } + } else + t->skip(t->getToken().Int()); + break; + case LEX_R_THROW: if(execute) { - if(runtimeFlags & RUNTIME_CANTHROW) { - runtimeFlags |= RUNTIME_THROW; - execute = false; - exceptionVar = a; + CScriptTokenizer::ScriptTokenPosition tokenPos = t->getPos(); + // int tokenStart = t->getToken().pos; + t->match(LEX_R_THROW); + CScriptVarPtr a = execute_base(execute); + if(execute) { + if(runtimeFlags & RUNTIME_CAN_THROW) { + runtimeFlags |= RUNTIME_THROW; + execute = false; + exceptionVar = a; + } + else + throw new CScriptException("uncaught exception: '"+a->getString()+"'", t->currentFile, tokenPos.currentLine(), tokenPos.currentColumn()); } - else - throw new CScriptException("uncaught exception: '"+a->getString()+"'", t->currentFile, tokenPos.currentLine(), tokenPos.currentColumn()); - } - } else if (t->tk == LEX_R_SWITCH) { + } else + t->skip(t->getToken().Int()); + break; + case LEX_R_SWITCH: if(execute) { t->match(LEX_R_SWITCH); t->match('('); - CScriptVarPtr SwitchValue = execute_base(execute)->getValue(); + CScriptVarPtr SwitchValue = execute_base(execute); t->match(')'); if(execute) { // save runtimeFlags int old_switch_runtimeFlags = runtimeFlags & RUNTIME_BREAK_MASK; // set runtimeFlags - runtimeFlags = (runtimeFlags & ~RUNTIME_BREAK_MASK) | RUNTIME_CANBREAK; + runtimeFlags = (runtimeFlags & ~RUNTIME_BREAK_MASK) | RUNTIME_CAN_BREAK; bool found = false ,execute = false; t->match('{'); CScopeControl ScopeControl(this); @@ -3930,7 +4486,8 @@ CScriptVarSmartLink CTinyJS::execute_statement(bool &execute) { CScriptTokenizer::ScriptTokenPosition defaultStart = t->getPos(); bool hasDefault = false; while (t->tk) { - if(t->tk == LEX_R_CASE) { + switch(t->tk) { + case LEX_R_CASE: if(found) { t->skip(t->getToken().Int()); if(execute) t->match(':'); @@ -3938,9 +4495,9 @@ CScriptVarSmartLink CTinyJS::execute_statement(bool &execute) { } else { t->match(LEX_R_CASE); execute = true; - CScriptVarSmartLink CaseValue = execute_base(execute); + CScriptVarLinkPtr CaseValue = execute_base(execute); if(execute) { - CaseValue = CaseValue->getValue()->mathsOp(SwitchValue, LEX_EQUAL); + CaseValue = mathsOp(execute, CaseValue, SwitchValue, LEX_EQUAL); found = execute = (*CaseValue)->getBool(); if(found) t->match(':'); else t->skip(t->getToken().Int()); @@ -3949,7 +4506,8 @@ CScriptVarSmartLink CTinyJS::execute_statement(bool &execute) { t->skip(t->getToken().Int()); } } - } else if(t->tk == LEX_R_DEFAULT) { + break; + case LEX_R_DEFAULT: t->match(LEX_R_DEFAULT); if(found) { if(execute) t->match(':'); @@ -3959,16 +4517,21 @@ CScriptVarSmartLink CTinyJS::execute_statement(bool &execute) { defaultStart = t->getPos(); t->skip(t->getToken().Int()); } - } else if(t->tk == '}') { + break; + case '}': if(!found && hasDefault) { found = execute = true; t->setPos(defaultStart); t->match(':'); } else - break; - } else + goto end_while; // goto isn't fine but C supports no "break lable;" + break; + default: execute_statement(execute); + break; + } } +end_while: t->match('}'); if(!found || (runtimeFlags & RUNTIME_BREAK) ) execute = true; @@ -3980,15 +4543,38 @@ CScriptVarSmartLink CTinyJS::execute_statement(bool &execute) { execute_block(execute); } else t->skip(t->getToken().Int()); - } else if(t->tk == LEX_T_LABEL) { - t->match(LEX_T_LABEL); // ignore Labels + break; + case LEX_T_DUMMY_LABEL: + t->match(LEX_T_DUMMY_LABEL); // ignore dummy Label t->match(':'); - } else if(t->tk != LEX_EOF) { + break; + case LEX_T_LABEL: + { + string Label = t->tkStr(); + t->match(LEX_T_LABEL); + t->match(':'); + if(execute) { + execute_statement(execute); + if(!execute) { + if(runtimeFlags & (RUNTIME_BREAK) && Label == label) { // break this label + runtimeFlags &= ~RUNTIME_BREAK; + execute = true; + } + } + } + else + execute_statement(noexecute); + } + break; + case LEX_EOF: + t->match(LEX_EOF); + break; + default: /* Execute a simple statement that only contains basic arithmetic... */ ret = execute_base(execute); t->match(';'); - } else - t->match(LEX_EOF); + break; + } return ret; } @@ -3996,162 +4582,179 @@ CScriptVarSmartLink CTinyJS::execute_statement(bool &execute) { /// Finds a child, looking recursively up the scopes CScriptVarLink *CTinyJS::findInScopes(const string &childName) { return scope()->findInScopes(childName); -/* - CScriptVarLink *v = scopes.back()->findChild(childName); - if(!v && scopes.front() != scopes.back()) - v = scopes.front()->findChild(childName); - return v; -*/ -} - -/// Get all Keynames of en given object (optionial look up the prototype-chain) -void CTinyJS::keys(STRING_VECTOR_t &Keys, CScriptVarPtr object, bool WithPrototypeChain) { - CScriptVarLink *__proto__; - for(SCRIPTVAR_CHILDS_it it = object->Childs.begin(); it != object->Childs.end(); ++it) { - if((*it)->IsEnumerable()) - Keys.push_back((*it)->name); - - if (WithPrototypeChain) { - if( (__proto__ = (*(*it))->findChild(TINYJS___PROTO___VAR)) ) - keys(Keys, __proto__, WithPrototypeChain); - else if((*(*it))->isString()) - keys(Keys, stringPrototype, WithPrototypeChain); - else if((*(*it))->isArray()) - keys(Keys, arrayPrototype, WithPrototypeChain); - else if((*(*it))->isFunction()) - keys(Keys, functionPrototype, WithPrototypeChain); - else - keys(Keys, objectPrototype, false); - } - } -} -/// Look up in any parent classes of the given object -CScriptVarLink *CTinyJS::findInPrototypeChain(CScriptVarPtr object, const string &name) { - // Look for links to actual parent classes - CScriptVarLink *__proto__; -// CScriptVar *_object = object; - while( (__proto__ = object->findChild(TINYJS___PROTO___VAR)) ) { - CScriptVarLink *implementation = (*__proto__)->findChild(name); - if (implementation){ - return implementation; - } - object = __proto__->getVarPtr(); - } -/* - if (object->isString()) { - CScriptVarLink *implementation = stringPrototype->findChild(name); - if (implementation) return implementation; - } - if (object->isArray()) { - CScriptVarLink *implementation = arrayPrototype->findChild(name); - if (implementation) return implementation; - } - if (object->isFunction()) { - CScriptVarLink *implementation = functionPrototype->findChild(name); - if (implementation) return implementation; - } - CScriptVarLink *implementation = objectPrototype->findChild(name); - if (implementation) return implementation; -*/ - return 0; } -////////////////////////////////////////////////////////////////////////// native Object +////////////////////////////////////////////////////////////////////////// +/// Object +////////////////////////////////////////////////////////////////////////// void CTinyJS::native_Object(const CFunctionsScopePtr &c, void *data) { - CScriptVarPtr objc = c->getParameter(0); + CScriptVarPtr objc = c->getArgument(0); if(objc->isUndefined() || objc->isNull()) c->setReturnVar(newScriptVar(Object)); - else + else if(objc->isPrimitive()) { + c->setReturnVar(newScriptVar(ObjectWrap, objc)); + } else c->setReturnVar(objc); } void CTinyJS::native_Object_hasOwnProperty(const CFunctionsScopePtr &c, void *data) { - c->setReturnVar(c->newScriptVar(c->getParameter("this")->findChild(c->getParameter("prop")->getString()) != 0)); + c->setReturnVar(c->constScriptVar(c->getArgument("this")->findChild(c->getArgument("prop")->getString()) != 0)); +} +void CTinyJS::native_Object_getPrototypeOf(const CFunctionsScopePtr &c, void *data) { + if(c->getArgumentsLength()>=1) { + c->setReturnVar(c->getArgument(0)->findChild(TINYJS___PROTO___VAR)); + } + // TODO throw error +} +void CTinyJS::native_Object_valueOf(const CFunctionsScopePtr &c, void *data) { + bool execute = true; + c->setReturnVar(c->getArgument("this")->_valueOf(execute)); +} +void CTinyJS::native_Object_toString(const CFunctionsScopePtr &c, void *data) { + bool execute = true; + int radix = 10; + if(c->getArgumentsLength()>=1) radix = c->getArgument("radix")->getInt(); + c->setReturnVar(c->getArgument("this")->_toString(execute, radix)); } -////////////////////////////////////////////////////////////////////////// native Array +////////////////////////////////////////////////////////////////////////// +/// Array +////////////////////////////////////////////////////////////////////////// void CTinyJS::native_Array(const CFunctionsScopePtr &c, void *data) { // CScriptVar *returnVar = new CScriptVarArray(c->getContext()); CScriptVarPtr returnVar = c->newScriptVar(Array); c->setReturnVar(returnVar); - int length = c->getParameterLength(); - if(length == 1 && c->getParameter(0)->isNumber()) - returnVar->setArrayIndex(c->getParameter(0)->getInt(), newScriptVar(Undefined)); + int length = c->getArgumentsLength(); + if(length == 1 && c->getArgument(0)->isNumber()) + returnVar->setArrayIndex(c->getArgument(0)->getInt(), constScriptVar(Undefined)); else for(int i=0; isetArrayIndex(i, c->getParameter(i)); + returnVar->setArrayIndex(i, c->getArgument(i)); } -////////////////////////////////////////////////////////////////////////// native String +////////////////////////////////////////////////////////////////////////// +/// String +////////////////////////////////////////////////////////////////////////// void CTinyJS::native_String(const CFunctionsScopePtr &c, void *data) { - if(c->getParameterLength()==0) - c->setReturnVar(c->newScriptVar("")); + CScriptVarPtr arg; + if(c->getArgumentsLength()==0) + arg = newScriptVar(""); else - c->setReturnVar(c->newScriptVar(c->getParameter(0)->getString())); + arg = newScriptVar(c->getArgument(0)->getString()); + CScriptVarLink *This = c->findChild("this"); + This->replaceWith(newScriptVar(ObjectWrap, arg)); + c->setReturnVar(arg); } -////////////////////////////////////////////////////////////////////////// native Number +////////////////////////////////////////////////////////////////////////// +/// Number +////////////////////////////////////////////////////////////////////////// void CTinyJS::native_Number(const CFunctionsScopePtr &c, void *data) { - if(c->getParameterLength()==0) - c->setReturnVar(c->newScriptVar(0)); + CScriptVarPtr arg; + if(c->getArgumentsLength()==0) + arg = newScriptVar(0); + else + arg = c->getArgument(0)->getNumericVar(); + CScriptVarLink *This = c->findChild("this"); + This->replaceWith(newScriptVar(ObjectWrap, arg)); + c->setReturnVar(arg); +} + +////////////////////////////////////////////////////////////////////////// +/// Boolean +////////////////////////////////////////////////////////////////////////// + +void CTinyJS::native_Boolean(const CFunctionsScopePtr &c, void *data) { + CScriptVarPtr arg; + if(c->getArgumentsLength()==0) + arg = constScriptVar(false); else - c->setReturnVar(c->getParameter(0)->getNumericVar()); + arg = constScriptVar(c->getArgument(0)->getBool()); + CScriptVarLink *This = c->findChild("this"); + This->replaceWith(newScriptVar(ObjectWrap, arg)); + c->setReturnVar(arg); } -////////////////////////////////////////////////////////////////////////// native Function +////////////////////////////////////////////////////////////////////////// +/// Function +////////////////////////////////////////////////////////////////////////// void CTinyJS::native_Function(const CFunctionsScopePtr &c, void *data) { - int length = c->getParameterLength(); + int length = c->getArgumentsLength(); string params, body; if(length>=1) - body = c->getParameter(length-1)->getString(); + body = c->getArgument(length-1)->getString(); if(length>=2) { - params = c->getParameter(0)->getString(); + params = c->getArgument(0)->getString(); for(int i=1; igetParameter(i)->getString()); + params.append(c->getArgument(i)->getString()); } } c->setReturnVar(parseFunctionsBodyFromString(params,body)); } void CTinyJS::native_Function_call(const CFunctionsScopePtr &c, void *data) { - int length = c->getParameterLength(); - CScriptVarPtr Fnc = c->getParameter("this"); - CScriptVarPtr This = c->getParameter(0); + int length = c->getArgumentsLength(); + CScriptVarPtr Fnc = c->getArgument("this"); + CScriptVarPtr This = c->getArgument(0); vector Params; for(int i=1; igetParameter(i)); + Params.push_back(c->getArgument(i)); bool execute = true; - callFunction(Fnc, Params, This, execute); + callFunction(execute, Fnc, Params, This); } void CTinyJS::native_Function_apply(const CFunctionsScopePtr &c, void *data) { - CScriptVarPtr Fnc = c->getParameter("this"); - CScriptVarPtr This = c->getParameter(0); - CScriptVarPtr Array = c->getParameter(1); + CScriptVarPtr Fnc = c->getArgument("this"); + CScriptVarPtr This = c->getArgument(0); + CScriptVarPtr Array = c->getArgument(1); CScriptVarLink *Length = Array->findChild("length"); - int length = Length ? Length->getValue()->getInt() : 0; + int length = Length ? (*Length)->getInt() : 0; vector Params; for(int i=0; ifindChild(int2string(i)); - if(value) Params.push_back(value->getValue()); - else Params.push_back(newScriptVar(Undefined)); + if(value) Params.push_back(value); + else Params.push_back(constScriptVar(Undefined)); } bool execute = true; - callFunction(Fnc, Params, This, execute); -} - -////////////////////////////////////////////////////////////////////////// global functions + callFunction(execute, Fnc, Params, This); +} + +////////////////////////////////////////////////////////////////////////// +/// Error +////////////////////////////////////////////////////////////////////////// + +static CScriptVarPtr _newError(CTinyJS *context, ERROR_TYPES type, const CFunctionsScopePtr &c) { + int i = c->getArgumentsLength(); + string message, fileName; + int line=-1, column=-1; + if(i>0) message = c->getArgument(0)->getString(); + if(i>1) fileName = c->getArgument(1)->getString(); + if(i>2) line = c->getArgument(2)->getInt(); + if(i>3) column = c->getArgument(3)->getInt(); + return ::newScriptVarError(context, type, message.c_str(), fileName.c_str(), line, column); +} +void CTinyJS::native_Error(const CFunctionsScopePtr &c, void *data) { c->setReturnVar(_newError(this, Error,c)); } +void CTinyJS::native_EvalError(const CFunctionsScopePtr &c, void *data) { c->setReturnVar(_newError(this, EvalError,c)); } +void CTinyJS::native_RangeError(const CFunctionsScopePtr &c, void *data) { c->setReturnVar(_newError(this, RangeError,c)); } +void CTinyJS::native_ReferenceError(const CFunctionsScopePtr &c, void *data){ c->setReturnVar(_newError(this, ReferenceError,c)); } +void CTinyJS::native_SyntaxError(const CFunctionsScopePtr &c, void *data){ c->setReturnVar(_newError(this, SyntaxError,c)); } +void CTinyJS::native_TypeError(const CFunctionsScopePtr &c, void *data){ c->setReturnVar(_newError(this, TypeError,c)); } + +////////////////////////////////////////////////////////////////////////// +/// global functions +////////////////////////////////////////////////////////////////////////// void CTinyJS::native_eval(const CFunctionsScopePtr &c, void *data) { - string Code = c->getParameter("jsCode")->getString(); + string Code = c->getArgument("jsCode")->getString(); CScriptVarScopePtr scEvalScope = scopes.back(); // save scope scopes.pop_back(); // go back to the callers scope - CScriptVarSmartLink returnVar; + CScriptVarLinkPtr returnVar; CScriptTokenizer *oldTokenizer = t; t=0; + runtimeFlags &= ~RUNTIME_CAN_RETURN; // we can't return a function from eval-code try { CScriptTokenizer Tokenizer(Code.c_str(), "eval"); t = &Tokenizer; @@ -4160,48 +4763,52 @@ void CTinyJS::native_eval(const CFunctionsScopePtr &c, void *data) { returnVar = execute_statement(execute); while (t->tk==';') t->match(';'); // skip empty statements } while (t->tk!=LEX_EOF); - } catch (CScriptException *e) { - t = oldTokenizer; + } catch (CScriptException *e) { // script exceptions + t = oldTokenizer; // restore tokenizer scopes.push_back(scEvalScope); // restore Scopes; - throw e; - } - t = oldTokenizer; + if(runtimeFlags & RUNTIME_CAN_THROW) { // an Error in eval is allways catchable + CScriptVarPtr E = newScriptVarError(this, e->errorType, e->message.c_str(), e->fileName.c_str(), e->lineNumber, e->column); + delete e; + throw E; + } else + throw e; + } catch (...) { // all other exceptions + t = oldTokenizer; // restore tokenizer + scopes.push_back(scEvalScope); // restore Scopes; + throw; +} + t = oldTokenizer; // restore tokenizer scopes.push_back(scEvalScope); // restore Scopes; if(returnVar) c->setReturnVar(returnVar->getVarPtr()); - - // check of exceptions - int exceptionState = runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE); - runtimeFlags &= ~RUNTIME_LOOP_MASK; - if(exceptionState) throw new CScriptEvalException(exceptionState); } void CTinyJS::native_isNAN(const CFunctionsScopePtr &c, void *data) { - c->setReturnVar(newScriptVar(c->getParameter("objc")->getNumericVar()->isNaN())); + c->setReturnVar(constScriptVar(c->getArgument("objc")->getNumericVar()->isNaN())); } void CTinyJS::native_isFinite(const CFunctionsScopePtr &c, void *data) { - CScriptVarPtr objc = c->getParameter("objc")->getNumericVar(); - c->setReturnVar(newScriptVar(!(objc->isInfinity() || objc->isNaN()))); + CScriptVarPtr objc = c->getArgument("objc")->getNumericVar(); + c->setReturnVar(constScriptVar(!(objc->isInfinity() || objc->isNaN()))); } void CTinyJS::native_parseInt(const CFunctionsScopePtr &c, void *) { - string str = c->getParameter("string")->getString(); - int radix = c->getParameter("radix")->getInt(); + string str = c->getArgument("string")->getString(); + int radix = c->getArgument("radix")->getInt(); char *endp = 0; int val = strtol(str.c_str(),&endp,radix!=0 ? radix : 0); if(endp == str.c_str()) - c->setReturnVar(c->newScriptVar(NaN)); + c->setReturnVar(c->constScriptVar(NaN)); else c->setReturnVar(c->newScriptVar(val)); } void CTinyJS::native_parseFloat(const CFunctionsScopePtr &c, void *) { - string str = c->getParameter("string")->getString(); + string str = c->getArgument("string")->getString(); char *endp = 0; double val = strtod(str.c_str(),&endp); if(endp == str.c_str()) - c->setReturnVar(c->newScriptVar(NaN)); + c->setReturnVar(c->constScriptVar(NaN)); else c->setReturnVar(c->newScriptVar(val)); } @@ -4209,9 +4816,9 @@ void CTinyJS::native_parseFloat(const CFunctionsScopePtr &c, void *) { void CTinyJS::native_JSON_parse(const CFunctionsScopePtr &c, void *data) { - string Code = "�" + c->getParameter("text")->getString(); + string Code = "�" + c->getArgument("text")->getString(); // "�" is a spezal-token - it's for the tokenizer and means the code begins not in Statement-level - CScriptVarSmartLink returnVar; + CScriptVarLinkPtr returnVar; CScriptTokenizer *oldTokenizer = t; t=0; try { CScriptTokenizer Tokenizer(Code.c_str(), "JSON.parse", 0, -1); @@ -4234,23 +4841,30 @@ void CTinyJS::native_JSON_parse(const CFunctionsScopePtr &c, void *data) { // if(exceptionState) throw new CScriptEvalException(exceptionState); } +void CTinyJS::setTemporaryID_recursive(uint32_t ID) { + for(vector::iterator it = pseudo_refered.begin(); it!=pseudo_refered.end(); ++it) + if(**it) (**it)->setTemporaryID_recursive(ID); + for(int i=Error; isetTemporaryID_recursive(ID); + root->setTemporaryID_recursive(ID); +} -void CTinyJS::ClearLostVars(const CScriptVarPtr &extra/*=CScriptVarPtr()*/) { - int UniqueID = getUniqueID(); - root->setTempraryIDrecursive(UniqueID); - if(extra) extra->setTempraryIDrecursive(UniqueID); +void CTinyJS::ClearUnreferedVars(const CScriptVarPtr &extra/*=CScriptVarPtr()*/) { + uint32_t UniqueID = getUniqueID(); + setTemporaryID_recursive(UniqueID); + if(extra) extra->setTemporaryID_recursive(UniqueID); CScriptVar *p = first; while(p) { if(p->temporaryID != UniqueID) { - CScriptVar *var = p; - var->ref(); + CScriptVarPtr var = p; var->removeAllChildren(); p = var->next; - var->unref(); } else p = p->next; } } + + diff --git a/TinyJS.h b/TinyJS.h index 6d83aa4..b7e59e8 100755 --- a/TinyJS.h +++ b/TinyJS.h @@ -1,1307 +1,1642 @@ -/* - * TinyJS - * - * A single-file Javascript-alike engine - * - * Authored By Gordon Williams - * - * Copyright (C) 2009 Pur3 Ltd - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef TINYJS_H -#define TINYJS_H - -#include -#include -#include -#include -#include "pool_allocator.h" -#include -#include - -#ifndef ASSERT -# define ASSERT(X) assert(X) -#endif - -#ifndef __GNUC__ -# define __attribute__(x) -#endif - - -#undef TRACE -#ifndef TRACE -#define TRACE printf -#endif // TRACE - -const int TINYJS_LOOP_MAX_ITERATIONS = 8192; - -enum LEX_TYPES { - LEX_EOF = 0, -#define LEX_RELATIONS_1_BEGIN LEX_EQUAL - LEX_EQUAL = 256, - LEX_TYPEEQUAL, - LEX_NEQUAL, - LEX_NTYPEEQUAL, -#define LEX_RELATIONS_1_END LEX_NTYPEEQUAL - LEX_LEQUAL, - LEX_GEQUAL, -#define LEX_SHIFTS_BEGIN LEX_LSHIFT - LEX_LSHIFT, - LEX_RSHIFT, - LEX_RSHIFTU, // unsigned -#define LEX_SHIFTS_END LEX_RSHIFTU - LEX_PLUSPLUS, - LEX_MINUSMINUS, - LEX_ANDAND, - LEX_OROR, - LEX_INT, - -#define LEX_ASSIGNMENTS_BEGIN LEX_PLUSEQUAL - LEX_PLUSEQUAL, - LEX_MINUSEQUAL, - LEX_ASTERISKEQUAL, - LEX_SLASHEQUAL, - LEX_PERCENTEQUAL, - LEX_LSHIFTEQUAL, - LEX_RSHIFTEQUAL, - LEX_RSHIFTUEQUAL, // unsigned - LEX_ANDEQUAL, - LEX_OREQUAL, - LEX_XOREQUAL, -#define LEX_ASSIGNMENTS_END LEX_XOREQUAL - -#define LEX_TOKEN_STRING_BEGIN LEX_ID - LEX_ID, - LEX_STR, - LEX_REGEXP, - LEX_T_LABEL, -#define LEX_TOKEN_STRING_END LEX_T_LABEL - - LEX_FLOAT, - - // reserved words - LEX_R_IF, - LEX_R_ELSE, -#define LEX_TOKEN_LOOP_BEGIN LEX_R_DO - LEX_R_DO, - LEX_R_WHILE, - LEX_R_FOR, -#define LEX_TOKEN_LOOP_END LEX_R_FOR - LEX_R_IN, - LEX_R_BREAK, - LEX_R_CONTINUE, - LEX_R_FUNCTION, - LEX_R_RETURN, - LEX_R_VAR, - LEX_R_LET, - LEX_R_WITH, - LEX_R_TRUE, - LEX_R_FALSE, - LEX_R_NULL, - LEX_R_NEW, - LEX_R_TRY, - LEX_R_CATCH, - LEX_R_FINALLY, - LEX_R_THROW, - LEX_R_TYPEOF, - LEX_R_VOID, - LEX_R_DELETE, - LEX_R_INSTANCEOF, - LEX_R_SWITCH, - LEX_R_CASE, - LEX_R_DEFAULT, - - // special token -// LEX_T_FILE, - LEX_T_FUNCTION_OPERATOR, - LEX_T_GET, - LEX_T_SET, - - LEX_T_FOR_IN, - LEX_T_FOR_EACH_IN, - - LEX_T_SKIP, - -}; -#define LEX_TOKEN_DATA_STRING(tk) ((LEX_TOKEN_STRING_BEGIN<= tk && tk <= LEX_TOKEN_STRING_END)) -#define LEX_TOKEN_DATA_FLOAT(tk) (tk==LEX_FLOAT) -#define LEX_TOKEN_DATA_FUNCTION(tk) (tk==LEX_R_FUNCTION || tk==LEX_T_FUNCTION_OPERATOR || tk==LEX_T_SET || tk==LEX_T_GET) -#define LEX_TOKEN_DATA_SIMPLE(tk) (!LEX_TOKEN_DATA_STRING(tk) && !LEX_TOKEN_DATA_FLOAT(tk) && !LEX_TOKEN_DATA_FUNCTION(tk)) -/* -enum SCRIPTVAR_FLAGS { - SCRIPTVAR_UNDEFINED = 0, - SCRIPTVAR_FUNCTION = 1<<0, - SCRIPTVAR_OBJECT = 1<<1, - SCRIPTVAR_ARRAY = 1<<2, - SCRIPTVAR_DOUBLE = 1<<3, // floating point double - SCRIPTVAR_INTEGER = 1<<4, // integer number - SCRIPTVAR_BOOLEAN = 1<<5, // boolean - SCRIPTVAR_STRING = 1<<6, // string - SCRIPTVAR_NULL = 1<<7, // it seems null is its own data type - SCRIPTVAR_INFINITY = 1<<8, // it seems infinity is its own data type - SCRIPTVAR_NAN = 1<<9, // it seems NaN is its own data type - SCRIPTVAR_ACCESSOR = 1<<10, // it seems an Object with get() and set() - - SCRIPTVAR_END_OF_TYPES = 1<<15, // it seems NaN is its own data type - - SCRIPTVAR_NATIVE_FNC = 1<<16, // to specify this is a native function - SCRIPTVAR_NATIVE_MFNC = 1<<17, // to specify this is a native function from class->memberFunc - - SCRIPTVAR_NUMBERMASK = SCRIPTVAR_DOUBLE | - SCRIPTVAR_INTEGER, - SCRIPTVAR_NUMERICMASK = SCRIPTVAR_NUMBERMASK | - SCRIPTVAR_NULL | - SCRIPTVAR_BOOLEAN, - - SCRIPTVAR_VARTYPEMASK = SCRIPTVAR_END_OF_TYPES - 1, - - SCRIPTVAR_NATIVE = SCRIPTVAR_NATIVE_FNC | - SCRIPTVAR_NATIVE_MFNC, -}; -*/ -enum SCRIPTVARLINK_FLAGS { - SCRIPTVARLINK_OWNED = 1<<0, - SCRIPTVARLINK_WRITABLE = 1<<1, - SCRIPTVARLINK_DELETABLE = 1<<2, - SCRIPTVARLINK_ENUMERABLE = 1<<3, - SCRIPTVARLINK_HIDDEN = 1<<4, - SCRIPTVARLINK_DEFAULT = SCRIPTVARLINK_WRITABLE | SCRIPTVARLINK_DELETABLE | SCRIPTVARLINK_ENUMERABLE, -}; -enum RUNTIME_FLAGS { - RUNTIME_CANRETURN = 1<<0, - - RUNTIME_CANBREAK = 1<<1, - RUNTIME_BREAK = 1<<2, - RUNTIME_BREAK_MASK = RUNTIME_CANBREAK | RUNTIME_BREAK, - - RUNTIME_CANCONTINUE = 1<<3, - RUNTIME_CONTINUE = 1<<4, - RUNTIME_LOOP_MASK = RUNTIME_BREAK_MASK | RUNTIME_CANCONTINUE | RUNTIME_CONTINUE, - - RUNTIME_NEW = 1<<5, - - RUNTIME_CANTHROW = 1<<6, - RUNTIME_THROW = 1<<7, - RUNTIME_THROW_MASK = RUNTIME_CANTHROW | RUNTIME_THROW, - -}; - -#define SAVE_RUNTIME_RETURN int old_return_runtimeFlags = runtimeFlags & RUNTIME_CANRETURN -#define RESTORE_RUNTIME_RETURN runtimeFlags = (runtimeFlags & ~RUNTIME_CANRETURN) | old_pass_runtimeFlags -#define SET_RUNTIME_CANRETURN runtimeFlags |= RUNTIME_CANRETURN -#define IS_RUNTIME_CANRETURN ((runtimeFlags & RUNTIME_CANRETURN) == RUNTIME_CANRETURN) - - -#define TINYJS_RETURN_VAR "return" -#define TINYJS_LOKALE_VAR "__locale__" -#define TINYJS_ANONYMOUS_VAR "__anonymous__" -#define TINYJS_ARGUMENTS_VAR "arguments" -#define TINYJS___PROTO___VAR "__proto__" -#define TINYJS_PROTOTYPE_CLASS "prototype" -#define TINYJS_FUNCTION_CLOSURE_VAR "__function_closure__" -#define TINYJS_SCOPE_PARENT_VAR "__scope_parent__" -#define TINYJS_SCOPE_WITH_VAR "__scope_with__" -#define TINYJS_ACCESSOR_GET_VAR "__accessor_get__" -#define TINYJS_ACCESSOR_SET_VAR "__accessor_set__" -#define TINYJS_TEMP_NAME "" -#define TINYJS_BLANK_DATA "" -#define TINYJS_NEGATIVE_INFINITY_DATA "-1" -#define TINYJS_POSITIVE_INFINITY_DATA "+1" - -typedef std::vector STRING_VECTOR_t; -typedef STRING_VECTOR_t::iterator STRING_VECTOR_it; - -/// convert the given string into a quoted string suitable for javascript -std::string getJSString(const std::string &str); -/// convert the given int into a string -std::string int2string(int intData); -/// convert the given double into a string -std::string float2string(const double &floatData); - -// ----------------------------------------------------------------------------------- CScriptSmartPointer -template -class CScriptSmartPointer -{ -public: - CScriptSmartPointer() : pointer(0) {} - explicit CScriptSmartPointer(T *Pointer) : pointer(Pointer ? new p(Pointer) : 0) {} - CScriptSmartPointer(const CScriptSmartPointer &Copy) : pointer(0) {*this = Copy;} - CScriptSmartPointer &operator=(const CScriptSmartPointer &Copy) - { - if(this == &Copy) return *this; - if(pointer) pointer->unref(); - if(Copy.pointer) - pointer = Copy.pointer->ref(); - else - pointer = 0; - return *this; - } - ~CScriptSmartPointer() { release(); } - void release() { if(pointer) pointer->unref(); pointer = 0;} - T *operator->() { return operator T*(); } - operator T*() { return pointer ? pointer->pointer : 0; } -private: - class p - { - public: - p(T *Pointer) : pointer(Pointer), refs(1) {} - p *ref() { refs++; return this; } - void unref() { if(--refs == 0) { delete pointer; delete this; } } - T *pointer; - int refs; - } *pointer; -}; - -class CScriptException { -public: - std::string text; -// int pos; - std::string file; - int line; - int column; -// CScriptException(const std::string &exceptionText, int Pos=-1); - CScriptException(const std::string &Text, const std::string &File, int Line=-1, int Column=-1) : - text(Text), file(File), line(Line), column(Column){} - CScriptException(const std::string &Text, const char *File="", int Line=-1, int Column=-1) : - text(Text), file(File), line(Line), column(Column){} -}; - -// ----------------------------------------------------------------------------------- CSCRIPTLEX -class CScriptLex -{ -public: - CScriptLex(const char *Code, const std::string &File="", int Line=0, int Column=0); - - int tk; ///< The type of the token that we have - int last_tk; ///< The type of the last token that we have - const char *tokenStart; - std::string tkStr; ///< Data contained in the token we have here - - void check(int expected_tk); ///< Lexical check wotsit - void match(int expected_tk); ///< Lexical match wotsit - static std::string getTokenStr(int token); ///< Get the string representation of the given token - void reset(const char *toPos, int line, const char *LineStart); ///< Reset this lex so we can start again - - std::string currentFile; - int currentLine; - const char *currentLineStart; - int currentColumn() { return tokenStart-currentLineStart; } - -private: - const char *data; - const char *dataPos; - char currCh, nextCh; - - void getNextCh(); - void getNextToken(); ///< Get the text token from our text string -}; - -// ----------------------------------------------------------------------------------- CSCRIPTTOKEN -class CScriptToken; -typedef std::vector TOKEN_VECT; -typedef std::vector::iterator TOKEN_VECT_it; -class CScriptTokenData -{ -public: - virtual ~CScriptTokenData() {} - void ref() { refs++; } - void unref() { if(--refs == 0) delete this; } -protected: - CScriptTokenData() : refs(0){} -private: - int refs; -}; -class CScriptTokenDataString : public fixed_size_object, public CScriptTokenData { -public: - CScriptTokenDataString(const std::string &String) : tokenStr(String) {} - std::string tokenStr; -private: -}; -class CScriptTokenDataFnc : public fixed_size_object, public CScriptTokenData { -public: - std::string file; - int line; - std::string name; - std::vector parameter; - TOKEN_VECT body; -private: -}; - -class CScriptTokenizer; -/* - a Token needs 8 Byte - 2 Bytes for the Row-Position of the Token - 2 Bytes for the Token self - and - 4 Bytes for special Datas in an union - e.g. an int for interger-literals - or pointer for double-literals, - for string-literals or for functions -*/ -class CScriptToken : public fixed_size_object -{ -public: - CScriptToken() : line(0), column(0), token(0) {} - CScriptToken(CScriptLex *l, int Match=-1); - CScriptToken(uint16_t Tk, int IntData=0); - CScriptToken(uint16_t Tk, const std::string &TkStr); - CScriptToken(const CScriptToken &Copy) : token(0) { *this = Copy; } - CScriptToken &operator =(const CScriptToken &Copy); - ~CScriptToken() { clear(); } - - int &Int() { ASSERT(LEX_TOKEN_DATA_SIMPLE(token)); return intData; } - std::string &String() { ASSERT(LEX_TOKEN_DATA_STRING(token)); return stringData->tokenStr; } - double &Float() { ASSERT(LEX_TOKEN_DATA_FLOAT(token)); return *floatData; } - CScriptTokenDataFnc &Fnc() { ASSERT(LEX_TOKEN_DATA_FUNCTION(token)); return *fncData; } - uint16_t line; - uint16_t column; - uint16_t token; - void print(std::string &indent); - std::string getParsableString(const std::string Indent=""); - std::string getParsableString(std::string &indentString, int &newln, const std::string &indent); ///< newln ==> 1=inject indentString, 2=inject \n, 3=last='{' -private: - - void clear(); - union { - int intData; - double *floatData; - CScriptTokenDataString *stringData; - CScriptTokenDataFnc *fncData; - }; -}; - -// ----------------------------------------------------------------------------------- CSCRIPTTOKENIZER -/* - the tokenizer converts the code in an Vector with Tokens -*/ -class CScriptTokenizer -{ -public: - struct ScriptTokenPosition { - ScriptTokenPosition(TOKEN_VECT *Tokens) : tokens(Tokens), pos(tokens->begin())/*, currentLine(0)*//*, currentColumn(0)*/ {} - bool operator ==(const ScriptTokenPosition &eq) { return pos == eq.pos; } - ScriptTokenPosition &operator =(const ScriptTokenPosition ©) { - tokens=copy.tokens; pos=copy.pos; -// currentLine=copy.currentLine; - return *this; - } - TOKEN_VECT *tokens; - TOKEN_VECT::iterator pos; - int currentLine() { return pos->line; } - int currentColumn() { return pos->column; } - }; - CScriptTokenizer(); - CScriptTokenizer(CScriptLex &Lexer); - CScriptTokenizer(const char *Code, const std::string &File="", int Line=0, int Column=0); - void tokenizeCode(CScriptLex &Lexer); - - CScriptToken &getToken() { return *(tokenScopeStack.back().pos); } - void getNextToken(); - bool check(int ExpectedToken); - void match(int ExpectedToken); - void pushTokenScope(TOKEN_VECT &Tokens); - ScriptTokenPosition &getPos() { return tokenScopeStack.back(); } - void setPos(ScriptTokenPosition &TokenPos); - ScriptTokenPosition &getPrevPos() { return prevPos; } - void skip(int Tokens); - int tk; // current Token - std::string currentFile; - int currentLine() { return getPos().currentLine();} - int currentColumn() { return getPos().currentColumn();} - std::string &tkStr() { return getToken().String(); } -private: - void tokenizeTry(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); - void tokenizeSwitch(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); - void tokenizeWith(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); - void tokenizeWhile(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); - void tokenizeDo(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); - void tokenizeIf(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); - void tokenizeForIn(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); - void tokenizeFor(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); - void tokenizeFunction(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); - void tokenizeBlock(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); - void tokenizeLet(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); - void tokenizeVar(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); - void tokenizeStatement(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); - void tokenizeToken(TOKEN_VECT &Tokens, bool &Statement, std::vector &BlockStart, std::vector &Marks); - int pushToken(TOKEN_VECT &Tokens, int Match=-1); - void throwTokenNotExpected(); - CScriptLex *l; - TOKEN_VECT tokens; - ScriptTokenPosition prevPos; - std::vector tokenScopeStack; -}; -////////////////////////////////////////////////////////////////////////// CScriptVarPointer - -class CScriptVar; -class CScriptVarLink; -class CScriptVarSmartLink; -template -class CScriptVarPointer { -public: - CScriptVarPointer() : var(0) {} ///< explizit 0-Pointer - CScriptVarPointer(CScriptVar *Var) : var(dynamic_cast(Var)) { if(Var) ASSERT(var); if(var) var->ref(); } - CScriptVarPointer(const CScriptVarLink *Link); - CScriptVarPointer(const CScriptVarSmartLink &Link); - //: var(dynamic_cast(Var)) { if(Var) ASSERT(var); if(var) var->ref(); } - ~CScriptVarPointer() { if(var) var->unref(); } - CScriptVarPointer(const CScriptVarPointer &VarPtr) : var(0) { *this = VarPtr; } - CScriptVarPointer& operator=(const CScriptVarPointer &VarPtr) { - if(var != VarPtr.var) { - if(var) var->unref(); - var = VarPtr.var; if(var) var->ref(); - } - return *this; - } - template - operator CScriptVarPointer() const { return CScriptVarPointer(var); } - operator bool() const { return var!=0; } - - template - bool operator ==(const CScriptVarPointer &Other) const { return (CScriptVar*)var == (CScriptVar*)(Other.getVar()); } - template - bool operator !=(const CScriptVarPointer &Other) const { return (CScriptVar*)var != (CScriptVar*)(Other.getVar()); } - - C * operator ->() const { ASSERT(var); return var; } - CScriptVar *getVar() const { ASSERT(var); return var; } -private: - C *var; -}; - -typedef CScriptVarPointer CScriptVarPtr; - -// ----------------------------------------------------------------------------------- CSCRIPTVARLINK -class CScriptVar; -class CScriptVarLink : public fixed_size_object -{ -public: - std::string name; -// const std::string &Name() { return name; } -// void SetName(const std::string &Name) { name = Name; } -private: - CScriptVar *var; -public: - CScriptVarPtr getValue(); -private: -// CScriptVar *get() { return var; } -public: - CScriptVarPtr getVarPtr() { return var; } - CScriptVar *owner; // pointer to the owner CScriptVar - uint32_t flags; - CScriptVarLink(const CScriptVarPtr &var, const std::string &name = TINYJS_TEMP_NAME, int flags = SCRIPTVARLINK_DEFAULT); - CScriptVarLink(const CScriptVarLink &link); ///< Copy constructor - ~CScriptVarLink(); - void replaceWith(const CScriptVarPtr &newVar); ///< Replace the Variable pointed to - void replaceWith(CScriptVarLink *newVar); ///< Replace the Variable pointed to (just dereferences) -// void SetOwner(CScriptVar *Owner) { owner = Owner; } -// void SetOwner(CScriptVar *Owner, bool Owned) { owner = Owner; SetOwned(Owned); } -// bool IsOwned() { return owner != 0; } - bool IsOwned() { return (flags & SCRIPTVARLINK_OWNED) != 0; } - void SetOwned(bool On) { On ? (flags |= SCRIPTVARLINK_OWNED) : (flags &= ~SCRIPTVARLINK_OWNED); } - - bool IsWritable() { return (flags & SCRIPTVARLINK_WRITABLE) != 0; } - void SetWritable(bool On) { On ? (flags |= SCRIPTVARLINK_WRITABLE) : (flags &= ~SCRIPTVARLINK_DELETABLE); } - bool IsDeletable() { return (flags & SCRIPTVARLINK_DELETABLE) != 0; } - void SetDeletable(bool On) { On ? (flags |= SCRIPTVARLINK_DELETABLE) : (flags &= ~SCRIPTVARLINK_DELETABLE); } - bool IsEnumerable() { return (flags & SCRIPTVARLINK_ENUMERABLE) != 0; } - void SetEnumerable(bool On) { On ? (flags |= SCRIPTVARLINK_ENUMERABLE) : (flags &= ~SCRIPTVARLINK_ENUMERABLE); } - bool IsHidden() { return (flags & SCRIPTVARLINK_HIDDEN) != 0; } - void SetHidden(bool On) { On ? (flags |= SCRIPTVARLINK_HIDDEN) : (flags &= ~SCRIPTVARLINK_HIDDEN); } - CScriptVar * operator ->() const { return var; } -// friend class CScriptVar; -// friend class CScriptVarSmartLink; -// friend class CScriptVarObject; -}; -template -CScriptVarPointer::CScriptVarPointer(const CScriptVarLink *Link) : var(0) { if(Link) { var = dynamic_cast(Link->operator->()); ASSERT(var); } if(var) var->ref(); } - -// ----------------------------------------------------------------------------------- CSCRIPTVAR -typedef std::vector SCRIPTVAR_CHILDS_t; -typedef SCRIPTVAR_CHILDS_t::iterator SCRIPTVAR_CHILDS_it; -typedef SCRIPTVAR_CHILDS_t::const_iterator SCRIPTVAR_CHILDS_cit; -class CTinyJS; -class CScriptVarScopeFnc; -typedef CScriptVarPointer CFunctionsScopePtr; -typedef void (*JSCallback)(const CFunctionsScopePtr &var, void *userdata); - - -class CScriptVar : public fixed_size_object { -protected: - CScriptVar(CTinyJS *Context, const CScriptVarPtr &Prototype=CScriptVarPtr()); ///< Create - CScriptVar(const CScriptVar &Copy); ///< Copy protected -> use clone for public -private: - CScriptVar & operator=(const CScriptVar &Copy); ///< private -> no assignment-Copy -public: - virtual ~CScriptVar(void); - virtual CScriptVarPtr clone()=0; - - /// Type - virtual bool isInt(); // {return false;} - virtual bool isBool(); // {return false;} - virtual bool isDouble(); // {return false;} - virtual bool isString(); // {return false;} - virtual bool isNumber(); // {return false;} - virtual bool isNumeric(); // {return false;} - virtual bool isFunction(); // {return false;} - virtual bool isObject(); // {return false;} - virtual bool isArray(); // {return false;} - virtual bool isNative(); // {return false;} - virtual bool isUndefined();// {return false;} - virtual bool isNull(); // {return false;} - virtual bool isNaN(); // {return false;} - virtual int isInfinity(); // { return 0; } ///< +1==POSITIVE_INFINITY, -1==NEGATIVE_INFINITY, 0==is not an InfinityVar - virtual bool isAccessor(); // {return false;} - - bool isBasic() { return Childs.empty(); } ///< Is this *not* an array/object/etc - - - /// Value - virtual int getInt(); ///< return 0 - virtual bool getBool(); ///< return false - virtual double getDouble(); ///< return 0.0 - virtual CScriptTokenDataFnc *getFunctionData(); ///< { return 0; } - virtual std::string getString(); ///< return "" - - virtual std::string getParsableString(const std::string &indentString, const std::string &indent); ///< get Data as a parsable javascript string - virtual std::string getVarType()=0; - virtual CScriptVarPtr getNumericVar(); ///< returns an Integer, a Double, an Infinity or a NaN - - /// find - CScriptVarLink *findChild(const std::string &childName); ///< Tries to find a child with the given name, may return 0 - CScriptVarLink *findChildByPath(const std::string &path); ///< Tries to find a child with the given path (separated by dots) - CScriptVarLink *findChildOrCreate(const std::string &childName/*, int varFlags=SCRIPTVAR_UNDEFINED*/); ///< Tries to find a child with the given name, or will create it with the given flags - CScriptVarLink *findChildOrCreateByPath(const std::string &path); ///< Tries to find a child with the given path (separated by dots) - - /// add & remove - CScriptVarLink *addChild(const std::string &childName, const CScriptVarPtr &child, int linkFlags = SCRIPTVARLINK_DEFAULT); - CScriptVarLink *addChildNoDup(const std::string &childName, const CScriptVarPtr &child, int linkFlags = SCRIPTVARLINK_DEFAULT); ///< add a child overwriting any with the same name - bool removeLink(CScriptVarLink *&link); ///< Remove a specific link (this is faster than finding via a child) - void removeAllChildren(); - - - /// funcions for FUNCTION - void setReturnVar(const CScriptVarPtr &var); ///< Set the result value. Use this when setting complex return data as it avoids a deepCopy() - CScriptVarPtr getParameter(const std::string &name); ///< If this is a function, get the parameter with the given name (for use by native functions) - CScriptVarPtr getParameter(int Idx); ///< If this is a function, get the parameter with the given index (for use by native functions) - int getParameterLength(); ///< If this is a function, get the count of parameters - - - /// ARRAY - CScriptVarPtr getArrayIndex(int idx); ///< The the value at an array index - void setArrayIndex(int idx, const CScriptVarPtr &value); ///< Set the value at an array index - int getArrayLength(); ///< If this is an array, return the number of items in it (else 0) - - ////////////////////////////////////////////////////////////////////////// - int getChildren() { return Childs.size(); } ///< Get the number of children - CTinyJS *getContext() { return context; } - CScriptVarPtr mathsOp(const CScriptVarPtr &b, int op); ///< do a maths op with another script variable - - void trace(const std::string &name = ""); ///< Dump out the contents of this using trace - void trace(std::string &indentStr, int uniqueID, const std::string &name = ""); ///< Dump out the contents of this using trace - std::string getFlagsAsString(); ///< For debugging - just dump a string version of the flags -// void getJSON(std::ostringstream &destination, const std::string linePrefix=""); ///< Write out all the JS code needed to recreate this script variable to the stream (as JSON) - - SCRIPTVAR_CHILDS_t Childs; - - /// For memory management/garbage collection - CScriptVar *ref(); ///< Add reference to this variable - void unref(); ///< Remove a reference, and delete this variable if required - int getRefs(); ///< Get the number of references to this script variable - template - operator T *(){ T* ret = dynamic_cast(this); ASSERT(ret!=0); return ret; } - template - T *get(){ T* ret = dynamic_cast(this); ASSERT(ret!=0); return ret; } - - template - CScriptVarPtr newScriptVar(T t); // { return ::newScriptVar(context, t); } - void setTempraryID(int ID) { temporaryID = ID; } - void setTempraryIDrecursive(int ID); - int getTempraryID() { return temporaryID; } -protected: - CTinyJS *context; - int refs; ///< The number of references held to this - used for garbage collection - CScriptVar *prev; -public: - CScriptVar *next; - int temporaryID; -}; -////////////////////////////////////////////////////////////////////////// -#define define_dummy_t(t1) struct t1##_t{}; extern t1##_t t1 -#define declare_dummy_t(t1) t1##_t t1 -#define define_newScriptVar_Fnc(t1, ...) CScriptVar##t1##Ptr newScriptVar(__VA_ARGS__) -#define define_ScriptVarPtr_Type(t1) class CScriptVar##t1; typedef CScriptVarPointer CScriptVar##t1##Ptr - - -////////////////////////////////////////////////////////////////////////// CScriptVarObject - -define_dummy_t(Object); -define_ScriptVarPtr_Type(Object); - -class CScriptVarObject : public CScriptVar { -protected: - CScriptVarObject(CTinyJS *Context, const CScriptVarPtr &Prototype=CScriptVarPtr()) : CScriptVar(Context, Prototype) {} - CScriptVarObject(const CScriptVarObject &Copy) : CScriptVar(Copy) {} ///< Copy protected -> use clone for public -public: - virtual ~CScriptVarObject(); - virtual CScriptVarPtr clone(); - virtual bool isObject(); // { return true; } - - virtual std::string getString(); // { return "[ Object ]"; }; - virtual std::string getParsableString(const std::string &indentString, const std::string &indent); - virtual std::string getVarType(); // { return "object"; } -private: - - friend define_newScriptVar_Fnc(Object, CTinyJS* Context, Object_t); - friend define_newScriptVar_Fnc(Object, CTinyJS* Context, Object_t, const CScriptVarPtr &); -}; -inline define_newScriptVar_Fnc(Object, CTinyJS* Context, Object_t) { return new CScriptVarObject(Context); } -inline define_newScriptVar_Fnc(Object, CTinyJS* Context, Object_t, const CScriptVarPtr &Prototype) { return new CScriptVarObject(Context, Prototype); } - - -////////////////////////////////////////////////////////////////////////// CScriptVarAccessor - -define_dummy_t(Accessor); -define_ScriptVarPtr_Type(Accessor); - -class CScriptVarAccessor : public CScriptVar { -protected: - CScriptVarAccessor(CTinyJS *Context) : CScriptVar(Context) {} - CScriptVarAccessor(const CScriptVarAccessor &Copy) : CScriptVar(Copy) {} ///< Copy protected -> use clone for public -public: - virtual ~CScriptVarAccessor(); - virtual CScriptVarPtr clone(); - virtual bool isAccessor(); // { return true; } - - virtual std::string getString(); // { return "[ Object ]"; }; - virtual std::string getParsableString(const std::string &indentString, const std::string &indent); - virtual std::string getVarType(); // { return "object"; } - - CScriptVarPtr getValue(); - - friend define_newScriptVar_Fnc(Accessor, CTinyJS* Context, Accessor_t); - -}; -inline define_newScriptVar_Fnc(Accessor, CTinyJS* Context, Accessor_t) { return new CScriptVarAccessor(Context); } - - -////////////////////////////////////////////////////////////////////////// CScriptVarArray - -define_dummy_t(Array); -define_ScriptVarPtr_Type(Array); -class CScriptVarArray : public CScriptVar { -protected: - CScriptVarArray(CTinyJS *Context); - CScriptVarArray(const CScriptVarArray &Copy) : CScriptVar(Copy) {} ///< Copy protected -> use clone for public -public: - virtual ~CScriptVarArray(); - virtual CScriptVarPtr clone(); - virtual bool isArray(); // { return true; } - virtual std::string getString(); - virtual std::string getVarType(); // { return "object"; } - virtual std::string getParsableString(const std::string &indentString, const std::string &indent); - friend define_newScriptVar_Fnc(Array, CTinyJS* Context, Array_t); -private: - void native_Length(const CFunctionsScopePtr &c, void *data); -}; -inline define_newScriptVar_Fnc(Array, CTinyJS* Context, Array_t) { return new CScriptVarArray(Context); } - - -////////////////////////////////////////////////////////////////////////// CScriptVarNull - -define_dummy_t(Null); -define_ScriptVarPtr_Type(Null); -class CScriptVarNull : public CScriptVar { -protected: - CScriptVarNull(CTinyJS *Context) : CScriptVar(Context) {} - CScriptVarNull(const CScriptVarNull &Copy) : CScriptVar(Copy) {} ///< Copy protected -> use clone for public -public: - virtual ~CScriptVarNull(); - virtual CScriptVarPtr clone(); - virtual bool isNull(); // { return true; } - virtual std::string getString(); // { return "null"; }; - virtual std::string getVarType(); // { return "null"; } - virtual CScriptVarPtr getNumericVar(); ///< returns an Integer, a Double, an Infinity or a NaN - friend define_newScriptVar_Fnc(Null, CTinyJS* Context, Null_t); -}; -inline define_newScriptVar_Fnc(Null, CTinyJS* Context, Null_t) { return new CScriptVarNull(Context); } - - -////////////////////////////////////////////////////////////////////////// CScriptVarUndefined - -define_dummy_t(Undefined); -define_ScriptVarPtr_Type(Undefined); -class CScriptVarUndefined : public CScriptVar { -protected: - CScriptVarUndefined(CTinyJS *Context) : CScriptVar(Context) {} - CScriptVarUndefined(const CScriptVarUndefined &Copy) : CScriptVar(Copy) {} ///< Copy protected -> use clone for public -public: - virtual ~CScriptVarUndefined(); - virtual CScriptVarPtr clone(); - virtual bool isUndefined(); // { return true; } - virtual std::string getString(); // { return "undefined"; }; - virtual std::string getVarType(); // { return "undefined"; } - friend define_newScriptVar_Fnc(Undefined, CTinyJS* Context, Undefined_t); -}; -inline define_newScriptVar_Fnc(Undefined, CTinyJS* Context, Undefined_t) { return new CScriptVarUndefined(Context); } - - -////////////////////////////////////////////////////////////////////////// CScriptVarNaN - -define_dummy_t(NaN); -define_ScriptVarPtr_Type(NaN); -class CScriptVarNaN : public CScriptVar { -protected: - CScriptVarNaN(CTinyJS *Context) : CScriptVar(Context) {} - CScriptVarNaN(const CScriptVarNaN &Copy) : CScriptVar(Copy) {} ///< Copy protected -> use clone for public -public: - virtual ~CScriptVarNaN(); - virtual CScriptVarPtr clone(); - virtual bool isNaN();// { return true; } - virtual std::string getString(); // { return "NaN"; }; - virtual std::string getVarType(); // { return "number"; } - friend define_newScriptVar_Fnc(NaN, CTinyJS* Context, NaN_t); -}; -inline define_newScriptVar_Fnc(NaN, CTinyJS* Context, NaN_t) { return new CScriptVarNaN(Context); } - - -////////////////////////////////////////////////////////////////////////// CScriptVarString - -define_ScriptVarPtr_Type(String); -class CScriptVarString : public CScriptVar { -protected: - CScriptVarString(CTinyJS *Context, const std::string &Data); - CScriptVarString(const CScriptVarString &Copy) : CScriptVar(Copy), data(Copy.data) {} ///< Copy protected -> use clone for public -public: - virtual ~CScriptVarString(); - virtual CScriptVarPtr clone(); - virtual bool isString(); // { return true; } - virtual int getInt(); // {return strtol(data.c_str(),0,0); } - virtual bool getBool(); // {return data.length()!=0;} - virtual double getDouble(); // {return strtod(data.c_str(),0);} - virtual std::string getString(); // { return data; } - virtual std::string getParsableString(); // { return getJSString(data); } - virtual std::string getVarType(); // { return "string"; } - virtual CScriptVarPtr getNumericVar(); ///< returns an Integer, a Double, an Infinity or a NaN -protected: - std::string data; -private: - void native_Length(const CFunctionsScopePtr &c, void *data); - - friend define_newScriptVar_Fnc(String, CTinyJS* Context, const std::string &); - friend define_newScriptVar_Fnc(String, CTinyJS* Context, const char *); -}; -inline define_newScriptVar_Fnc(String, CTinyJS* Context, const std::string &Obj) { return new CScriptVarString(Context, Obj); } -inline define_newScriptVar_Fnc(String, CTinyJS* Context, const char *Obj) { return new CScriptVarString(Context, Obj); } - - -////////////////////////////////////////////////////////////////////////// CScriptVarIntegerBase - -class CScriptVarIntegerBase : public CScriptVar { -protected: - CScriptVarIntegerBase(CTinyJS *Context, int Data); - CScriptVarIntegerBase(const CScriptVarIntegerBase &Copy) : CScriptVar(Copy), data(Copy.data) {} ///< Copy protected -> use clone for public -public: - virtual ~CScriptVarIntegerBase(); - virtual int getInt(); // {return data; } - virtual bool getBool(); // {return data!=0;} - virtual double getDouble(); // {return data;} - virtual std::string getString(); // {return int2string(data);} - virtual std::string getVarType(); // { return "number"; } - virtual CScriptVarPtr getNumericVar(); ///< returns an Integer, a Double, an Infinity or a NaN -protected: - int data; -}; - - -////////////////////////////////////////////////////////////////////////// CScriptVarInteger - -define_ScriptVarPtr_Type(Integer); -class CScriptVarInteger : public CScriptVarIntegerBase { -protected: - CScriptVarInteger(CTinyJS *Context, int Data) : CScriptVarIntegerBase(Context, Data) {} - CScriptVarInteger(const CScriptVarInteger &Copy) : CScriptVarIntegerBase(Copy) {} ///< Copy protected -> use clone for public -public: - virtual ~CScriptVarInteger(); - virtual CScriptVarPtr clone(); - virtual bool isNumber(); // { return true; } - virtual bool isInt(); // { return true; } - friend define_newScriptVar_Fnc(Integer, CTinyJS* Context, int); -}; -inline define_newScriptVar_Fnc(Integer, CTinyJS* Context, int Obj) { return new CScriptVarInteger(Context, Obj); } - - -////////////////////////////////////////////////////////////////////////// CScriptVarBool - -define_ScriptVarPtr_Type(Bool); -class CScriptVarBool : public CScriptVarIntegerBase { -protected: - CScriptVarBool(CTinyJS *Context, bool Data) : CScriptVarIntegerBase(Context, Data?1:0) {} - CScriptVarBool(const CScriptVarBool &Copy) : CScriptVarIntegerBase(Copy) {} ///< Copy protected -> use clone for public -public: - virtual ~CScriptVarBool(); - virtual CScriptVarPtr clone(); - virtual bool isBool(); // { return true; } - virtual std::string getString(); // {return data!=0?"true":"false";} - virtual std::string getVarType(); // { return "boolean"; } - virtual CScriptVarPtr getNumericVar(); ///< returns an Integer, a Double, an Infinity or a NaN - friend define_newScriptVar_Fnc(Bool, CTinyJS* Context, bool); -}; -inline define_newScriptVar_Fnc(Bool, CTinyJS* Context, bool Obj) { return new CScriptVarBool(Context, Obj); } - - -////////////////////////////////////////////////////////////////////////// CScriptVarInfinity - -struct Infinity{Infinity(int Sig=1):sig(Sig){} int sig; }; -extern Infinity InfinityPositive; -extern Infinity InfinityNegative; -define_ScriptVarPtr_Type(Infinity); -class CScriptVarInfinity : public CScriptVarIntegerBase { -protected: - CScriptVarInfinity(CTinyJS *Context, int Data) : CScriptVarIntegerBase(Context, Data<0?-1:1) {} - CScriptVarInfinity(const CScriptVarInfinity &Copy) : CScriptVarIntegerBase(Copy) {} ///< Copy protected -> use clone for public -public: - virtual ~CScriptVarInfinity(); - virtual CScriptVarPtr clone(); - virtual int isInfinity(); // { return data; } - virtual std::string getString(); // {return data<0?"-Infinity":"Infinity";} - friend define_newScriptVar_Fnc(Infinity, CTinyJS* Context, Infinity); -}; -inline define_newScriptVar_Fnc(Infinity, CTinyJS* Context, Infinity Obj) { return new CScriptVarInfinity(Context, Obj.sig); } - - -////////////////////////////////////////////////////////////////////////// CScriptVarDouble - -define_ScriptVarPtr_Type(Double); -class CScriptVarDouble : public CScriptVar { -protected: - CScriptVarDouble(CTinyJS *Context, double Data); - CScriptVarDouble(const CScriptVarDouble &Copy) : CScriptVar(Copy), data(Copy.data) {} ///< Copy protected -> use clone for public -public: - virtual ~CScriptVarDouble(); - virtual CScriptVarPtr clone(); - virtual bool isNumber(); // { return true; } - virtual bool isDouble(); // { return true; } - virtual int getInt(); // {return (int)data; } - virtual bool getBool(); // {return data!=0.0;} - virtual double getDouble(); // {return data;} - virtual std::string getString(); // {return float2string(data);} - virtual std::string getVarType(); // { return "number"; } - virtual CScriptVarPtr getNumericVar(); ///< returns an Integer, a Double, an Infinity or a NaN -private: - double data; - friend define_newScriptVar_Fnc(Double, CTinyJS* Context, double); -}; -inline define_newScriptVar_Fnc(Double, CTinyJS* Context, double Obj) { return new CScriptVarDouble(Context, Obj); } - - -////////////////////////////////////////////////////////////////////////// CScriptVarFunction - -define_ScriptVarPtr_Type(Function); -class CScriptVarFunction : public CScriptVar { -protected: - CScriptVarFunction(CTinyJS *Context, CScriptTokenDataFnc *Data); - CScriptVarFunction(const CScriptVarFunction &Copy) : CScriptVar(Copy), data(Copy.data) { data->ref(); } ///< Copy protected -> use clone for public -public: - virtual ~CScriptVarFunction(); - virtual CScriptVarPtr clone(); - virtual bool isFunction(); // { return true; } - - virtual std::string getString(); // {return "[ Function ]";} - virtual std::string getVarType(); // { return "function"; } - virtual std::string getParsableString(const std::string &indentString, const std::string &indent); - virtual CScriptTokenDataFnc *getFunctionData(); - void setFunctionData(CScriptTokenDataFnc *Data); -private: - CScriptTokenDataFnc *data; - std::string getParsableBlockString(TOKEN_VECT::iterator &it, TOKEN_VECT::iterator end, const std::string indentString, const std::string indent); - - - friend define_newScriptVar_Fnc(Function, CTinyJS* Context, CScriptTokenDataFnc *); -}; -inline define_newScriptVar_Fnc(Function, CTinyJS* Context, CScriptTokenDataFnc *Obj) { return new CScriptVarFunction(Context, Obj); } - - -////////////////////////////////////////////////////////////////////////// CScriptVarFunctionNative - -define_ScriptVarPtr_Type(FunctionNative); -class CScriptVarFunctionNative : public CScriptVarFunction { -protected: - CScriptVarFunctionNative(CTinyJS *Context, void *Userdata) : CScriptVarFunction(Context, 0), jsUserData(Userdata) { } - CScriptVarFunctionNative(const CScriptVarFunctionNative &Copy) : CScriptVarFunction(Copy), jsUserData(Copy.jsUserData) { } ///< Copy protected -> use clone for public -public: - virtual ~CScriptVarFunctionNative(); - virtual CScriptVarPtr clone()=0; - virtual bool isNative(); // { return true; } - - virtual std::string getString(); // {return "[ Function Native ]";} - - virtual void callFunction(const CFunctionsScopePtr &c)=0;// { jsCallback(c, jsCallbackUserData); } -protected: - void *jsUserData; ///< user data passed as second argument to native functions -}; - - -////////////////////////////////////////////////////////////////////////// CScriptVarFunctionNativeCallback - - -define_ScriptVarPtr_Type(FunctionNativeCallback); -class CScriptVarFunctionNativeCallback : public CScriptVarFunctionNative { -protected: - CScriptVarFunctionNativeCallback(CTinyJS *Context, JSCallback Callback, void *Userdata) : CScriptVarFunctionNative(Context, Userdata), jsCallback(Callback) { } - CScriptVarFunctionNativeCallback(const CScriptVarFunctionNativeCallback &Copy) : CScriptVarFunctionNative(Copy), jsCallback(Copy.jsCallback) { } ///< Copy protected -> use clone for public -public: - virtual ~CScriptVarFunctionNativeCallback(); - virtual CScriptVarPtr clone(); - virtual void callFunction(const CFunctionsScopePtr &c); -private: - JSCallback jsCallback; ///< Callback for native functions - friend define_newScriptVar_Fnc(FunctionNativeCallback, CTinyJS* Context, JSCallback Callback, void*); -}; -inline define_newScriptVar_Fnc(FunctionNativeCallback, CTinyJS* Context, JSCallback Callback, void* Userdata) { return new CScriptVarFunctionNativeCallback(Context, Callback, Userdata); } - - -////////////////////////////////////////////////////////////////////////// CScriptVarFunctionNativeClass - -template -class CScriptVarFunctionNativeClass : public CScriptVarFunctionNative { -protected: - CScriptVarFunctionNativeClass(CTinyJS *Context, native *ClassPtr, void (native::*ClassFnc)(const CFunctionsScopePtr &, void *), void *Userdata) : CScriptVarFunctionNative(Context, Userdata), classPtr(ClassPtr), classFnc(ClassFnc) { } - CScriptVarFunctionNativeClass(const CScriptVarFunctionNativeClass &Copy) : CScriptVarFunctionNative(Copy), classPtr(Copy.classPtr), classFnc(Copy.classFnc) { } ///< Copy protected -> use clone for public -public: - virtual CScriptVarPtr clone() { return new CScriptVarFunctionNativeClass(*this); } - - virtual void callFunction(const CFunctionsScopePtr &c) { (classPtr->*classFnc)(c, jsUserData); } -private: - native *classPtr; - void (native::*classFnc)(const CFunctionsScopePtr &c, void *userdata); - template - friend CScriptVarFunctionNativePtr newScriptVar(CTinyJS* Context, native2 *ClassPtr, void (native2::*ClassFnc)(const CFunctionsScopePtr &, void *), void *Userdata); -}; -template -inline CScriptVarFunctionNativePtr newScriptVar(CTinyJS* Context, native *ClassPtr, void (native::*ClassFnc)(const CFunctionsScopePtr &, void *), void *Userdata) { return new CScriptVarFunctionNativeClass(Context, ClassPtr, ClassFnc, Userdata); } - - -////////////////////////////////////////////////////////////////////////// CScriptVarScope - -define_dummy_t(Scope); -define_ScriptVarPtr_Type(Scope); -class CScriptVarScope : public CScriptVarObject { -protected: // only derived classes or friends can be created - CScriptVarScope(CTinyJS *Context) // constructor for rootScope - : CScriptVarObject(Context) {} - virtual CScriptVarPtr clone(); - virtual bool isObject(); // { return false; } -public: - virtual ~CScriptVarScope(); - virtual CScriptVarPtr scopeVar(); ///< to create var like: var a = ... - virtual CScriptVarPtr scopeLet(); ///< to create var like: let a = ... - virtual CScriptVarLink *findInScopes(const std::string &childName); - virtual CScriptVarScopePtr getParent(); - friend define_newScriptVar_Fnc(Scope, CTinyJS* Context, Scope_t); -}; -inline define_newScriptVar_Fnc(Scope, CTinyJS* Context, Scope_t) { return new CScriptVarScope(Context); } - - -////////////////////////////////////////////////////////////////////////// CScriptVarScopeFnc - -define_dummy_t(ScopeFnc); -define_ScriptVarPtr_Type(ScopeFnc); -class CScriptVarScopeFnc : public CScriptVarScope { -protected: // only derived classes or friends can be created - CScriptVarScopeFnc(CTinyJS *Context, const CScriptVarScopePtr &Closure) // constructor for FncScope - : CScriptVarScope(Context), closure(Closure ? addChild(TINYJS_FUNCTION_CLOSURE_VAR, Closure, 0) : 0) {} -public: - virtual ~CScriptVarScopeFnc(); - virtual CScriptVarLink *findInScopes(const std::string &childName); -protected: - CScriptVarLink *closure; - friend define_newScriptVar_Fnc(ScopeFnc, CTinyJS* Context, ScopeFnc_t, const CScriptVarScopePtr &Closure); -}; -inline define_newScriptVar_Fnc(ScopeFnc, CTinyJS* Context, ScopeFnc_t, const CScriptVarScopePtr &Closure) { return new CScriptVarScopeFnc(Context, Closure); } - - -////////////////////////////////////////////////////////////////////////// CScriptVarScopeLet - -define_dummy_t(ScopeLet); -define_ScriptVarPtr_Type(ScopeLet); -class CScriptVarScopeLet : public CScriptVarScope { -protected: // only derived classes or friends can be created - CScriptVarScopeLet(const CScriptVarScopePtr &Parent); // constructor for LetScope -// : CScriptVarScope(Parent->getContext()), parent( context->getRoot() != Parent ? addChild(TINYJS_SCOPE_PARENT_VAR, Parent, 0) : 0) {} -public: - virtual ~CScriptVarScopeLet(); - virtual CScriptVarLink *findInScopes(const std::string &childName); - virtual CScriptVarPtr scopeVar(); ///< to create var like: var a = ... - virtual CScriptVarScopePtr getParent(); -protected: - CScriptVarLink *parent; - friend define_newScriptVar_Fnc(ScopeLet, CTinyJS* Context, ScopeLet_t, const CScriptVarScopePtr &Parent); -}; -inline define_newScriptVar_Fnc(ScopeLet, CTinyJS* , ScopeLet_t, const CScriptVarScopePtr &Parent) { return new CScriptVarScopeLet(Parent); } - - -////////////////////////////////////////////////////////////////////////// CScriptVarScopeWith - -define_dummy_t(ScopeWith); -define_ScriptVarPtr_Type(ScopeWith); -class CScriptVarScopeWith : public CScriptVarScopeLet { -protected: - CScriptVarScopeWith(const CScriptVarScopePtr &Parent, const CScriptVarPtr &With) - : CScriptVarScopeLet(Parent), with(addChild(TINYJS_SCOPE_WITH_VAR, With, 0)) {} - -public: - virtual ~CScriptVarScopeWith(); - virtual CScriptVarPtr scopeLet(); ///< to create var like: let a = ... - virtual CScriptVarLink *findInScopes(const std::string &childName); -private: - CScriptVarLink *with; - friend define_newScriptVar_Fnc(ScopeWith, CTinyJS* Context, ScopeWith_t, const CScriptVarScopePtr &Parent, const CScriptVarPtr &With); -}; -inline define_newScriptVar_Fnc(ScopeWith, CTinyJS* , ScopeWith_t, const CScriptVarScopePtr &Parent, const CScriptVarPtr &With) { return new CScriptVarScopeWith(Parent, With); } - -////////////////////////////////////////////////////////////////////////// -template -inline CScriptVarPtr CScriptVar::newScriptVar(T t) { return ::newScriptVar(context, t); } -////////////////////////////////////////////////////////////////////////// -// ----------------------------------------------------------------------------------- CSCRIPTVARSMARTLINK -class CScriptVarSmartLink : public fixed_size_object -{ -public: - CScriptVarSmartLink() : link(0){} - CScriptVarSmartLink(CScriptVarLink *Link) : link(Link) {} - explicit CScriptVarSmartLink (CScriptVarPtr Var) : link(0) { *this = Var; } - ~ CScriptVarSmartLink() { if(link && !link->IsOwned()) delete link; } - /// Copy - when copying a SmartLink to an other then the right hand side will lost your link - CScriptVarSmartLink (const CScriptVarSmartLink &Link) : link(0) { *this = Link; } - CScriptVarSmartLink &operator = (const CScriptVarSmartLink &Link); - ///- - CScriptVarSmartLink &operator = (CScriptVarLink *Link); - CScriptVarSmartLink &operator = (CScriptVarPtr Var); - CScriptVarLink *operator ->() const { return link; } - CScriptVarLink &operator *() { return *link; } - CScriptVarSmartLink &operator << (CScriptVarSmartLink &Link); - operator bool() const { return link != 0; } -// operator CScriptVarLink *() { return link; } - CScriptVarLink *&getLink() { return link; }; -private: - CScriptVarLink *link; -}; -template -CScriptVarPointer::CScriptVarPointer(const CScriptVarSmartLink &Link) : var(0) { if(Link) { var = dynamic_cast(Link->operator->()); ASSERT(var); } if(var) var->ref(); } - -// ----------------------------------------------------------------------------------- CTINYJS -class CTinyJS { -public: - CTinyJS(); - ~CTinyJS(); - - void execute(CScriptTokenizer &Tokenizer); - void execute(const char *Code, const std::string &File="", int Line=0, int Column=0); - void execute(const std::string &Code, const std::string &File="", int Line=0, int Column=0); - /** Evaluate the given code and return a link to a javascript object, - * useful for (dangerous) JSON parsing. If nothing to return, will return - * 'undefined' variable type. CScriptVarLink is returned as this will - * automatically unref the result as it goes out of scope. If you want to - * keep it, you must use ref() and unref() */ - CScriptVarLink evaluateComplex(CScriptTokenizer &Tokenizer); - /** Evaluate the given code and return a link to a javascript object, - * useful for (dangerous) JSON parsing. If nothing to return, will return - * 'undefined' variable type. CScriptVarLink is returned as this will - * automatically unref the result as it goes out of scope. If you want to - * keep it, you must use ref() and unref() */ - CScriptVarLink evaluateComplex(const char *code, const std::string &File="", int Line=0, int Column=0); - /** Evaluate the given code and return a link to a javascript object, - * useful for (dangerous) JSON parsing. If nothing to return, will return - * 'undefined' variable type. CScriptVarLink is returned as this will - * automatically unref the result as it goes out of scope. If you want to - * keep it, you must use ref() and unref() */ - CScriptVarLink evaluateComplex(const std::string &code, const std::string &File="", int Line=0, int Column=0); - /** Evaluate the given code and return a string. If nothing to return, will return - * 'undefined' */ - std::string evaluate(CScriptTokenizer &Tokenizer); - /** Evaluate the given code and return a string. If nothing to return, will return - * 'undefined' */ - std::string evaluate(const char *code, const std::string &File="", int Line=0, int Column=0); - /** Evaluate the given code and return a string. If nothing to return, will return - * 'undefined' */ - std::string evaluate(const std::string &code, const std::string &File="", int Line=0, int Column=0); - - /// add a native function to be called from TinyJS - /** example: - \code - void scRandInt(const CFunctionsScopePtr &c, void *userdata) { ... } - tinyJS->addNative("function randInt(min, max)", scRandInt, 0); - \endcode - - or - - \code - void scSubstring(const CFunctionsScopePtr &c, void *userdata) { ... } - tinyJS->addNative("function String.substring(lo, hi)", scSubstring, 0); - \endcode - or - - \code - class Class - { - public: - void scSubstring(const CFunctionsScopePtr &c, void *userdata) { ... } - }; - Class Instanz; - tinyJS->addNative("function String.substring(lo, hi)", &Instanz, &Class::*scSubstring, 0); - \endcode - */ - - CScriptVarFunctionNativePtr addNative(const std::string &funcDesc, JSCallback ptr, void *userdata=0); - template - CScriptVarFunctionNativePtr addNative(const std::string &funcDesc, C *class_ptr, void(C::*class_fnc)(const CFunctionsScopePtr &, void *), void *userdata=0) - { - return addNative(funcDesc, ::newScriptVar(this, class_ptr, class_fnc, userdata)); - } - - /// Send all variables to stdout - void trace(); - - const CScriptVarScopePtr &getRoot() { return root; }; /// gets the root of symbol table - // CScriptVar *root; /// root of symbol table - - template - CScriptVarPtr newScriptVar(T t) { return ::newScriptVar(this, t); } -private: - static bool noexecute; - CScriptTokenizer *t; /// current tokenizer - int runtimeFlags; - std::vector loop_labels; - std::vectorscopes; - CScriptVarScopePtr root; - - const CScriptVarScopePtr &scope() { return scopes.back(); } - - class CScopeControl { - private: - CScopeControl(const CScopeControl& Copy); // no copy - CScopeControl& operator =(const CScopeControl& Copy); - public: - CScopeControl(CTinyJS* Context) : context(Context), count(0) {} - ~CScopeControl() { while(count--) {CScriptVarScopePtr parent = context->scopes.back()->getParent(); if(parent) context->scopes.back() = parent; else context->scopes.pop_back() ;} } - void addFncScope(const CScriptVarScopePtr &Scope) { context->scopes.push_back(Scope); count++; } - void addLetScope() { context->scopes.back() = ::newScriptVar(context, ScopeLet, context->scopes.back()); count++; } - void addWithScope(const CScriptVarPtr &With) { context->scopes.back() = ::newScriptVar(context, ScopeWith, context->scopes.back(), With); count++; } - private: - CTinyJS *context; - int count; - }; - friend class CScopeControl; -public: - CScriptVarPtr objectPrototype; /// Built in object class - CScriptVarPtr arrayPrototype; /// Built in array class - CScriptVarPtr stringPrototype; /// Built in string class - CScriptVarPtr numberPrototype; /// Built in string class - CScriptVarPtr functionPrototype; /// Built in function class -private: - CScriptVarPtr exceptionVar; /// containing the exception var by (runtimeFlags&RUNTIME_THROW) == true; - - void CheckRightHandVar(bool &execute, CScriptVarSmartLink &link) - { - if(execute && link && !link->IsOwned() && !link->owner && link->name.length()>0) - throwError(execute, link->name + " is not defined", t->getPrevPos()); - } - - void CheckRightHandVar(bool &execute, CScriptVarSmartLink &link, CScriptTokenizer::ScriptTokenPosition &Pos) - { - if(execute && link && !link->IsOwned() && !link->owner && link->name.length()>0) - throwError(execute, link->name + " is not defined", Pos); - } - - // function call -public: -// CScriptVarSmartLink callFunction(CScriptVarSmartLink &Function, std::vector &Arguments, const CScriptVarPtr& This, bool &execute); - CScriptVarPtr callFunction(const CScriptVarPtr &Function, std::vector &Arguments, const CScriptVarPtr& This, bool &execute); -private: - // - CScriptVarSmartLink getValue(CScriptVarSmartLink Var, bool execute); - CScriptVarSmartLink setValue(CScriptVarSmartLink Var); - // parsing - in order of precedence - CScriptVarSmartLink execute_literals(bool &execute); - CScriptVarSmartLink execute_member(CScriptVarSmartLink &parent, bool &execute); - CScriptVarSmartLink execute_function_call(bool &execute); - CScriptVarSmartLink execute_unary(bool &execute); - CScriptVarSmartLink execute_term(bool &execute); - CScriptVarSmartLink execute_expression(bool &execute); - CScriptVarSmartLink execute_binary_shift(bool &execute); - CScriptVarSmartLink execute_relation(bool &execute, int set=LEX_EQUAL, int set_n='<'); - CScriptVarSmartLink execute_binary_logic(bool &execute, int op='|', int op_n1='^', int op_n2='&'); - CScriptVarSmartLink execute_logic(bool &execute, int op=LEX_OROR, int op_n=LEX_ANDAND); - CScriptVarSmartLink execute_condition(bool &execute); - CScriptVarSmartLink execute_assignment(bool &execute); - CScriptVarSmartLink execute_base(bool &execute); - void execute_block(bool &execute); - CScriptVarSmartLink execute_statement(bool &execute); - // parsing utility functions - CScriptVarSmartLink parseFunctionDefinition(CScriptToken &FncToken); -// CScriptVarSmartLink parseFunctionDefinition(); -// void parseFunctionArguments(CScriptVar *funcVar); - CScriptVarSmartLink parseFunctionsBodyFromString(const std::string &Parameter, const std::string &FncBody); - - CScriptVarLink *findInScopes(const std::string &childName); ///< Finds a child, looking recursively up the scopes - /// Get all Keynames of en given object (optionial look up the prototype-chain) - void keys(STRING_VECTOR_t &Keys, CScriptVarPtr object, bool WithPrototypeChain); - /// Look up in any parent classes of the given object - CScriptVarLink *findInPrototypeChain(CScriptVarPtr object, const std::string &name); - CScriptVarFunctionNativePtr addNative(const std::string &funcDesc, CScriptVarFunctionNativePtr Var); - - /// throws an Error - void throwError(bool &execute, const std::string &message); - void throwError(bool &execute, const std::string &message, CScriptTokenizer::ScriptTokenPosition &Pos); - - /// native Object-Constructors & prototype-functions - - void native_Object(const CFunctionsScopePtr &c, void *data); - void native_Object_hasOwnProperty(const CFunctionsScopePtr &c, void *data); - - void native_Array(const CFunctionsScopePtr &c, void *data); - - void native_String(const CFunctionsScopePtr &c, void *data); - - void native_Number(const CFunctionsScopePtr &c, void *data); - - void native_Function(const CFunctionsScopePtr &c, void *data); - void native_Function_call(const CFunctionsScopePtr &c, void *data); - void native_Function_apply(const CFunctionsScopePtr &c, void *data); - - /// globale function - - void native_eval(const CFunctionsScopePtr &c, void *data); - void native_isNAN(const CFunctionsScopePtr &c, void *data); - void native_isFinite(const CFunctionsScopePtr &c, void *data); - void native_parseInt(const CFunctionsScopePtr &c, void *data); - void native_parseFloat(const CFunctionsScopePtr &c, void *data); - - - - void native_JSON_parse(const CFunctionsScopePtr &c, void *data); - - - int uniqueID; -public: - int getUniqueID() { return ++uniqueID; } - CScriptVar *first; - void ClearLostVars(const CScriptVarPtr &extra=CScriptVarPtr()); -}; - -#endif +/* + * TinyJS + * + * A single-file Javascript-alike engine + * + * Authored By Gordon Williams + * + * Copyright (C) 2009 Pur3 Ltd + * + + * 42TinyJS + * + * A fork of TinyJS with the goal to makes a more JavaScript/ECMA compliant engine + * + * Authored / Changed By Armin Diedering + * + * Copyright (C) 2010 ardisoft + * + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef TINYJS_H +#define TINYJS_H + +#include +#include +#include +#include +#include "pool_allocator.h" +#include +#include + +#ifndef ASSERT +# define ASSERT(X) assert(X) +#endif + +#ifndef __GNUC__ +# define __attribute__(x) +#endif + +#ifdef _MSC_VER +# define DEPRECATED(_Text) __declspec(deprecated(_Text)) +#elif defined(__GNUC__) +# define DEPRECATED(_Text) __attribute__ ((deprecated)) +#else +# define DEPRECATED(_Text) +#endif + +#undef TRACE +#ifndef TRACE +#define TRACE printf +#endif // TRACE + +enum LEX_TYPES { + LEX_EOF = 0, +#define LEX_RELATIONS_1_BEGIN LEX_EQUAL + LEX_EQUAL = 256, + LEX_TYPEEQUAL, + LEX_NEQUAL, + LEX_NTYPEEQUAL, +#define LEX_RELATIONS_1_END LEX_NTYPEEQUAL + LEX_LEQUAL, + LEX_GEQUAL, +#define LEX_SHIFTS_BEGIN LEX_LSHIFT + LEX_LSHIFT, + LEX_RSHIFT, + LEX_RSHIFTU, // unsigned +#define LEX_SHIFTS_END LEX_RSHIFTU + LEX_PLUSPLUS, + LEX_MINUSMINUS, + LEX_ANDAND, + LEX_OROR, + LEX_INT, + +#define LEX_ASSIGNMENTS_BEGIN LEX_PLUSEQUAL + LEX_PLUSEQUAL, + LEX_MINUSEQUAL, + LEX_ASTERISKEQUAL, + LEX_SLASHEQUAL, + LEX_PERCENTEQUAL, + LEX_LSHIFTEQUAL, + LEX_RSHIFTEQUAL, + LEX_RSHIFTUEQUAL, // unsigned + LEX_ANDEQUAL, + LEX_OREQUAL, + LEX_XOREQUAL, +#define LEX_ASSIGNMENTS_END LEX_XOREQUAL + +#define LEX_TOKEN_STRING_BEGIN LEX_ID + LEX_ID, + LEX_STR, + LEX_REGEXP, + LEX_T_LABEL, + LEX_T_DUMMY_LABEL, + LEX_T_LOOP_LABEL, +#define LEX_TOKEN_STRING_END LEX_T_LOOP_LABEL + + LEX_FLOAT, + + // reserved words + LEX_R_IF, + LEX_R_ELSE, +#define LEX_TOKEN_LOOP_BEGIN LEX_R_DO + LEX_R_DO, + LEX_R_WHILE, + LEX_R_FOR, + LEX_T_FOR_IN, + LEX_T_FOR_EACH_IN, +#define LEX_TOKEN_LOOP_END LEX_T_FOR_EACH_IN + LEX_R_IN, + LEX_R_BREAK, + LEX_R_CONTINUE, + LEX_R_FUNCTION, + LEX_R_RETURN, + LEX_R_VAR, + LEX_R_LET, + LEX_R_WITH, + LEX_R_TRUE, + LEX_R_FALSE, + LEX_R_NULL, + LEX_R_NEW, + LEX_R_TRY, + LEX_R_CATCH, + LEX_R_FINALLY, + LEX_R_THROW, + LEX_R_TYPEOF, + LEX_R_VOID, + LEX_R_DELETE, + LEX_R_INSTANCEOF, + LEX_R_SWITCH, + LEX_R_CASE, + LEX_R_DEFAULT, + + // special token +// LEX_T_FILE, + LEX_T_FUNCTION_OPERATOR, + LEX_T_GET, + LEX_T_SET, + + LEX_T_SKIP, + +}; +#define LEX_TOKEN_DATA_STRING(tk) ((LEX_TOKEN_STRING_BEGIN<= tk && tk <= LEX_TOKEN_STRING_END)) +#define LEX_TOKEN_DATA_FLOAT(tk) (tk==LEX_FLOAT) +#define LEX_TOKEN_DATA_FUNCTION(tk) (tk==LEX_R_FUNCTION || tk==LEX_T_FUNCTION_OPERATOR || tk==LEX_T_SET || tk==LEX_T_GET) +#define LEX_TOKEN_DATA_SIMPLE(tk) (!LEX_TOKEN_DATA_STRING(tk) && !LEX_TOKEN_DATA_FLOAT(tk) && !LEX_TOKEN_DATA_FUNCTION(tk)) +/* +enum SCRIPTVAR_FLAGS { + SCRIPTVAR_UNDEFINED = 0, + SCRIPTVAR_FUNCTION = 1<<0, + SCRIPTVAR_OBJECT = 1<<1, + SCRIPTVAR_ARRAY = 1<<2, + SCRIPTVAR_DOUBLE = 1<<3, // floating point double + SCRIPTVAR_INTEGER = 1<<4, // integer number + SCRIPTVAR_BOOLEAN = 1<<5, // boolean + SCRIPTVAR_STRING = 1<<6, // string + SCRIPTVAR_NULL = 1<<7, // it seems null is its own data type + SCRIPTVAR_INFINITY = 1<<8, // it seems infinity is its own data type + SCRIPTVAR_NAN = 1<<9, // it seems NaN is its own data type + SCRIPTVAR_ACCESSOR = 1<<10, // it seems an Object with get() and set() + + SCRIPTVAR_END_OF_TYPES = 1<<15, // it seems NaN is its own data type + + SCRIPTVAR_NATIVE_FNC = 1<<16, // to specify this is a native function + SCRIPTVAR_NATIVE_MFNC = 1<<17, // to specify this is a native function from class->memberFunc + + SCRIPTVAR_NUMBERMASK = SCRIPTVAR_DOUBLE | + SCRIPTVAR_INTEGER, + SCRIPTVAR_NUMERICMASK = SCRIPTVAR_NUMBERMASK | + SCRIPTVAR_NULL | + SCRIPTVAR_BOOLEAN, + + SCRIPTVAR_VARTYPEMASK = SCRIPTVAR_END_OF_TYPES - 1, + + SCRIPTVAR_NATIVE = SCRIPTVAR_NATIVE_FNC | + SCRIPTVAR_NATIVE_MFNC, +}; +*/ +enum SCRIPTVARLINK_FLAGS { + SCRIPTVARLINK_OWNED = 1<<0, + SCRIPTVARLINK_WRITABLE = 1<<1, + SCRIPTVARLINK_DELETABLE = 1<<2, + SCRIPTVARLINK_ENUMERABLE = 1<<3, + SCRIPTVARLINK_HIDDEN = 1<<4, + SCRIPTVARLINK_DEFAULT = SCRIPTVARLINK_WRITABLE | SCRIPTVARLINK_DELETABLE | SCRIPTVARLINK_ENUMERABLE, + SCRIPTVARLINK_NATIVEDEFAULT = SCRIPTVARLINK_WRITABLE, +}; +enum RUNTIME_FLAGS { + RUNTIME_CAN_RETURN = 1<<0, + + RUNTIME_CAN_BREAK = 1<<1, + RUNTIME_BREAK = 1<<2, + RUNTIME_BREAK_MASK = RUNTIME_CAN_BREAK | RUNTIME_BREAK, + + RUNTIME_CAN_CONTINUE = 1<<3, + RUNTIME_CONTINUE = 1<<4, + RUNTIME_LOOP_MASK = RUNTIME_BREAK_MASK | RUNTIME_CAN_CONTINUE | RUNTIME_CONTINUE, + RUNTIME_LOOP_CAN_MASK = RUNTIME_CAN_BREAK | RUNTIME_CAN_CONTINUE, + RUNTIME_LOOP_STATE_MASK = RUNTIME_BREAK | RUNTIME_CONTINUE, + + RUNTIME_NEW = 1<<5, + + RUNTIME_CAN_THROW = 1<<6, + RUNTIME_THROW = 1<<7, + RUNTIME_THROW_MASK = RUNTIME_CAN_THROW | RUNTIME_THROW, + +}; + +#define SAVE_RUNTIME_RETURN int old_return_runtimeFlags = runtimeFlags & RUNTIME_CAN_RETURN +#define RESTORE_RUNTIME_RETURN runtimeFlags = (runtimeFlags & ~RUNTIME_CAN_RETURN) | old_pass_runtimeFlags +#define SET_RUNTIME_CANRETURN runtimeFlags |= RUNTIME_CAN_RETURN +#define IS_RUNTIME_CANRETURN ((runtimeFlags & RUNTIME_CAN_RETURN) == RUNTIME_CAN_RETURN) + +enum ERROR_TYPES { + Error = 0, + EvalError, + RangeError, + ReferenceError, + SyntaxError, + TypeError +}; +#define ERROR_MAX TypeError +#define ERROR_COUNT (ERROR_MAX+1) +extern const char *ERROR_NAME[]; + + +#define TINYJS_RETURN_VAR "return" +#define TINYJS_LOKALE_VAR "__locale__" +#define TINYJS_ANONYMOUS_VAR "__anonymous__" +#define TINYJS_ARGUMENTS_VAR "arguments" +#define TINYJS___PROTO___VAR "__proto__" +#define TINYJS_PROTOTYPE_CLASS "prototype" +#define TINYJS_FUNCTION_CLOSURE_VAR "__function_closure__" +#define TINYJS_SCOPE_PARENT_VAR "__scope_parent__" +#define TINYJS_SCOPE_WITH_VAR "__scope_with__" +#define TINYJS_ACCESSOR_GET_VAR "__accessor_get__" +#define TINYJS_ACCESSOR_SET_VAR "__accessor_set__" +#define TINYJS_TEMP_NAME "" +#define TINYJS_BLANK_DATA "" +#define TINYJS_NEGATIVE_INFINITY_DATA "-1" +#define TINYJS_POSITIVE_INFINITY_DATA "+1" + +typedef std::vector STRING_VECTOR_t; +typedef STRING_VECTOR_t::iterator STRING_VECTOR_it; + +typedef std::set STRING_SET_t; +typedef STRING_SET_t::iterator STRING_SET_it; + +/// convert the given string into a quoted string suitable for javascript +std::string getJSString(const std::string &str); +/// convert the given int into a string +std::string int2string(int intData); +/// convert the given double into a string +std::string float2string(const double &floatData); + +////////////////////////////////////////////////////////////////////////// +/// CScriptException +////////////////////////////////////////////////////////////////////////// + +class CScriptException { +public: + ERROR_TYPES errorType; + std::string message; + std::string fileName; + int lineNumber; + int column; + CScriptException(const std::string &Message, const std::string &File, int Line=-1, int Column=-1) : + errorType(Error), message(Message), fileName(File), lineNumber(Line+1), column(Column+1){} + CScriptException(ERROR_TYPES ErrorType, const std::string &Message, const std::string &File, int Line=-1, int Column=-1) : + errorType(ErrorType), message(Message), fileName(File), lineNumber(Line+1), column(Column+1){} + CScriptException(const std::string &Message, const char *File="", int Line=-1, int Column=-1) : + errorType(Error), message(Message), fileName(File), lineNumber(Line+1), column(Column+1){} + CScriptException(ERROR_TYPES ErrorType, const std::string &Message, const char *File="", int Line=-1, int Column=-1) : + errorType(ErrorType), message(Message), fileName(File), lineNumber(Line+1), column(Column+1){} + std::string toString(); +}; + +////////////////////////////////////////////////////////////////////////// +/// CSCRIPTLEX +////////////////////////////////////////////////////////////////////////// + +class CScriptLex +{ +public: + CScriptLex(const char *Code, const std::string &File="", int Line=0, int Column=0); + + int tk; ///< The type of the token that we have + int last_tk; ///< The type of the last token that we have + const char *tokenStart; + std::string tkStr; ///< Data contained in the token we have here + + void check(int expected_tk, int alternate_tk=-1); ///< Lexical check wotsit + void match(int expected_tk, int alternate_tk=-1); ///< Lexical match wotsit + static std::string getTokenStr(int token); ///< Get the string representation of the given token + void reset(const char *toPos, int line, const char *LineStart); ///< Reset this lex so we can start again + + std::string currentFile; + int currentLine; + const char *currentLineStart; + int currentColumn() { return tokenStart-currentLineStart; } + bool lineBreakBeforeToken; +private: + const char *data; + const char *dataPos; + char currCh, nextCh; + + void getNextCh(); + void getNextToken(); ///< Get the text token from our text string +}; + +// ----------------------------------------------------------------------------------- CSCRIPTTOKEN +class CScriptToken; +typedef std::vector TOKEN_VECT; +typedef std::vector::iterator TOKEN_VECT_it; +class CScriptTokenData +{ +public: + virtual ~CScriptTokenData() {} + void ref() { refs++; } + void unref() { if(--refs == 0) delete this; } +protected: + CScriptTokenData() : refs(0){} +private: + int refs; +}; +class CScriptTokenDataString : public fixed_size_object, public CScriptTokenData { +public: + CScriptTokenDataString(const std::string &String) : tokenStr(String) {} + std::string tokenStr; +private: +}; +class CScriptTokenDataFnc : public fixed_size_object, public CScriptTokenData { +public: + std::string file; + int line; + std::string name; + STRING_VECTOR_t arguments; + TOKEN_VECT body; +private: +}; + +class CScriptTokenizer; +/* + a Token needs 8 Byte + 2 Bytes for the Row-Position of the Token + 2 Bytes for the Token self + and + 4 Bytes for special Datas in an union + e.g. an int for interger-literals + or pointer for double-literals, + for string-literals or for functions +*/ +class CScriptToken : public fixed_size_object +{ +public: + CScriptToken() : line(0), column(0), token(0) {} + CScriptToken(CScriptLex *l, int Match=-1, int Alternate=-1); + CScriptToken(uint16_t Tk, int IntData=0); + CScriptToken(uint16_t Tk, const std::string &TkStr); + CScriptToken(const CScriptToken &Copy) : token(0) { *this = Copy; } + CScriptToken &operator =(const CScriptToken &Copy); + ~CScriptToken() { clear(); } + + int &Int() { ASSERT(LEX_TOKEN_DATA_SIMPLE(token)); return intData; } + std::string &String() { ASSERT(LEX_TOKEN_DATA_STRING(token)); return stringData->tokenStr; } + double &Float() { ASSERT(LEX_TOKEN_DATA_FLOAT(token)); return *floatData; } + CScriptTokenDataFnc &Fnc() { ASSERT(LEX_TOKEN_DATA_FUNCTION(token)); return *fncData; } + uint16_t line; + uint16_t column; + uint16_t token; + void print(std::string &indent); + std::string getParsableString(const std::string Indent=""); + std::string getParsableString(std::string &indentString, int &newln, const std::string &indent); ///< newln ==> 1=inject indentString, 2=inject \n, 3=last='{' +private: + + void clear(); + union { + int intData; + double *floatData; + CScriptTokenDataString *stringData; + CScriptTokenDataFnc *fncData; + }; +}; + +// ----------------------------------------------------------------------------------- CSCRIPTTOKENIZER +/* + the tokenizer converts the code in an Vector with Tokens +*/ +class CScriptTokenizer +{ +public: + struct ScriptTokenPosition { + ScriptTokenPosition(TOKEN_VECT *Tokens) : tokens(Tokens), pos(tokens->begin())/*, currentLine(0)*//*, currentColumn(0)*/ {} + bool operator ==(const ScriptTokenPosition &eq) { return pos == eq.pos; } + ScriptTokenPosition &operator =(const ScriptTokenPosition ©) { + tokens=copy.tokens; pos=copy.pos; +// currentLine=copy.currentLine; + return *this; + } + TOKEN_VECT *tokens; + TOKEN_VECT_it pos; + int currentLine() { return pos->line; } + int currentColumn() { return pos->column; } + }; + CScriptTokenizer(); + CScriptTokenizer(CScriptLex &Lexer); + CScriptTokenizer(const char *Code, const std::string &File="", int Line=0, int Column=0); + void tokenizeCode(CScriptLex &Lexer); + + CScriptToken &getToken() { return *(tokenScopeStack.back().pos); } + void getNextToken(); + bool check(int ExpectedToken, int AlternateToken=-1); + void match(int ExpectedToken, int AlternateToken=-1); + void pushTokenScope(TOKEN_VECT &Tokens); + ScriptTokenPosition &getPos() { return tokenScopeStack.back(); } + void setPos(ScriptTokenPosition &TokenPos); + ScriptTokenPosition &getPrevPos() { return prevPos; } + void skip(int Tokens); + int tk; // current Token + std::string currentFile; + int currentLine() { return getPos().currentLine();} + int currentColumn() { return getPos().currentColumn();} + std::string &tkStr() { return getToken().String(); } +private: + void tokenizeCatch(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); + void tokenizeTry(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); + void tokenizeSwitch(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); + void tokenizeWith(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); + void tokenizeWhile(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); + void tokenizeDo(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); + void tokenizeIf(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); + void tokenizeFor(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); + void tokenizeFunction(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); + void tokenizeLet(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); + void tokenizeVar(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); + + void tokenizeLiteral(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); + void tokenizeMember(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); + void tokenizeFunctionCall(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); + void tokenizeSubExpression(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); + void tokenizeCondition(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); + void tokenizeAssignment(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); + void tokenizeExpression(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); + void tokenizeBlock(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); + void tokenizeStatement(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); + + int pushToken(TOKEN_VECT &Tokens, int Match=-1, int Alternate=-1); + void throwTokenNotExpected(); + CScriptLex *l; + TOKEN_VECT tokens; + ScriptTokenPosition prevPos; + std::vector tokenScopeStack; +}; + + +////////////////////////////////////////////////////////////////////////// +/// forward-declaration +////////////////////////////////////////////////////////////////////////// + +class CScriptVar; +class CScriptVarPtr; +template class CScriptVarPointer; +class CScriptVarLink; +class CScriptVarLinkPtr; + +class CScriptVarScopeFnc; +typedef CScriptVarPointer CFunctionsScopePtr; +typedef void (*JSCallback)(const CFunctionsScopePtr &var, void *userdata); + +class CTinyJS; + + + +////////////////////////////////////////////////////////////////////////// +/// CScriptVar +////////////////////////////////////////////////////////////////////////// + +typedef std::vector SCRIPTVAR_CHILDS_t; +typedef SCRIPTVAR_CHILDS_t::iterator SCRIPTVAR_CHILDS_it; +typedef SCRIPTVAR_CHILDS_t::const_iterator SCRIPTVAR_CHILDS_cit; + +class CScriptVar : public fixed_size_object { +protected: + CScriptVar(CTinyJS *Context, const CScriptVarPtr &Prototype); ///< Create + CScriptVar(const CScriptVar &Copy); ///< Copy protected -> use clone for public +private: + CScriptVar & operator=(const CScriptVar &Copy); ///< private -> no assignment-Copy +public: + virtual ~CScriptVar(void); + virtual CScriptVarPtr clone()=0; + + /// Type + virtual bool isObject(); ///< is CScriptVarObject + virtual bool isError(); ///< is CScriptVarObject + virtual bool isArray(); ///< is CScriptVarArray + virtual bool isAccessor(); ///< is CScriptVarAccessor + virtual bool isNull(); ///< is CScriptVarNull + virtual bool isUndefined();///< is CScriptVarUndefined + virtual bool isNaN(); ///< is CScriptVarNaN + virtual bool isString(); ///< is CScriptVarString + virtual bool isInt(); ///< is CScriptVarInteger + virtual bool isBool(); ///< is CScriptVarBool + virtual int isInfinity(); //< is CScriptVarInfinity ///< +1==POSITIVE_INFINITY, -1==NEGATIVE_INFINITY, 0==is not an InfinityVar + virtual bool isDouble(); ///< is CScriptVarDouble + + virtual bool isNumber(); ///< is CScriptVarInteger / CScriptVarDouble + virtual bool isNumeric(); ///< is CScriptVarNull + virtual bool isPrimitive();///< isNull | isUndefined | isNaN | isString | isInt | isDouble | isInfinity + + virtual bool isFunction(); ///< is CScriptVarFunction / CScriptVarFunctionNativeCallback / CScriptVarFunctionNativeClass + virtual bool isNative(); ///< is CScriptVarFunctionNativeCallback / CScriptVarFunctionNativeClass + + bool isBasic() { return Childs.empty(); } ///< Is this *not* an array/object/etc + + + /// Value + virtual int getInt(); ///< return 0 + virtual bool getBool(); ///< return false + virtual double getDouble(); ///< return 0.0 + virtual CScriptTokenDataFnc *getFunctionData(); ///< { return 0; } + virtual std::string getString(); ///< return "" + + virtual std::string getParsableString(const std::string &indentString, const std::string &indent); ///< get Data as a parsable javascript string + virtual std::string getVarType()=0; + CScriptVarPtr getPrimitivVar(); ///< if the var an ObjectType gets the valueOf; if valueOf of an ObjectType gets toString / otherwise gets the Var itself + CScriptVarPtr getPrimitivVar(bool execute); ///< if the var an ObjectType gets the valueOf; if valueOf of an ObjectType gets toString / otherwise gets the Var itself + virtual CScriptVarPtr getNumericVar(); ///< returns an Integer, a Double, an Infinity or a NaN + CScriptVarPtr valueOf(bool execute); + virtual CScriptVarPtr _valueOf(bool execute); + CScriptVarPtr toString(bool execute, int radix=0); + virtual CScriptVarPtr _toString(bool execute, int radix=0); + /// find + CScriptVarLink *findChild(const std::string &childName); ///< Tries to find a child with the given name, may return 0 + CScriptVarLink *findChildInPrototypeChain(const std::string &childName); + CScriptVarLink *findChildWithPrototypeChain(const std::string &childName); + CScriptVarLink *findChildByPath(const std::string &path); ///< Tries to find a child with the given path (separated by dots) + CScriptVarLink *findChildOrCreate(const std::string &childName/*, int varFlags=SCRIPTVAR_UNDEFINED*/); ///< Tries to find a child with the given name, or will create it with the given flags + CScriptVarLink *findChildOrCreateByPath(const std::string &path); ///< Tries to find a child with the given path (separated by dots) + void keys(STRING_SET_t &Keys, bool OnlyEnumerable=true, uint32_t ID=0); + /// add & remove + CScriptVarLink *addChild(const std::string &childName, const CScriptVarPtr &child, int linkFlags = SCRIPTVARLINK_DEFAULT); + CScriptVarLink *addChildNoDup(const std::string &childName, const CScriptVarPtr &child, int linkFlags = SCRIPTVARLINK_DEFAULT); ///< add a child overwriting any with the same name + bool removeLink(CScriptVarLinkPtr &link); ///< Remove a specific link (this is faster than finding via a child) + bool removeLink(CScriptVarLink *&link); ///< Remove a specific link (this is faster than finding via a child) + void removeAllChildren(); + + /// ARRAY + CScriptVarPtr getArrayIndex(int idx); ///< The the value at an array index + void setArrayIndex(int idx, const CScriptVarPtr &value); ///< Set the value at an array index + int getArrayLength(); ///< If this is an array, return the number of items in it (else 0) + + ////////////////////////////////////////////////////////////////////////// + int getChildren() { return Childs.size(); } ///< Get the number of children + CTinyJS *getContext() { return context; } + CScriptVarPtr mathsOp(const CScriptVarPtr &b, int op); ///< do a maths op with another script variable + + void trace(const std::string &name = ""); ///< Dump out the contents of this using trace + void trace(std::string &indentStr, uint32_t uniqueID, const std::string &name = ""); ///< Dump out the contents of this using trace + std::string getFlagsAsString(); ///< For debugging - just dump a string version of the flags +// void getJSON(std::ostringstream &destination, const std::string linePrefix=""); ///< Write out all the JS code needed to recreate this script variable to the stream (as JSON) + + SCRIPTVAR_CHILDS_t Childs; + + /// For memory management/garbage collection +private: + CScriptVar *ref(); ///< Add reference to this variable + void unref(); ///< Remove a reference, and delete this variable if required +public: + int getRefs(); ///< Get the number of references to this script variable + template + operator T *(){ T *ret = dynamic_cast(this); ASSERT(ret!=0); return ret; } + template + T *get(){ T *ret = dynamic_cast(this); ASSERT(ret!=0); return ret; } + + template CScriptVarPtr newScriptVar(T t); // { return ::newScriptVar(context, t); } + template const CScriptVarPtr &constScriptVar(T t); // { return ::newScriptVar(context, t); } + void setTemporaryID(uint32_t ID) { temporaryID = ID; } + virtual void setTemporaryID_recursive(uint32_t ID); + uint32_t getTempraryID() { return temporaryID; } +protected: + CTinyJS *context; + int refs; ///< The number of references held to this - used for garbage collection + CScriptVar *prev; +public: + CScriptVar *next; + uint32_t temporaryID; + + friend class CScriptVarPtr; +}; + +////////////////////////////////////////////////////////////////////////// +/// CScriptVarPrimitive +////////////////////////////////////////////////////////////////////////// + +class CScriptVarPrimitive : public CScriptVar { +protected: + CScriptVarPrimitive(CTinyJS *Context, const CScriptVarPtr &Prototype) : CScriptVar(Context, Prototype) {} + CScriptVarPrimitive(const CScriptVarPrimitive &Copy) : CScriptVar(Copy) {} +public: + +}; +////////////////////////////////////////////////////////////////////////// +/// CScriptVarPtr +////////////////////////////////////////////////////////////////////////// + +class CScriptVarPtr { +public: + // construct + CScriptVarPtr() : var(0) {} ///< 0-Pointer + CScriptVarPtr(CScriptVar *Var) : var(Var) { if(var) var->ref(); } // creates a new CScriptVar (from new); + + + CScriptVarPtr(const CScriptVarLink *Link); +// CScriptVarPtr(bool &execute, const CScriptVarLink *Link); + CScriptVarPtr(const CScriptVarLinkPtr &Link); + + // from CScriptVarLinkPtr +// CScriptVarPtr(bool &execute, const CScriptVarLinkPtr &Link);// : var(0) { *this = Link.get(execute); } +// CScriptVarPtr(const CScriptVarLinkPtr &Link);// : var(0) { *this = Link.get(); } +// CScriptVarPtr & operator =(const CScriptVarLinkPtr &Link); // { *this = 10; } +public: + // CScriptVarPtr & operator =(const CScriptVarLinkPtr &Link, bool &execute) : { + // *this = Link.get(execute)); + // } + + // copy + CScriptVarPtr(const CScriptVarPtr &Copy) : var(0) { *this = Copy; } + CScriptVarPtr& operator=(const CScriptVarPtr &Copy) { + if(var != Copy.var) { + if(var) var->unref(); + var = Copy.var; if(var) var->ref(); + } + return *this; + } + // deconstruct + ~CScriptVarPtr() { if(var) var->unref(); } + + operator bool() const { return var!=0; } + + bool operator ==(const CScriptVarPtr &Other) const { return var == Other.var; } + bool operator !=(const CScriptVarPtr &Other) const { return var != Other.var; } + + CScriptVar * operator ->() const { return var; } + CScriptVar *getVar() const { return var; } + + /// temporary + // CScriptVarPtr(CScriptVarLink *Var); + + void clear() { if(var) var->unref(); var=0; } +protected: + CScriptVar *var; +}; + +////////////////////////////////////////////////////////////////////////// +/// CScriptVarPointer - template +////////////////////////////////////////////////////////////////////////// + +template +class CScriptVarPointer : public CScriptVarPtr { +public: + CScriptVarPointer() {} + CScriptVarPointer(const CScriptVarPtr &Copy) : CScriptVarPtr(Copy) { if(var) { var = dynamic_cast(var); } } +// CScriptVarPointer(const CScriptVarPointer &Copy) : CScriptVarPtr(Copy) { if(var) { var = dynamic_cast(var); } } + C * operator ->() const { C *Var = dynamic_cast(var); ASSERT(var && Var); return Var; } +}; + +////////////////////////////////////////////////////////////////////////// +/// CScriptVarLink +////////////////////////////////////////////////////////////////////////// + + +class CScriptVarLink : public fixed_size_object +{ +public: + CScriptVarLink(const CScriptVarPtr &var, const std::string &name = TINYJS_TEMP_NAME, int flags = SCRIPTVARLINK_DEFAULT); + CScriptVarLink(const CScriptVarLink &link); ///< Copy constructor + ~CScriptVarLink(); + + const std::string &getName() const { return name; } + + int getFlags() { return flags; } + + const CScriptVarPtr &getVarPtr() const { return var; } + CScriptVarPtr &setVarPtr(const CScriptVarPtr &Var) { return var = Var; } + + void replaceWith(const CScriptVarPtr &newVar); ///< simple Replace the Variable pointed to + + bool isOwned() const { return (flags & SCRIPTVARLINK_OWNED) != 0; } + void setOwned(bool On) { On ? (flags |= SCRIPTVARLINK_OWNED) : (flags &= ~SCRIPTVARLINK_OWNED); } + + bool isOwner() const { return owner!=0; } + + bool isWritable() const { return (flags & SCRIPTVARLINK_WRITABLE) != 0; } + void setWritable(bool On) { On ? (flags |= SCRIPTVARLINK_WRITABLE) : (flags &= ~SCRIPTVARLINK_DELETABLE); } + bool isDeletable() const { return (flags & SCRIPTVARLINK_DELETABLE) != 0; } + void setDeletable(bool On) { On ? (flags |= SCRIPTVARLINK_DELETABLE) : (flags &= ~SCRIPTVARLINK_DELETABLE); } + bool isEnumerable() const { return (flags & SCRIPTVARLINK_ENUMERABLE) != 0; } + void setEnumerable(bool On) { On ? (flags |= SCRIPTVARLINK_ENUMERABLE) : (flags &= ~SCRIPTVARLINK_ENUMERABLE); } + bool isHidden() const { return (flags & SCRIPTVARLINK_HIDDEN) != 0; } + void setHidden(bool On) { On ? (flags |= SCRIPTVARLINK_HIDDEN) : (flags &= ~SCRIPTVARLINK_HIDDEN); } + + const CScriptVarPtr &operator ->() const { return var; } + const CScriptVarPtr &operator *() const { return var; } + + // TODO remove this (make's saver) + CScriptVar *getOwner() { return owner; }; + void setOwner(CScriptVar *Owner) { owner = Owner; } +protected: + std::string name; + CScriptVar *owner; // pointer to the owner CScriptVar + uint32_t flags; + CScriptVarPtr var; + char dummy[24]; + friend class CScriptVarLinkPtr; +}; + +// to create a CScriptVarPtr always call getValue to call getter if's needed +inline CScriptVarPtr::CScriptVarPtr(const CScriptVarLink *Link) : var(0) { if(Link) { var = Link->getVarPtr()->ref(); } } + + +////////////////////////////////////////////////////////////////////////// +/// CScriptVarLinkTmp / CScriptVarLinkTmpPtr +////////////////////////////////////////////////////////////////////////// +class CScriptVarLinkTmp : public CScriptVarLink{ +private: + CScriptVarLinkTmp(const CScriptVarPtr &var, const std::string &name = TINYJS_TEMP_NAME, int flags = SCRIPTVARLINK_DEFAULT) : CScriptVarLink(var, name, flags) { + refs=1; + } + int refs; + CScriptVarLinkTmp *ref() { refs++; return this;} + void unref() { if(--refs == 0) delete this; } + friend class CScriptVarLinkTmpPtr; +}; +class CScriptVarLinkTmpPtr{ +public: + CScriptVarLinkTmpPtr() : link(0) {} + CScriptVarLinkTmpPtr(const CScriptVarPtr &var, const std::string &name = TINYJS_TEMP_NAME, int flags = SCRIPTVARLINK_DEFAULT) { + link = new CScriptVarLinkTmp(var, name, flags); + } + CScriptVarLinkTmpPtr(const CScriptVarLinkTmpPtr &Copy) : link(Copy.link) { if(link) link->ref(); } + CScriptVarLinkTmpPtr &operator=(const CScriptVarLinkTmpPtr &Copy) { + if(link!=Copy.link) { if(link) link->unref(); link=Copy.link; if(link) link->ref(); } return *this; } + ~CScriptVarLinkTmpPtr() { if(link) link->unref(); } + operator bool() const { return link != 0; } + int refs() { return link ? link->refs : 0; } + CScriptVarLink *operator->() const { return link; } + operator CScriptVarLink *() const { return link; } + +private: + CScriptVarLinkTmp *link; +}; + + + + +////////////////////////////////////////////////////////////////////////// +/// CScriptVarSmartLink +////////////////////////////////////////////////////////////////////////// + +class CScriptVarLinkPtr : public fixed_size_object +{ +public: + // constructors + CScriptVarLinkPtr() : link(0) {} + CScriptVarLinkPtr(CScriptVarLink *Link) : link(0) { *this = Link; } + CScriptVarLinkPtr (CScriptVarPtr Var, const std::string &Name = TINYJS_TEMP_NAME, int Flags = SCRIPTVARLINK_DEFAULT) : link(0),tmp_link(Var, Name, Flags) { } + + // copy + CScriptVarLinkPtr (const CScriptVarLinkPtr &Link) : link(0) { *this = Link; } + CScriptVarLinkPtr &operator = (const CScriptVarLinkPtr &Link) { link = Link.link; tmp_link = Link.tmp_link; return *this; } + + // assign + CScriptVarLinkPtr &operator = (CScriptVarLink *Link) { return (*this)(Link); } + CScriptVarLinkPtr &operator = (const CScriptVarPtr &Var) { link = 0; set_tmp_link(Var); return *this; } + CScriptVarLinkPtr &operator()(CScriptVarLink *Link); + CScriptVarLinkPtr &operator()(bool &execute, CScriptVarLink *Link); + CScriptVarLinkPtr &operator()(const CScriptVarPtr &Var, const std::string &Name = TINYJS_TEMP_NAME, int Flags = SCRIPTVARLINK_DEFAULT) { link = 0; set_tmp_link(Var, Name, Flags); return *this; } + + +// void setLink(CScriptVarLink *Link); +// void setLink(bool &execute, CScriptVarLink *Link); + +// void setVarPtr(const CScriptVarPtr &Var, const std::string &Name = TINYJS_TEMP_NAME, int Flags = SCRIPTVARLINK_DEFAULT) { link = 0; set_tmp_link(Var, Name, Flags); } + // operator + CScriptVarLink *operator ->() { return getLink(); } + const CScriptVarLink *operator ->() const { return getLink(); } + CScriptVarLink &operator *() { return *getLink(); } + + void replaceVar(bool &execute, const CScriptVarPtr &Var); + + operator bool() const { return getLink()!=0; } + // operator CScriptVarLink *() { return link; } + CScriptVarLink *getLink() { return (tmp_link && tmp_link->var)?tmp_link:link; }; + CScriptVarLink *getRealLink() { return link; }; + const CScriptVarLink *getLink() const { return (tmp_link && tmp_link->var)?tmp_link:link; }; + void clear() { link=0; clear_tmp_link(); } + void swap(CScriptVarLinkPtr &Link); +private: + void set_tmp_link(const CScriptVarPtr &Var, const std::string &Name = TINYJS_TEMP_NAME, int Flags = SCRIPTVARLINK_DEFAULT); + void clear_tmp_link(); + + CScriptVarLink *link; + CScriptVarLinkTmpPtr tmp_link; +}; +inline CScriptVarPtr::CScriptVarPtr(const CScriptVarLinkPtr &Link) : var(0) { if(Link) { var = Link->getVarPtr()->ref(); } } + +////////////////////////////////////////////////////////////////////////// +#define define_dummy_t(t1) struct t1##_t{}; extern t1##_t t1 +#define declare_dummy_t(t1) t1##_t t1 +#define define_newScriptVar_Fnc(t1, ...) CScriptVarPtr newScriptVar(__VA_ARGS__) +#define define_newScriptVar_NamedFnc(t1, ...) CScriptVarPtr newScriptVar##t1(__VA_ARGS__) +#define define_ScriptVarPtr_Type(t1) class CScriptVar##t1; typedef CScriptVarPointer CScriptVar##t1##Ptr + +#define define_DEPRECATED_newScriptVar_Fnc(t1, ...) CScriptVarPtr DEPRECATED("newScriptVar("#__VA_ARGS__") is deprecated use constScriptVar("#__VA_ARGS__") instead") newScriptVar(__VA_ARGS__) +////////////////////////////////////////////////////////////////////////// +/// CScriptVarObject +////////////////////////////////////////////////////////////////////////// + +define_dummy_t(Object); +define_ScriptVarPtr_Type(Object); + +class CScriptVarObject : public CScriptVar { +protected: + CScriptVarObject(CTinyJS *Context); + CScriptVarObject(CTinyJS *Context, const CScriptVarPtr &Prototype) : CScriptVar(Context, Prototype) {} + CScriptVarObject(const CScriptVarObject &Copy) : CScriptVar(Copy) {} ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarObject(); + virtual CScriptVarPtr clone(); + + virtual bool isObject(); // { return true; } + virtual bool isPrimitive(); // { return false; } + + virtual int getInt(); + virtual bool getBool(); + virtual double getDouble(); + virtual std::string getString(); // { return "[ Object ]"; }; + virtual std::string getParsableString(const std::string &indentString, const std::string &indent); + virtual std::string getVarType(); // { return "object"; } + + virtual CScriptVarPtr _toString(bool execute, int radix=0); +private: + friend define_newScriptVar_Fnc(Object, CTinyJS *Context, Object_t); + friend define_newScriptVar_Fnc(Object, CTinyJS *Context, Object_t, const CScriptVarPtr &); +}; +inline define_newScriptVar_Fnc(Object, CTinyJS *Context, Object_t) { return new CScriptVarObject(Context); } +inline define_newScriptVar_Fnc(Object, CTinyJS *Context, Object_t, const CScriptVarPtr &Prototype) { return new CScriptVarObject(Context, Prototype); } + + +////////////////////////////////////////////////////////////////////////// +/// CScriptVarObjectWrap +////////////////////////////////////////////////////////////////////////// + +define_dummy_t(ObjectWrap); +define_ScriptVarPtr_Type(ObjectWrap); + +class CScriptVarObjectWrap : public CScriptVarObject { +protected: + CScriptVarObjectWrap(CTinyJS *Context, const CScriptVarPtr &Value);// : CScriptVarObject(Context), value(Value) {} + CScriptVarObjectWrap(const CScriptVarObjectWrap &Copy) : CScriptVarObject(Copy), value(Copy.value) {} ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarObjectWrap(); + virtual CScriptVarPtr clone(); + + virtual std::string getParsableString(const std::string &indentString, const std::string &indent); ///< get Data as a parsable javascript string + + virtual CScriptVarPtr _valueOf(bool execute); + virtual CScriptVarPtr _toString(bool execute, int radix=0); + virtual void setTemporaryID_recursive(uint32_t ID); +private: + CScriptVarPtr value; + friend define_newScriptVar_Fnc(ObjectWrap, CTinyJS *Context, ObjectWrap_t, const CScriptVarPtr); +}; +inline define_newScriptVar_Fnc(ObjectWrap, CTinyJS *Context, ObjectWrap_t, const CScriptVarPtr Value) { return new CScriptVarObjectWrap(Context, Value); } + +////////////////////////////////////////////////////////////////////////// +/// CScriptVarError +////////////////////////////////////////////////////////////////////////// + +define_ScriptVarPtr_Type(Error); + +class CScriptVarError : public CScriptVarObject { +protected: + CScriptVarError(CTinyJS *Context, ERROR_TYPES type, const char *message, const char *file, int line, int column);// : CScriptVarObject(Context), value(Value) {} + CScriptVarError(const CScriptVarError &Copy) : CScriptVarObject(Copy) {} ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarError(); + virtual CScriptVarPtr clone(); + virtual bool isError(); // { return true; } + +// virtual std::string getParsableString(const std::string &indentString, const std::string &indent); ///< get Data as a parsable javascript string + + virtual CScriptVarPtr _toString(bool execute, int radix=0); +private: + friend define_newScriptVar_NamedFnc(Error, CTinyJS *Context, ERROR_TYPES type, const char *message, const char *file, int line, int column); +}; +inline define_newScriptVar_NamedFnc(Error, CTinyJS *Context, ERROR_TYPES type, const char *message=0, const char *file=0, int line=-1, int column=-1) { return new CScriptVarError(Context, type, message, file, line, column); } + + +////////////////////////////////////////////////////////////////////////// +/// CScriptVarAccessor +////////////////////////////////////////////////////////////////////////// + +define_dummy_t(Accessor); +define_ScriptVarPtr_Type(Accessor); + +class CScriptVarAccessor : public CScriptVar { +protected: + CScriptVarAccessor(CTinyJS *Context); + CScriptVarAccessor(const CScriptVarAccessor &Copy) : CScriptVar(Copy) {} ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarAccessor(); + virtual CScriptVarPtr clone(); + virtual bool isAccessor(); // { return true; } + virtual bool isPrimitive(); // { return false; } + + virtual std::string getString(); // { return "[ Object ]"; }; + virtual std::string getParsableString(const std::string &indentString, const std::string &indent); + virtual std::string getVarType(); // { return "object"; } + + CScriptVarPtr getValue(); + + friend define_newScriptVar_Fnc(Accessor, CTinyJS *Context, Accessor_t); + +}; +inline define_newScriptVar_Fnc(Accessor, CTinyJS *Context, Accessor_t) { return new CScriptVarAccessor(Context); } + + +////////////////////////////////////////////////////////////////////////// +/// CScriptVarArray +////////////////////////////////////////////////////////////////////////// + +define_dummy_t(Array); +define_ScriptVarPtr_Type(Array); +class CScriptVarArray : public CScriptVar { +protected: + CScriptVarArray(CTinyJS *Context); + CScriptVarArray(const CScriptVarArray &Copy) : CScriptVar(Copy) {} ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarArray(); + virtual CScriptVarPtr clone(); + virtual bool isArray(); // { return true; } + virtual bool isPrimitive(); // { return false; } + + virtual std::string getString(); + virtual std::string getVarType(); // { return "object"; } + virtual std::string getParsableString(const std::string &indentString, const std::string &indent); + friend define_newScriptVar_Fnc(Array, CTinyJS *Context, Array_t); +private: + void native_Length(const CFunctionsScopePtr &c, void *data); +}; +inline define_newScriptVar_Fnc(Array, CTinyJS *Context, Array_t) { return new CScriptVarArray(Context); } + + +////////////////////////////////////////////////////////////////////////// +/// CScriptVarNull +////////////////////////////////////////////////////////////////////////// + +define_dummy_t(Null); +define_ScriptVarPtr_Type(Null); +class CScriptVarNull : public CScriptVar { +protected: + CScriptVarNull(CTinyJS *Context); + CScriptVarNull(const CScriptVarNull &Copy) : CScriptVar(Copy) {} ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarNull(); + virtual CScriptVarPtr clone(); + virtual bool isNull(); // { return true; } + virtual bool isNumeric(); // { return true; } + virtual std::string getString(); // { return "null"; }; + virtual std::string getVarType(); // { return "null"; } + virtual CScriptVarPtr getNumericVar(); ///< returns an Integer, a Double, an Infinity or a NaN + friend define_newScriptVar_Fnc(Null, CTinyJS *Context, Null_t); +}; +inline define_newScriptVar_Fnc(Null, CTinyJS *Context, Null_t) { return new CScriptVarNull(Context); } + + +////////////////////////////////////////////////////////////////////////// +/// CScriptVarUndefined +////////////////////////////////////////////////////////////////////////// + +define_dummy_t(Undefined); +define_ScriptVarPtr_Type(Undefined); +class CScriptVarUndefined : public CScriptVar { +protected: + CScriptVarUndefined(CTinyJS *Context); + CScriptVarUndefined(const CScriptVarUndefined &Copy) : CScriptVar(Copy) {} ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarUndefined(); + virtual CScriptVarPtr clone(); + virtual bool isUndefined(); // { return true; } + virtual bool isNumeric(); // { return true; } + virtual std::string getString(); // { return "undefined"; }; + virtual std::string getVarType(); // { return "undefined"; } + friend define_DEPRECATED_newScriptVar_Fnc(Undefined, CTinyJS *, Undefined_t); + friend define_newScriptVar_NamedFnc(Undefined, CTinyJS *Context); +}; +inline define_DEPRECATED_newScriptVar_Fnc(Undefined, CTinyJS *Context, Undefined_t) { return new CScriptVarUndefined(Context); } +inline define_newScriptVar_NamedFnc(Undefined, CTinyJS *Context) { return new CScriptVarUndefined(Context); } + + +////////////////////////////////////////////////////////////////////////// +/// CScriptVarNaN +////////////////////////////////////////////////////////////////////////// + +define_dummy_t(NaN); +define_ScriptVarPtr_Type(NaN); +class CScriptVarNaN : public CScriptVar { +protected: + CScriptVarNaN(CTinyJS *Context); + CScriptVarNaN(const CScriptVarNaN &Copy) : CScriptVar(Copy) {} ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarNaN(); + virtual CScriptVarPtr clone(); + virtual bool isNaN();// { return true; } + virtual bool isNumeric(); // { return true; } + virtual std::string getString(); // { return "NaN"; }; + virtual std::string getVarType(); // { return "number"; } + friend define_DEPRECATED_newScriptVar_Fnc(NaN, CTinyJS *, NaN_t); + friend define_newScriptVar_NamedFnc(NaN, CTinyJS *Context); +}; +inline define_DEPRECATED_newScriptVar_Fnc(NaN, CTinyJS *Context, NaN_t) { return new CScriptVarNaN(Context); } +inline define_newScriptVar_NamedFnc(NaN, CTinyJS *Context) { return new CScriptVarNaN(Context); } + + +////////////////////////////////////////////////////////////////////////// +/// CScriptVarString +////////////////////////////////////////////////////////////////////////// + +define_ScriptVarPtr_Type(String); +class CScriptVarString : public CScriptVar { +protected: + CScriptVarString(CTinyJS *Context, const std::string &Data); + CScriptVarString(const CScriptVarString &Copy) : CScriptVar(Copy), data(Copy.data) {} ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarString(); + virtual CScriptVarPtr clone(); + virtual bool isString(); // { return true; } + virtual int getInt(); // {return strtol(data.c_str(),0,0); } + virtual bool getBool(); // {return data.length()!=0;} + virtual double getDouble(); // {return strtod(data.c_str(),0);} + virtual std::string getString(); // { return data; } + virtual std::string getParsableString(); // { return getJSString(data); } + virtual std::string getVarType(); // { return "string"; } + virtual CScriptVarPtr getNumericVar(); ///< returns an Integer, a Double, an Infinity or a NaN +protected: + std::string data; +private: + void native_Length(const CFunctionsScopePtr &c, void *data); + + friend define_newScriptVar_Fnc(String, CTinyJS *Context, const std::string &); + friend define_newScriptVar_Fnc(String, CTinyJS *Context, const char *); + friend define_newScriptVar_Fnc(String, CTinyJS *Context, char *); +}; +inline define_newScriptVar_Fnc(String, CTinyJS *Context, const std::string &Obj) { return new CScriptVarString(Context, Obj); } +inline define_newScriptVar_Fnc(String, CTinyJS *Context, const char *Obj) { return new CScriptVarString(Context, Obj); } +inline define_newScriptVar_Fnc(String, CTinyJS *Context, char *Obj) { return new CScriptVarString(Context, Obj); } + + +////////////////////////////////////////////////////////////////////////// +/// CScriptVarIntegerBase +////////////////////////////////////////////////////////////////////////// + +class CScriptVarIntegerBase : public CScriptVar { +protected: + CScriptVarIntegerBase(CTinyJS *Context, const CScriptVarPtr &Prototype, int Data); + CScriptVarIntegerBase(const CScriptVarIntegerBase &Copy) : CScriptVar(Copy), data(Copy.data) {} ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarIntegerBase(); + virtual bool isNumeric(); // { return true; } + virtual int getInt(); // {return data; } + virtual bool getBool(); // {return data!=0;} + virtual double getDouble(); // {return data;} + virtual std::string getString(); // {return int2string(data);} + virtual std::string getVarType(); // { return "number"; } + virtual CScriptVarPtr getNumericVar(); ///< returns an Integer, a Double, an Infinity or a NaN +protected: + int data; +}; + + +////////////////////////////////////////////////////////////////////////// +/// CScriptVarInteger +////////////////////////////////////////////////////////////////////////// + +define_dummy_t(Zero); +define_dummy_t(One); +define_ScriptVarPtr_Type(Integer); +class CScriptVarInteger : public CScriptVarIntegerBase { +protected: + CScriptVarInteger(CTinyJS *Context, int Data); + CScriptVarInteger(const CScriptVarInteger &Copy) : CScriptVarIntegerBase(Copy) {} ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarInteger(); + virtual CScriptVarPtr clone(); + virtual bool isNumber(); // { return true; } + virtual bool isInt(); // { return true; } + virtual CScriptVarPtr _toString(bool execute, int radix=0); + friend define_newScriptVar_Fnc(Integer, CTinyJS *Context, int); + friend define_newScriptVar_Fnc(Integer, CTinyJS *Context, char); +}; +inline define_newScriptVar_Fnc(Integer, CTinyJS *Context, int Obj) { return new CScriptVarInteger(Context, Obj); } +inline define_newScriptVar_Fnc(Integer, CTinyJS *Context, char Obj) { return new CScriptVarInteger(Context, Obj); } + + +////////////////////////////////////////////////////////////////////////// +/// CScriptVarBool +////////////////////////////////////////////////////////////////////////// + +define_ScriptVarPtr_Type(Bool); +class CScriptVarBool : public CScriptVarIntegerBase { +protected: + CScriptVarBool(CTinyJS *Context, bool Data); + CScriptVarBool(const CScriptVarBool &Copy) : CScriptVarIntegerBase(Copy) {} ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarBool(); + virtual CScriptVarPtr clone(); + virtual bool isBool(); // { return true; } + virtual std::string getString(); // {return data!=0?"true":"false";} + virtual std::string getVarType(); // { return "boolean"; } + virtual CScriptVarPtr getNumericVar(); ///< returns an Integer, a Double, an Infinity or a NaN + friend define_DEPRECATED_newScriptVar_Fnc(Bool, CTinyJS *, bool); + friend define_newScriptVar_NamedFnc(Bool, CTinyJS *Context, bool); +}; +inline define_DEPRECATED_newScriptVar_Fnc(Bool, CTinyJS *Context, bool Obj) { return new CScriptVarBool(Context, Obj); } +inline define_newScriptVar_NamedFnc(Bool, CTinyJS *Context, bool Obj) { return new CScriptVarBool(Context, Obj); } + + +////////////////////////////////////////////////////////////////////////// +/// CScriptVarInfinity +////////////////////////////////////////////////////////////////////////// + +class Infinity{public:Infinity(int Sig=1):sig(Sig){} int Sig(){return sig;} private:int sig; } ; +extern Infinity InfinityPositive; +extern Infinity InfinityNegative; +define_ScriptVarPtr_Type(Infinity); +class CScriptVarInfinity : public CScriptVarIntegerBase { +protected: + CScriptVarInfinity(CTinyJS *Context, int Data); + CScriptVarInfinity(const CScriptVarInfinity &Copy) : CScriptVarIntegerBase(Copy) {} ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarInfinity(); + virtual CScriptVarPtr clone(); + virtual int isInfinity(); // { return data; } + virtual std::string getString(); // {return data<0?"-Infinity":"Infinity";} + friend define_DEPRECATED_newScriptVar_Fnc(Infinity, CTinyJS *, Infinity); + friend define_newScriptVar_NamedFnc(Infinity, CTinyJS *, Infinity); +}; +inline define_DEPRECATED_newScriptVar_Fnc(Infinity, CTinyJS *Context, Infinity Obj) { return new CScriptVarInfinity(Context, Obj.Sig()); } +inline define_newScriptVar_NamedFnc(Infinity, CTinyJS *Context, Infinity Obj) { return new CScriptVarInfinity(Context, Obj.Sig()); } + + +////////////////////////////////////////////////////////////////////////// +/// CScriptVarDouble +////////////////////////////////////////////////////////////////////////// + +define_ScriptVarPtr_Type(Double); +class CScriptVarDouble : public CScriptVar { +protected: + CScriptVarDouble(CTinyJS *Context, double Data); + CScriptVarDouble(const CScriptVarDouble &Copy) : CScriptVar(Copy), data(Copy.data) {} ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarDouble(); + virtual CScriptVarPtr clone(); + virtual bool isDouble(); // { return true; } + virtual bool isNumber(); // { return true; } + virtual bool isNumeric(); // { return true; } + virtual int getInt(); // {return (int)data; } + virtual bool getBool(); // {return data!=0.0;} + virtual double getDouble(); // {return data;} + virtual std::string getString(); // {return float2string(data);} + virtual std::string getVarType(); // { return "number"; } + virtual CScriptVarPtr getNumericVar(); ///< returns an Integer, a Double, an Infinity or a NaN + virtual CScriptVarPtr _toString(bool execute, int radix=0); +private: + double data; + friend define_newScriptVar_Fnc(Double, CTinyJS *Context, double); +}; +inline define_newScriptVar_Fnc(Double, CTinyJS *Context, double Obj) { return new CScriptVarDouble(Context, Obj); } + + +////////////////////////////////////////////////////////////////////////// +/// CScriptVarFunction +////////////////////////////////////////////////////////////////////////// + +define_ScriptVarPtr_Type(Function); +class CScriptVarFunction : public CScriptVarObject { +protected: + CScriptVarFunction(CTinyJS *Context, CScriptTokenDataFnc *Data); + CScriptVarFunction(const CScriptVarFunction &Copy) : CScriptVarObject(Copy), data(Copy.data) { data->ref(); } ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarFunction(); + virtual CScriptVarPtr clone(); + virtual bool isObject(); // { return true; } + virtual bool isFunction(); // { return true; } + virtual bool isPrimitive(); // { return false; } + +// virtual std::string getString(); // {return "[ Function ]";} + virtual std::string getVarType(); // { return "function"; } + virtual std::string getParsableString(const std::string &indentString, const std::string &indent); + virtual CScriptVarPtr _toString(bool execute, int radix=0); + virtual CScriptTokenDataFnc *getFunctionData(); + void setFunctionData(CScriptTokenDataFnc *Data); +private: + CScriptTokenDataFnc *data; + std::string getParsableBlockString(TOKEN_VECT_it &it, TOKEN_VECT_it end, const std::string indentString, const std::string indent); + + + friend define_newScriptVar_Fnc(Function, CTinyJS *Context, CScriptTokenDataFnc *); +}; +inline define_newScriptVar_Fnc(Function, CTinyJS *Context, CScriptTokenDataFnc *Obj) { return new CScriptVarFunction(Context, Obj); } + + +////////////////////////////////////////////////////////////////////////// +/// CScriptVarFunctionNative +////////////////////////////////////////////////////////////////////////// + +define_ScriptVarPtr_Type(FunctionNative); +class CScriptVarFunctionNative : public CScriptVarFunction { +protected: + CScriptVarFunctionNative(CTinyJS *Context, void *Userdata) : CScriptVarFunction(Context, 0), jsUserData(Userdata) { } + CScriptVarFunctionNative(const CScriptVarFunctionNative &Copy) : CScriptVarFunction(Copy), jsUserData(Copy.jsUserData) { } ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarFunctionNative(); + virtual CScriptVarPtr clone()=0; + virtual bool isNative(); // { return true; } + + virtual std::string getString(); // {return "[ Function Native ]";} + + virtual void callFunction(const CFunctionsScopePtr &c)=0;// { jsCallback(c, jsCallbackUserData); } +protected: + void *jsUserData; ///< user data passed as second argument to native functions +}; + + +////////////////////////////////////////////////////////////////////////// +/// CScriptVarFunctionNativeCallback +////////////////////////////////////////////////////////////////////////// + +define_ScriptVarPtr_Type(FunctionNativeCallback); +class CScriptVarFunctionNativeCallback : public CScriptVarFunctionNative { +protected: + CScriptVarFunctionNativeCallback(CTinyJS *Context, JSCallback Callback, void *Userdata) : CScriptVarFunctionNative(Context, Userdata), jsCallback(Callback) { } + CScriptVarFunctionNativeCallback(const CScriptVarFunctionNativeCallback &Copy) : CScriptVarFunctionNative(Copy), jsCallback(Copy.jsCallback) { } ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarFunctionNativeCallback(); + virtual CScriptVarPtr clone(); + virtual void callFunction(const CFunctionsScopePtr &c); +private: + JSCallback jsCallback; ///< Callback for native functions + friend define_newScriptVar_Fnc(FunctionNativeCallback, CTinyJS *Context, JSCallback Callback, void*); +}; +inline define_newScriptVar_Fnc(FunctionNativeCallback, CTinyJS *Context, JSCallback Callback, void *Userdata) { return new CScriptVarFunctionNativeCallback(Context, Callback, Userdata); } + + +////////////////////////////////////////////////////////////////////////// +/// CScriptVarFunctionNativeClass +////////////////////////////////////////////////////////////////////////// + +template +class CScriptVarFunctionNativeClass : public CScriptVarFunctionNative { +protected: + CScriptVarFunctionNativeClass(CTinyJS *Context, native *ClassPtr, void (native::*ClassFnc)(const CFunctionsScopePtr &, void *), void *Userdata) : CScriptVarFunctionNative(Context, Userdata), classPtr(ClassPtr), classFnc(ClassFnc) { } + CScriptVarFunctionNativeClass(const CScriptVarFunctionNativeClass &Copy) : CScriptVarFunctionNative(Copy), classPtr(Copy.classPtr), classFnc(Copy.classFnc) { } ///< Copy protected -> use clone for public +public: + virtual CScriptVarPtr clone() { return new CScriptVarFunctionNativeClass(*this); } + + virtual void callFunction(const CFunctionsScopePtr &c) { (classPtr->*classFnc)(c, jsUserData); } +private: + native *classPtr; + void (native::*classFnc)(const CFunctionsScopePtr &c, void *userdata); + template + friend define_newScriptVar_Fnc(FunctionNativeCallback, CTinyJS*, native2 *, void (native2::*)(const CFunctionsScopePtr &, void *), void *); +}; +template +define_newScriptVar_Fnc(FunctionNativeCallback, CTinyJS *Context, native *ClassPtr, void (native::*ClassFnc)(const CFunctionsScopePtr &, void *), void *Userdata) { return new CScriptVarFunctionNativeClass(Context, ClassPtr, ClassFnc, Userdata); } + +////////////////////////////////////////////////////////////////////////// +/// CScriptVarScope +////////////////////////////////////////////////////////////////////////// + +define_dummy_t(Scope); +define_ScriptVarPtr_Type(Scope); +class CScriptVarScope : public CScriptVarObject { +protected: // only derived classes or friends can be created + CScriptVarScope(CTinyJS *Context) // constructor for rootScope + : CScriptVarObject(Context) {} + virtual CScriptVarPtr clone(); + virtual bool isObject(); // { return false; } +public: + virtual ~CScriptVarScope(); + virtual CScriptVarPtr scopeVar(); ///< to create var like: var a = ... + virtual CScriptVarPtr scopeLet(); ///< to create var like: let a = ... + virtual CScriptVarLink *findInScopes(const std::string &childName); + virtual CScriptVarScopePtr getParent(); + friend define_newScriptVar_Fnc(Scope, CTinyJS *Context, Scope_t); +}; +inline define_newScriptVar_Fnc(Scope, CTinyJS *Context, Scope_t) { return new CScriptVarScope(Context); } + + +////////////////////////////////////////////////////////////////////////// +/// CScriptVarScopeFnc +////////////////////////////////////////////////////////////////////////// + +define_dummy_t(ScopeFnc); +define_ScriptVarPtr_Type(ScopeFnc); +class CScriptVarScopeFnc : public CScriptVarScope { +protected: // only derived classes or friends can be created + CScriptVarScopeFnc(CTinyJS *Context, const CScriptVarScopePtr &Closure) // constructor for FncScope + : CScriptVarScope(Context), closure(Closure ? addChild(TINYJS_FUNCTION_CLOSURE_VAR, Closure, 0) : 0) {} +public: + virtual ~CScriptVarScopeFnc(); + virtual CScriptVarLink *findInScopes(const std::string &childName); + + void setReturnVar(const CScriptVarPtr &var); ///< Set the result value. Use this when setting complex return data as it avoids a deepCopy() + + #define DEPRECATED_getParameter DEPRECATED("getParameter is deprecated use getArgument instead") + DEPRECATED_getParameter CScriptVarPtr getParameter(const std::string &name); + DEPRECATED_getParameter CScriptVarPtr getParameter(int Idx); + CScriptVarPtr getArgument(const std::string &name); ///< If this is a function, get the parameter with the given name (for use by native functions) + CScriptVarPtr getArgument(int Idx); ///< If this is a function, get the parameter with the given index (for use by native functions) + DEPRECATED("getParameterLength is deprecated use getArgumentsLength instead") int getParameterLength(); ///< If this is a function, get the count of parameters + int getArgumentsLength(); ///< If this is a function, get the count of parameters +protected: + CScriptVarLink *closure; + friend define_newScriptVar_Fnc(ScopeFnc, CTinyJS *Context, ScopeFnc_t, const CScriptVarScopePtr &Closure); +}; +inline define_newScriptVar_Fnc(ScopeFnc, CTinyJS *Context, ScopeFnc_t, const CScriptVarScopePtr &Closure) { return new CScriptVarScopeFnc(Context, Closure); } + + +////////////////////////////////////////////////////////////////////////// +/// CScriptVarScopeLet +////////////////////////////////////////////////////////////////////////// + +define_dummy_t(ScopeLet); +define_ScriptVarPtr_Type(ScopeLet); +class CScriptVarScopeLet : public CScriptVarScope { +protected: // only derived classes or friends can be created + CScriptVarScopeLet(const CScriptVarScopePtr &Parent); // constructor for LetScope +// : CScriptVarScope(Parent->getContext()), parent( context->getRoot() != Parent ? addChild(TINYJS_SCOPE_PARENT_VAR, Parent, 0) : 0) {} +public: + virtual ~CScriptVarScopeLet(); + virtual CScriptVarLink *findInScopes(const std::string &childName); + virtual CScriptVarPtr scopeVar(); ///< to create var like: var a = ... + virtual CScriptVarScopePtr getParent(); +protected: + CScriptVarLink *parent; + friend define_newScriptVar_Fnc(ScopeLet, CTinyJS *Context, ScopeLet_t, const CScriptVarScopePtr &Parent); +}; +inline define_newScriptVar_Fnc(ScopeLet, CTinyJS *, ScopeLet_t, const CScriptVarScopePtr &Parent) { return new CScriptVarScopeLet(Parent); } + + +////////////////////////////////////////////////////////////////////////// +/// CScriptVarScopeWith +////////////////////////////////////////////////////////////////////////// + +define_dummy_t(ScopeWith); +define_ScriptVarPtr_Type(ScopeWith); +class CScriptVarScopeWith : public CScriptVarScopeLet { +protected: + CScriptVarScopeWith(const CScriptVarScopePtr &Parent, const CScriptVarPtr &With) + : CScriptVarScopeLet(Parent), with(addChild(TINYJS_SCOPE_WITH_VAR, With, 0)) {} + +public: + virtual ~CScriptVarScopeWith(); + virtual CScriptVarPtr scopeLet(); ///< to create var like: let a = ... + virtual CScriptVarLink *findInScopes(const std::string &childName); +private: + CScriptVarLink *with; + friend define_newScriptVar_Fnc(ScopeWith, CTinyJS *Context, ScopeWith_t, const CScriptVarScopePtr &Parent, const CScriptVarPtr &With); +}; +inline define_newScriptVar_Fnc(ScopeWith, CTinyJS *, ScopeWith_t, const CScriptVarScopePtr &Parent, const CScriptVarPtr &With) { return new CScriptVarScopeWith(Parent, With); } + +////////////////////////////////////////////////////////////////////////// +template +inline CScriptVarPtr CScriptVar::newScriptVar(T t) { return ::newScriptVar(context, t); } +////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////// +/// CTinyJS +////////////////////////////////////////////////////////////////////////// + +class CTinyJS { +public: + CTinyJS(); + ~CTinyJS(); + + void execute(CScriptTokenizer &Tokenizer); + void execute(const char *Code, const std::string &File="", int Line=0, int Column=0); + void execute(const std::string &Code, const std::string &File="", int Line=0, int Column=0); + /** Evaluate the given code and return a link to a javascript object, + * useful for (dangerous) JSON parsing. If nothing to return, will return + * 'undefined' variable type. CScriptVarLink is returned as this will + * automatically unref the result as it goes out of scope. If you want to + * keep it, you must use ref() and unref() */ + CScriptVarLink evaluateComplex(CScriptTokenizer &Tokenizer); + /** Evaluate the given code and return a link to a javascript object, + * useful for (dangerous) JSON parsing. If nothing to return, will return + * 'undefined' variable type. CScriptVarLink is returned as this will + * automatically unref the result as it goes out of scope. If you want to + * keep it, you must use ref() and unref() */ + CScriptVarLink evaluateComplex(const char *code, const std::string &File="", int Line=0, int Column=0); + /** Evaluate the given code and return a link to a javascript object, + * useful for (dangerous) JSON parsing. If nothing to return, will return + * 'undefined' variable type. CScriptVarLink is returned as this will + * automatically unref the result as it goes out of scope. If you want to + * keep it, you must use ref() and unref() */ + CScriptVarLink evaluateComplex(const std::string &code, const std::string &File="", int Line=0, int Column=0); + /** Evaluate the given code and return a string. If nothing to return, will return + * 'undefined' */ + std::string evaluate(CScriptTokenizer &Tokenizer); + /** Evaluate the given code and return a string. If nothing to return, will return + * 'undefined' */ + std::string evaluate(const char *code, const std::string &File="", int Line=0, int Column=0); + /** Evaluate the given code and return a string. If nothing to return, will return + * 'undefined' */ + std::string evaluate(const std::string &code, const std::string &File="", int Line=0, int Column=0); + + /// add a native function to be called from TinyJS + /** example: + \code + void scRandInt(const CFunctionsScopePtr &c, void *userdata) { ... } + tinyJS->addNative("function randInt(min, max)", scRandInt, 0); + \endcode + + or + + \code + void scSubstring(const CFunctionsScopePtr &c, void *userdata) { ... } + tinyJS->addNative("function String.substring(lo, hi)", scSubstring, 0); + \endcode + or + + \code + class Class + { + public: + void scSubstring(const CFunctionsScopePtr &c, void *userdata) { ... } + }; + Class Instanz; + tinyJS->addNative("function String.substring(lo, hi)", &Instanz, &Class::*scSubstring, 0); + \endcode + */ + + CScriptVarFunctionNativePtr addNative(const std::string &funcDesc, JSCallback ptr, void *userdata=0, int LinkFlags=SCRIPTVARLINK_NATIVEDEFAULT); + template + CScriptVarFunctionNativePtr addNative(const std::string &funcDesc, C *class_ptr, void(C::*class_fnc)(const CFunctionsScopePtr &, void *), void *userdata=0, int LinkFlags=SCRIPTVARLINK_NATIVEDEFAULT) + { + return addNative(funcDesc, ::newScriptVar(this, class_ptr, class_fnc, userdata), LinkFlags); + } + + /// Send all variables to stdout + void trace(); + + const CScriptVarScopePtr &getRoot() { return root; }; /// gets the root of symbol table + // CScriptVar *root; /// root of symbol table + + /// newVars & constVars + template CScriptVarPtr newScriptVar(T t) { return ::newScriptVar(this, t); } + template CScriptVarPtr newScriptVar(T1 t1, T2 t2) { return ::newScriptVar(this, t1, t2); } + const CScriptVarPtr &constScriptVar(Undefined_t) { return constUndefined; } + const CScriptVarPtr &constScriptVar(NaN_t) { return constNaN; } + const CScriptVarPtr &constScriptVar(Infinity t) { return t.Sig()<0 ? constInfinityNegative : constInfinityPositive; } + const CScriptVarPtr &constScriptVar(bool Val) { return Val?constTrue:constFalse; } + const CScriptVarPtr &constScriptVar(Zero_t) { return constZero; } + const CScriptVarPtr &constScriptVar(One_t) { return constOne; } + + +private: + static bool noexecute; + CScriptTokenizer *t; /// current tokenizer + int runtimeFlags; + std::string label; + std::vectorscopes; + CScriptVarScopePtr root; + const CScriptVarScopePtr &scope() { return scopes.back(); } + + class CScopeControl { // helper-class to manage scopes + private: + CScopeControl(const CScopeControl& Copy); // no copy + CScopeControl& operator =(const CScopeControl& Copy); + public: + CScopeControl(CTinyJS *Context) : context(Context), count(0) {} + ~CScopeControl() { while(count--) {CScriptVarScopePtr parent = context->scopes.back()->getParent(); if(parent) context->scopes.back() = parent; else context->scopes.pop_back() ;} } + void addFncScope(const CScriptVarScopePtr &Scope) { context->scopes.push_back(Scope); count++; } + void addLetScope() { context->scopes.back() = ::newScriptVar(context, ScopeLet, context->scopes.back()); count++; } + void addWithScope(const CScriptVarPtr &With) { context->scopes.back() = ::newScriptVar(context, ScopeWith, context->scopes.back(), With); count++; } + private: + CTinyJS *context; + int count; + }; + friend class CScopeControl; +public: + CScriptVarPtr objectPrototype; /// Built in object class + CScriptVarPtr objectPrototype_valueOf; /// Built in object class + CScriptVarPtr objectPrototype_toString; /// Built in object class + CScriptVarPtr arrayPrototype; /// Built in array class + CScriptVarPtr stringPrototype; /// Built in string class + CScriptVarPtr numberPrototype; /// Built in number class + CScriptVarPtr booleanPrototype; /// Built in boolean class + CScriptVarPtr functionPrototype; /// Built in function class + const CScriptVarPtr &getErrorPrototype(ERROR_TYPES Type) { return errorPrototypes[Type]; } +private: + CScriptVarPtr errorPrototypes[ERROR_COUNT]; /// Built in error class + CScriptVarPtr constUndefined; + CScriptVarPtr constNaN; + CScriptVarPtr constInfinityPositive; + CScriptVarPtr constInfinityNegative; + CScriptVarPtr constTrue; + CScriptVarPtr constFalse; + CScriptVarPtr constOne; + CScriptVarPtr constZero; + std::vector pseudo_refered; + CScriptVarPtr exceptionVar; /// containing the exception var by (runtimeFlags&RUNTIME_THROW) == true; + + void CheckRightHandVar(bool &execute, CScriptVarLinkPtr &link) + { + if(execute && link && !link->isOwned() && !link->isOwner() && !link->getName().empty()) + throwError(execute, ReferenceError, link->getName() + " is not defined", t->getPrevPos()); + } + + void CheckRightHandVar(bool &execute, CScriptVarLinkPtr &link, CScriptTokenizer::ScriptTokenPosition &Pos) + { + if(execute && link && !link->isOwned() && !link->isOwner() && !link->getName().empty()) + throwError(execute, ReferenceError, link->getName() + " is not defined", Pos); + } + +public: + // function call + CScriptVarPtr callFunction(bool &execute, const CScriptVarFunctionPtr &Function, std::vector &Arguments, const CScriptVarPtr &This, CScriptVarPtr *newThis=0); + // + // parsing - in order of precedence + + CScriptVarPtr mathsOp(bool &execute, const CScriptVarPtr &a, const CScriptVarPtr &b, int op); +private: + + CScriptVarLinkPtr execute_literals(bool &execute); + CScriptVarLinkPtr execute_member(CScriptVarLinkPtr &parent, bool &execute); + CScriptVarLinkPtr execute_function_call(bool &execute); + CScriptVarLinkPtr execute_unary(bool &execute); + CScriptVarLinkPtr execute_term(bool &execute); + CScriptVarLinkPtr execute_expression(bool &execute); + CScriptVarLinkPtr execute_binary_shift(bool &execute); + CScriptVarLinkPtr execute_relation(bool &execute, int set=LEX_EQUAL, int set_n='<'); + CScriptVarLinkPtr execute_binary_logic(bool &execute, int op='|', int op_n1='^', int op_n2='&'); + CScriptVarLinkPtr execute_logic(bool &execute, int op=LEX_OROR, int op_n=LEX_ANDAND); + CScriptVarLinkPtr execute_condition(bool &execute); + CScriptVarLinkPtr execute_assignment(bool &execute); + CScriptVarLinkPtr execute_base(bool &execute); + void execute_block(bool &execute); + CScriptVarLinkPtr execute_statement(bool &execute); + // parsing utility functions + CScriptVarLinkPtr parseFunctionDefinition(CScriptToken &FncToken); + CScriptVarLinkPtr parseFunctionsBodyFromString(const std::string &ArgumentList, const std::string &FncBody); +public: + CScriptVarLink *findInScopes(const std::string &childName); ///< Finds a child, looking recursively up the scopes +private: + ////////////////////////////////////////////////////////////////////////// + /// addNative-helper + CScriptVarFunctionNativePtr addNative(const std::string &funcDesc, CScriptVarFunctionNativePtr Var, int LinkFlags); + + ////////////////////////////////////////////////////////////////////////// + /// throws an Error & Exception + void throwError(bool &execute, ERROR_TYPES ErrorType, const std::string &message); + void throwException(ERROR_TYPES ErrorType, const std::string &message); + void throwError(bool &execute, ERROR_TYPES ErrorType, const std::string &message, CScriptTokenizer::ScriptTokenPosition &Pos); + void throwException(ERROR_TYPES ErrorType, const std::string &message, CScriptTokenizer::ScriptTokenPosition &Pos); + + ////////////////////////////////////////////////////////////////////////// + /// native Object-Constructors & prototype-functions + + void native_Object(const CFunctionsScopePtr &c, void *data); + void native_Object_hasOwnProperty(const CFunctionsScopePtr &c, void *data); + void native_Object_getPrototypeOf(const CFunctionsScopePtr &c, void *data); + void native_Object_valueOf(const CFunctionsScopePtr &c, void *data); + void native_Object_toString(const CFunctionsScopePtr &c, void *data); + + void native_Array(const CFunctionsScopePtr &c, void *data); + + void native_String(const CFunctionsScopePtr &c, void *data); + + void native_Number(const CFunctionsScopePtr &c, void *data); + + void native_Boolean(const CFunctionsScopePtr &c, void *data); + + void native_Function(const CFunctionsScopePtr &c, void *data); + void native_Function_call(const CFunctionsScopePtr &c, void *data); + void native_Function_apply(const CFunctionsScopePtr &c, void *data); + + void native_Error(const CFunctionsScopePtr &c, void *data); + void native_EvalError(const CFunctionsScopePtr &c, void *data); + void native_RangeError(const CFunctionsScopePtr &c, void *data); + void native_ReferenceError(const CFunctionsScopePtr &c, void *data); + void native_SyntaxError(const CFunctionsScopePtr &c, void *data); + void native_TypeError(const CFunctionsScopePtr &c, void *data); + + + ////////////////////////////////////////////////////////////////////////// + /// global function + + void native_eval(const CFunctionsScopePtr &c, void *data); + void native_isNAN(const CFunctionsScopePtr &c, void *data); + void native_isFinite(const CFunctionsScopePtr &c, void *data); + void native_parseInt(const CFunctionsScopePtr &c, void *data); + void native_parseFloat(const CFunctionsScopePtr &c, void *data); + + + + void native_JSON_parse(const CFunctionsScopePtr &c, void *data); + + + uint32_t uniqueID; +public: + uint32_t getUniqueID() { return ++uniqueID; } + CScriptVar *first; + void setTemporaryID_recursive(uint32_t ID); + void ClearUnreferedVars(const CScriptVarPtr &extra=CScriptVarPtr()); +}; + +////////////////////////////////////////////////////////////////////////// +template +inline const CScriptVarPtr &CScriptVar::constScriptVar(T t) { return context->constScriptVar(t); } +////////////////////////////////////////////////////////////////////////// + +#endif diff --git a/TinyJS_Functions.cpp b/TinyJS_Functions.cpp index 0e03298..1186009 100755 --- a/TinyJS_Functions.cpp +++ b/TinyJS_Functions.cpp @@ -7,22 +7,36 @@ * * Copyright (C) 2009 Pur3 Ltd * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + + * 42TinyJS + * + * A fork of TinyJS with the goal to makes a more JavaScript/ECMA compliant engine + * + * Authored / Changed By Armin Diedering * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. + * Copyright (C) 2010 ardisoft * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + #include #include #include @@ -34,39 +48,23 @@ using namespace std; static void scTrace(const CFunctionsScopePtr &c, void * userdata) { CTinyJS *js = (CTinyJS*)userdata; - if(c->getParameterLength()) - c->getParameter(0)->trace(); + if(c->getArgumentsLength()) + c->getArgument(0)->trace(); else js->getRoot()->trace("root"); } static void scObjectDump(const CFunctionsScopePtr &c, void *) { - c->getParameter("this")->trace("> "); + c->getArgument("this")->trace("> "); } static void scObjectClone(const CFunctionsScopePtr &c, void *) { - CScriptVarPtr obj = c->getParameter("this"); + CScriptVarPtr obj = c->getArgument("this"); c->setReturnVar(obj->clone()); } -static void scCharToInt(const CFunctionsScopePtr &c, void *) { - string str = c->getParameter("ch")->getString();; - int val = 0; - if (str.length()>0) - val = (int)str.c_str()[0]; - c->setReturnVar(c->newScriptVar(val)); -} - - -static void scStringFromCharCode(const CFunctionsScopePtr &c, void *) { - char str[2]; - str[0] = c->getParameter("char")->getInt(); - str[1] = 0; - c->setReturnVar(c->newScriptVar(str)); -} - static void scIntegerValueOf(const CFunctionsScopePtr &c, void *) { - string str = c->getParameter("str")->getString(); + string str = c->getArgument("str")->getString(); int val = 0; if (str.length()==1) @@ -76,36 +74,36 @@ static void scIntegerValueOf(const CFunctionsScopePtr &c, void *) { static void scJSONStringify(const CFunctionsScopePtr &c, void *) { string indent = " ", indentString; - c->setReturnVar(c->newScriptVar(c->getParameter("obj")->getParsableString(indentString, indent))); + c->setReturnVar(c->newScriptVar(c->getArgument("obj")->getParsableString(indentString, indent))); } static void scArrayContains(const CFunctionsScopePtr &c, void *data) { - CScriptVarPtr obj = c->getParameter("obj"); - CScriptVarPtr arr = c->getParameter("this"); + CScriptVarPtr obj = c->getArgument("obj"); + CScriptVarPtr arr = c->getArgument("this"); int l = arr->getArrayLength(); - CScriptVarSmartLink equal; + CScriptVarPtr equal = c->constScriptVar(Undefined); for (int i=0;imathsOp(arr->getArrayIndex(i), LEX_EQUAL); - if((*equal)->getBool()) { - c->setReturnVar(c->newScriptVar(true)); + if(equal->getBool()) { + c->setReturnVar(c->constScriptVar(true)); return; } } - c->setReturnVar(c->newScriptVar(false)); + c->setReturnVar(c->constScriptVar(false)); } static void scArrayRemove(const CFunctionsScopePtr &c, void *data) { - CScriptVarPtr obj = c->getParameter("obj"); - CScriptVarPtr arr = c->getParameter("this"); + CScriptVarPtr obj = c->getArgument("obj"); + CScriptVarPtr arr = c->getArgument("this"); int i; vector removedIndices; int l = arr->getArrayLength(); - CScriptVarSmartLink equal; + CScriptVarPtr equal = c->constScriptVar(Undefined); for (i=0;imathsOp(arr->getArrayIndex(i), LEX_EQUAL); - if((*equal)->getBool()) { + if(equal->getBool()) { removedIndices.push_back(i); } } @@ -131,8 +129,8 @@ static void scArrayRemove(const CFunctionsScopePtr &c, void *data) { } static void scArrayJoin(const CFunctionsScopePtr &c, void *data) { - string sep = c->getParameter("separator")->getString(); - CScriptVarPtr arr = c->getParameter("this"); + string sep = c->getArgument("separator")->getString(); + CScriptVarPtr arr = c->getArgument("this"); ostringstream sstr; int l = arr->getArrayLength(); diff --git a/TinyJS_Functions.h b/TinyJS_Functions.h index bffff37..2ddbcfe 100755 --- a/TinyJS_Functions.h +++ b/TinyJS_Functions.h @@ -7,20 +7,33 @@ * * Copyright (C) 2009 Pur3 Ltd * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + + * 42TinyJS + * + * A fork of TinyJS with the goal to makes a more JavaScript/ECMA compliant engine + * + * Authored / Changed By Armin Diedering + * + * Copyright (C) 2010 ardisoft + * + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ #ifndef TINYJS_FUNCTIONS_H diff --git a/TinyJS_MathFunctions.cpp b/TinyJS_MathFunctions.cpp index 8e5915c..08e420e 100644 --- a/TinyJS_MathFunctions.cpp +++ b/TinyJS_MathFunctions.cpp @@ -3,8 +3,39 @@ * * A single-file Javascript-alike engine * - * - Math and Trigonometry functions - + * - Math and Trigonometry functions * + * Authored By O.Z.L.B. + * + * Copyright (C) 2011 O.Z.L.B. + * + + * 42TinyJS + * + * A fork of TinyJS with the goal to makes a more JavaScript/ECMA compliant engine + * + * Authored / Changed By Armin Diedering + * + * Copyright (C) 2010 ardisoft + * + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ #include @@ -31,13 +62,13 @@ using namespace std; #define F_RNG(a,min,max) ((a)<(min) ? min : ((a)>(max) ? max : a )) //CScriptVar shortcut macro -#define scIsInt(a) ( c->getParameter(a)->isInt() ) -#define scIsDouble(a) ( c->getParameter(a)->isDouble() ) -#define scGetInt(a) ( c->getParameter(a)->getInt() ) -#define scGetDouble(a) ( c->getParameter(a)->getDouble() ) +#define scIsInt(a) ( c->getArgument(a)->isInt() ) +#define scIsDouble(a) ( c->getArgument(a)->isDouble() ) +#define scGetInt(a) ( c->getArgument(a)->getInt() ) +#define scGetDouble(a) ( c->getArgument(a)->getDouble() ) #define scReturnInt(a) ( c->setReturnVar(c->newScriptVar((int)a)) ) #define scReturnDouble(a) ( c->setReturnVar(c->newScriptVar((double)a)) ) -#define scReturnNaN() ( c->setReturnVar(c->newScriptVar(NaN)) ) +#define scReturnNaN() do { c->setReturnVar(c->constScriptVar(NaN)); return; } while(0) #ifdef _MSC_VER namespace @@ -68,7 +99,7 @@ namespace } #endif -#define GET_PARAMETER_AS_NUMERIC_VAR(v,n) CScriptVarPtr v = c->getParameter(n)->getNumericVar() +#define GET_PARAMETER_AS_NUMERIC_VAR(v,n) CScriptVarPtr v = c->getArgument(n)->getNumericVar() #define RETURN_NAN_IS_NAN(v) do{ if(v->isNaN()) { c->setReturnVar(v); return; } }while(0) #define RETURN_NAN_IS_NAN_OR_INFINITY(v) do{ if(v->isNaN() || v->isInfinity()) { c->setReturnVar(v); return; } }while(0) #define RETURN_INFINITY_IS_INFINITY(v) do{ if(v->isInfinity()) { c->setReturnVar(v); return; } }while(0) @@ -105,8 +136,8 @@ static void scMathFloor(const CFunctionsScopePtr &c, void *userdata) { //Math.min(a,b) - returns minimum of two given values static void scMathMin(const CFunctionsScopePtr &c, void *userdata) { - int length = c->getParameterLength(); - CScriptVarSmartLink ret(c->newScriptVar(InfinityPositive)); + int length = c->getArgumentsLength(); + CScriptVarLinkPtr ret(c->constScriptVar(InfinityPositive)); for(int i=0; igetParameterLength(); - CScriptVarSmartLink ret((CScriptVarPtr)newScriptVar(c->getContext(), Infinity(-1))); + int length = c->getArgumentsLength(); + CScriptVarLinkPtr ret(c->constScriptVar(InfinityNegative)); for(int i=0; imathsOp(b, LEX_LEQUAL); check_bool = check->getBool(); - if(!check_bool) { scReturnNaN(); return; } + if(!check_bool) scReturnNaN(); check = x->mathsOp(a, '<'); check_bool = check->getBool(); @@ -262,8 +293,8 @@ static void scMathLog(const CFunctionsScopePtr &c, void *userdata) { GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN(a); int a_i = a->isInfinity(); double a_d = a->getDouble(); - if(a_i>0) { c->setReturnVar(c->newScriptVar(InfinityPositive)); return; } - else if(a_i<0 || a_d<0.0) { scReturnNaN(); return; } + if(a_i>0) { c->setReturnVar(c->constScriptVar(InfinityPositive)); return; } + else if(a_i<0 || a_d<0.0) scReturnNaN(); scReturnDouble( log( a_d ) ); } @@ -272,8 +303,8 @@ static void scMathLog10(const CFunctionsScopePtr &c, void *userdata) { GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN(a); int a_i = a->isInfinity(); double a_d = a->getDouble(); - if(a_i>0) { c->setReturnVar(c->newScriptVar(InfinityPositive)); return; } - else if(a_i<0 || a_d<0.0) { c->setReturnVar(c->newScriptVar(NaN)); return; } + if(a_i>0) { c->setReturnVar(c->constScriptVar(InfinityPositive)); return; } + else if(a_i<0 || a_d<0.0) scReturnNaN(); scReturnDouble( log10( a_d ) ); } @@ -281,7 +312,7 @@ static void scMathLog10(const CFunctionsScopePtr &c, void *userdata) { static void scMathExp(const CFunctionsScopePtr &c, void *userdata) { GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN(a); int a_i = a->isInfinity(); - if(a_i>0) { c->setReturnVar(c->newScriptVar(InfinityPositive)); return; } + if(a_i>0) { c->setReturnVar(c->constScriptVar(InfinityPositive)); return; } else if(a_i<0) { c->setReturnVar(c->newScriptVar(0)); return; } scReturnDouble( exp( GET_DOUBLE(a) ) ); } @@ -293,7 +324,7 @@ static void scMathPow(const CFunctionsScopePtr &c, void *userdata) { int a_i = a->isInfinity(), b_i = b->isInfinity(); double a_d = a->getDouble(), b_d = b->getDouble(); if(b_i>0) { - if(a_i || a_d>1.0 || a_d<-1.0) { c->setReturnVar(c->newScriptVar(InfinityPositive)); return; } + if(a_i || a_d>1.0 || a_d<-1.0) { c->setReturnVar(c->constScriptVar(InfinityPositive)); return; } else if(a_i==0 && (a_d==1.0 || a_d==-1.0)) { c->setReturnVar(c->newScriptVar(1)); return; } if(a_i==0 && a_d<1.0 && a_d>-1.0) { c->setReturnVar(c->newScriptVar(0)); return; } } else if(b_i<0) { c->setReturnVar(c->newScriptVar(0)); return; } @@ -301,7 +332,7 @@ static void scMathPow(const CFunctionsScopePtr &c, void *userdata) { if(a_i) a_d = a_i; double result = pow(a_d, b_d); - if(a_i) { c->setReturnVar(c->newScriptVar(Infinity(result>=0.0?1:-1))); return; } + if(a_i) { c->setReturnVar(c->constScriptVar(Infinity(result>=0.0?1:-1))); return; } scReturnDouble( result ); } @@ -320,7 +351,7 @@ static void scMathSqrt(const CFunctionsScopePtr &c, void *userdata) { // ----------------------------------------------- Register Functions void registerMathFunctions(CTinyJS *tinyJS) { - CScriptVarLink *Math = tinyJS->getRoot()->addChild("Math", tinyJS->newScriptVar(Object)); + CScriptVarPtr Math = tinyJS->getRoot()->addChild("Math", tinyJS->newScriptVar(Object)); // --- Math and Trigonometry functions --- tinyJS->addNative("function Math.abs(a)", scMathAbs, 0); @@ -336,13 +367,13 @@ void registerMathFunctions(CTinyJS *tinyJS) { // atan2, ceil, floor, random, round, - (*Math)->addChild("LN2", tinyJS->newScriptVar(k_LN2), SCRIPTVARLINK_ENUMERABLE); - (*Math)->addChild("LN10", tinyJS->newScriptVar(k_LN10), SCRIPTVARLINK_ENUMERABLE); - (*Math)->addChild("LOG2E", tinyJS->newScriptVar(k_LOG2E), SCRIPTVARLINK_ENUMERABLE); - (*Math)->addChild("LOG10E", tinyJS->newScriptVar(k_LOG10E), SCRIPTVARLINK_ENUMERABLE); - (*Math)->addChild("SQRT1_2", tinyJS->newScriptVar(k_SQRT1_2), SCRIPTVARLINK_ENUMERABLE); - (*Math)->addChild("SQRT2", tinyJS->newScriptVar(k_SQRT2), SCRIPTVARLINK_ENUMERABLE); - (*Math)->addChild("PI", tinyJS->newScriptVar(k_PI), SCRIPTVARLINK_ENUMERABLE); + Math->addChild("LN2", tinyJS->newScriptVar(k_LN2), SCRIPTVARLINK_ENUMERABLE); + Math->addChild("LN10", tinyJS->newScriptVar(k_LN10), SCRIPTVARLINK_ENUMERABLE); + Math->addChild("LOG2E", tinyJS->newScriptVar(k_LOG2E), SCRIPTVARLINK_ENUMERABLE); + Math->addChild("LOG10E", tinyJS->newScriptVar(k_LOG10E), SCRIPTVARLINK_ENUMERABLE); + Math->addChild("SQRT1_2", tinyJS->newScriptVar(k_SQRT1_2), SCRIPTVARLINK_ENUMERABLE); + Math->addChild("SQRT2", tinyJS->newScriptVar(k_SQRT2), SCRIPTVARLINK_ENUMERABLE); + Math->addChild("PI", tinyJS->newScriptVar(k_PI), SCRIPTVARLINK_ENUMERABLE); // tinyJS->addNative("function Math.PI()", scMathPI, 0); tinyJS->addNative("function Math.toDegrees(a)", scMathToDegrees, 0); tinyJS->addNative("function Math.toRadians(a)", scMathToRadians, 0); @@ -359,7 +390,7 @@ void registerMathFunctions(CTinyJS *tinyJS) { tinyJS->addNative("function Math.tanh(a)", scMathTanh, 0); tinyJS->addNative("function Math.atanh(a)", scMathATanh, 0); - (*Math)->addChild("E", tinyJS->newScriptVar(k_E), SCRIPTVARLINK_ENUMERABLE); + Math->addChild("E", tinyJS->newScriptVar(k_E), SCRIPTVARLINK_ENUMERABLE); // tinyJS->addNative("function Math.E()", scMathE, 0); tinyJS->addNative("function Math.log(a)", scMathLog, 0); tinyJS->addNative("function Math.log10(a)", scMathLog10, 0); diff --git a/TinyJS_MathFunctions.h b/TinyJS_MathFunctions.h index 935fdd9..5249e1f 100644 --- a/TinyJS_MathFunctions.h +++ b/TinyJS_MathFunctions.h @@ -1,3 +1,43 @@ +/* + * TinyJS + * + * A single-file Javascript-alike engine + * + * - Math and Trigonometry functions + * + * Authored By O.Z.L.B. + * + * Copyright (C) 2011 O.Z.L.B. + * + + * 42TinyJS + * + * A fork of TinyJS with the goal to makes a more JavaScript/ECMA compliant engine + * + * Authored / Changed By Armin Diedering + * + * Copyright (C) 2010 ardisoft + * + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #ifndef TINYJS_MATHFUNCTIONS_H #define TINYJS_MATHFUNCTIONS_H diff --git a/TinyJS_StringFunctions.cpp b/TinyJS_StringFunctions.cpp index 40acc85..990cfb6 100644 --- a/TinyJS_StringFunctions.cpp +++ b/TinyJS_StringFunctions.cpp @@ -1,36 +1,30 @@ -/* - * TinyJS - * - * A single-file Javascript-alike engine - * - * Authored By Gordon Williams - * - * Copyright (C) 2009 Pur3 Ltd - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ /* * 42TinyJS * - * A fork of TinyJS + * A fork of TinyJS with the goal to makes a more JavaScript/ECMA compliant engine + * + * Authored By Armin Diedering * * Copyright (C) 2010 ardisoft * - * Authored By Armin Diedering * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ #include @@ -40,8 +34,8 @@ using namespace std; // ----------------------------------------------- Actual Functions static void scStringCharAt(const CFunctionsScopePtr &c, void *) { - string str = c->getParameter("this")->getString(); - int p = c->getParameter("pos")->getInt(); + string str = c->getArgument("this")->getString(); + int p = c->getArgument("pos")->getInt(); if (p>=0 && p<(int)str.length()) c->setReturnVar(c->newScriptVar(str.substr(p, 1))); else @@ -49,8 +43,8 @@ static void scStringCharAt(const CFunctionsScopePtr &c, void *) { } static void scStringCharCodeAt(const CFunctionsScopePtr &c, void *) { - string str = c->getParameter("this")->getString(); - int p = c->getParameter("pos")->getInt(); + string str = c->getArgument("this")->getString(); + int p = c->getArgument("pos")->getInt(); if (p>=0 && p<(int)str.length()) c->setReturnVar(c->newScriptVar(str.at(p))); else @@ -58,30 +52,30 @@ static void scStringCharCodeAt(const CFunctionsScopePtr &c, void *) { } static void scStringConcat(const CFunctionsScopePtr &c, void *userdata) { - int length = c->getParameterLength(); - string str = c->getParameter("this")->getString(); + int length = c->getArgumentsLength(); + string str = c->getArgument("this")->getString(); for(int i=(int)userdata; igetParameter(i)->getString()); + str.append(c->getArgument(i)->getString()); c->setReturnVar(c->newScriptVar(str)); } static void scStringIndexOf(const CFunctionsScopePtr &c, void *userdata) { - string str = c->getParameter("this")->getString(); - string search = c->getParameter("search")->getString(); + string str = c->getArgument("this")->getString(); + string search = c->getArgument("search")->getString(); size_t p = (userdata==0) ? str.find(search) : str.rfind(search); int val = (p==string::npos) ? -1 : p; c->setReturnVar(c->newScriptVar(val)); } static void scStringSlice(const CFunctionsScopePtr &c, void *userdata) { - string str = c->getParameter("this")->getString(); - int length = c->getParameterLength()-((int)userdata & 1); + string str = c->getArgument("this")->getString(); + int length = c->getArgumentsLength()-((int)userdata & 1); bool slice = ((int)userdata & 2) == 0; - int start = c->getParameter("start")->getInt(); + int start = c->getArgument("start")->getInt(); int end = (int)str.size(); if(slice && start<0) start = str.size()+start; if(length>1) { - end = c->getParameter("end")->getInt(); + end = c->getArgument("end")->getInt(); if(slice && end<0) end = str.size()+end; } if(!slice && end < start) { end^=start; start^=end; end^=start; } @@ -95,9 +89,9 @@ static void scStringSlice(const CFunctionsScopePtr &c, void *userdata) { } static void scStringSplit(const CFunctionsScopePtr &c, void *) { - string str = c->getParameter("this")->getString(); - CScriptVarPtr sep_var = c->getParameter("separator"); - CScriptVarPtr limit_var = c->getParameter("limit"); + string str = c->getArgument("this")->getString(); + CScriptVarPtr sep_var = c->getArgument("separator"); + CScriptVarPtr limit_var = c->getArgument("limit"); int limit = limit_var->isUndefined() ? 0x7fffffff : limit_var->getInt(); CScriptVarPtr result(newScriptVar(c->getContext(), Array)); @@ -125,32 +119,32 @@ static void scStringSplit(const CFunctionsScopePtr &c, void *) { } static void scStringSubstr(const CFunctionsScopePtr &c, void *userdata) { - string str = c->getParameter("this")->getString(); - int length = c->getParameterLength()-(int)userdata; - int start = c->getParameter("start")->getInt(); + string str = c->getArgument("this")->getString(); + int length = c->getArgumentsLength()-(int)userdata; + int start = c->getArgument("start")->getInt(); if(start<0 || start>=(int)str.size()) c->setReturnVar(c->newScriptVar("")); else if(length>1) { - int length = c->getParameter("length")->getInt(); + int length = c->getArgument("length")->getInt(); c->setReturnVar(c->newScriptVar(str.substr(start, length))); } else c->setReturnVar(c->newScriptVar(str.substr(start))); } static void scStringToLowerCase(const CFunctionsScopePtr &c, void *) { - string str = c->getParameter("this")->getString(); + string str = c->getArgument("this")->getString(); transform(str.begin(), str.end(), str.begin(), ::tolower); c->setReturnVar(c->newScriptVar(str)); } static void scStringToUpperCase(const CFunctionsScopePtr &c, void *) { - string str = c->getParameter("this")->getString(); + string str = c->getArgument("this")->getString(); transform(str.begin(), str.end(), str.begin(), ::toupper); c->setReturnVar(c->newScriptVar(str)); } static void scStringTrim(const CFunctionsScopePtr &c, void *userdata) { - string str = c->getParameter("this")->getString(); + string str = c->getArgument("this")->getString(); string::size_type start = 0; string::size_type end = string::npos; if((((int)userdata) & 2) == 0) { @@ -167,7 +161,7 @@ static void scStringTrim(const CFunctionsScopePtr &c, void *userdata) { static void scCharToInt(const CFunctionsScopePtr &c, void *) { - string str = c->getParameter("ch")->getString();; + string str = c->getArgument("ch")->getString();; int val = 0; if (str.length()>0) val = (int)str.c_str()[0]; @@ -177,7 +171,7 @@ static void scCharToInt(const CFunctionsScopePtr &c, void *) { static void scStringFromCharCode(const CFunctionsScopePtr &c, void *) { char str[2]; - str[0] = c->getParameter("char")->getInt(); + str[0] = c->getArgument("char")->getInt(); str[1] = 0; c->setReturnVar(c->newScriptVar(str)); } diff --git a/TinyJS_StringFunctions.h b/TinyJS_StringFunctions.h index ccafc94..8549d24 100644 --- a/TinyJS_StringFunctions.h +++ b/TinyJS_StringFunctions.h @@ -1,26 +1,30 @@ /* - * TinyJS + * 42TinyJS * - * A single-file Javascript-alike engine + * A fork of TinyJS with the goal to makes a more JavaScript/ECMA compliant engine * - * Authored By Gordon Williams + * Authored By Armin Diedering * - * Copyright (C) 2009 Pur3 Ltd + * Copyright (C) 2010 ardisoft * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ #ifndef TINYJS_STRINGFUNCTIONS_H diff --git a/lib-tiny-js.vcxproj b/lib-tiny-js.vcxproj index 6526bd8..80a5e7c 100644 --- a/lib-tiny-js.vcxproj +++ b/lib-tiny-js.vcxproj @@ -77,7 +77,6 @@ - diff --git a/lib-tiny-js.vcxproj.filters b/lib-tiny-js.vcxproj.filters index c36d8dd..745a842 100644 --- a/lib-tiny-js.vcxproj.filters +++ b/lib-tiny-js.vcxproj.filters @@ -32,9 +32,6 @@ - - Headerdateien - Headerdateien diff --git a/pool_allocator.cpp b/pool_allocator.cpp index fe3d567..b794ad3 100644 --- a/pool_allocator.cpp +++ b/pool_allocator.cpp @@ -1,5 +1,37 @@ +/* + * 42TinyJS + * + * A fork of TinyJS with the goal to makes a more JavaScript/ECMA compliant engine + * + * Authored By Armin Diedering + * + * Copyright (C) 2010 ardisoft + * + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #include "pool_allocator.h" -#include +#include +#include +#include + struct block { block* next; }; @@ -28,8 +60,9 @@ fixed_size_allocator::fixed_size_allocator( size_t numObjects, size_t objectSize frees= max = current= - blocks = 0; + blocks = #endif + refs = 0; } fixed_size_allocator::~fixed_size_allocator() @@ -41,20 +74,23 @@ fixed_size_allocator::~fixed_size_allocator() } #ifdef DEBUG_POOL_ALLOCATOR # ifndef LOG_POOL_ALLOCATOR_MEMORY_USAGE - if(allocs != frees) { + if(refs) { # endif fprintf(stderr, "allocator [%s](%d) destroyed\n", name.c_str(), object_size); fprintf(stderr, " allocs:%i, ", allocs); fprintf(stderr, "frees:%i, ", frees); fprintf(stderr, "max:%i, ", max); - fprintf(stderr, "blocks:%i\n\n", blocks); + fprintf(stderr, "blocks:%i\n", blocks); + if(refs) fprintf(stderr, "************ %i x not freed ************\n", refs); + fprintf(stderr, "\n"); # ifndef LOG_POOL_ALLOCATOR_MEMORY_USAGE } # endif #endif } -void* fixed_size_allocator::alloc( size_t ) { +void* fixed_size_allocator::_alloc( size_t ) { + refs++; #ifdef DEBUG_POOL_ALLOCATOR allocs++;current++; if(current>max)max=current; @@ -86,64 +122,132 @@ void* fixed_size_allocator::alloc( size_t ) { # define ASSERT(X) assert(X) #endif -void fixed_size_allocator::free( void* p, size_t ) { - if(p == 0) return; +bool fixed_size_allocator::_free( void* p, size_t ) { + if(p == 0) return refs==0; + refs--; #ifdef DEBUG_POOL_ALLOCATOR + ASSERT(refs>=0); frees++;current--; #endif block* dead_object = static_cast(p); dead_object->next = static_cast(head_of_free_list); head_of_free_list = dead_object; + return refs==0; +} +typedef std::vector allocator_pool_t; +typedef allocator_pool_t::iterator allocator_pool_it; + +static bool compare_allocator_pool(fixed_size_allocator *allocator, size_t Size) { + return allocator->objectSize() < Size; } -typedef std::map allocator_pool_t; -typedef std::map::iterator allocator_pool_it; static class _allocator_pool { public: - _allocator_pool() : last_allocate_allocator(0), last_free_allocator(0) { + _allocator_pool() : allocator_pool(0) { #ifdef LOG_POOL_ALLOCATOR_MEMORY_USAGE last_ok = last_access = 0; #endif } ~_allocator_pool() { - for(allocator_pool_it it = allocator_pool.begin(); it!=allocator_pool.end(); it++) - delete it->second; + if(allocator_pool && !allocator_pool->empty()) + for(allocator_pool_it it = allocator_pool->begin(); it!=allocator_pool->end(); it++) + delete *it; + delete allocator_pool; #ifdef LOG_POOL_ALLOCATOR_MEMORY_USAGE if(last_access) fprintf(stderr, "last_ok:%i(%i)=%i%%\n", last_ok, last_access, (last_ok*100)/last_access); #endif } - allocator_pool_t allocator_pool; - fixed_size_allocator *last_allocate_allocator; - fixed_size_allocator *last_free_allocator; + allocator_pool_it *findAllocator(size_t size, allocator_pool_it &it) { + if(!allocator_pool) return 0; + it = lower_bound(allocator_pool->begin(), allocator_pool->end(), size, compare_allocator_pool); + if(it != allocator_pool->end() && (*it)->objectSize() == size) + return ⁢ + return 0; + } + fixed_size_allocator *checkLastAllocator(size_t size) { #ifdef LOG_POOL_ALLOCATOR_MEMORY_USAGE - int last_ok; - int last_access; + last_access++; #endif -}allocator_pool; -fixed_size_allocator *fixed_size_allocator::get(size_t size, bool for_alloc, const char *for_class) { + if(last_allocate_allocator && last_allocate_allocator->objectSize()==size) { #ifdef LOG_POOL_ALLOCATOR_MEMORY_USAGE - allocator_pool.last_access++; + last_ok++; #endif - if(allocator_pool.last_allocate_allocator && allocator_pool.last_allocate_allocator->object_size==size) { + return last_allocate_allocator; + } + else if(last_free_allocator && last_free_allocator->objectSize()==size) { #ifdef LOG_POOL_ALLOCATOR_MEMORY_USAGE - allocator_pool.last_ok++; + last_ok++; #endif - return allocator_pool.last_allocate_allocator; + return last_free_allocator; + } else + return 0; + } + void removeAllocator(allocator_pool_it it) { + if(last_allocate_allocator == *it) last_allocate_allocator = 0; + if(last_free_allocator == *it) last_free_allocator = 0; + delete *it; allocator_pool->erase(it); + if(allocator_pool->empty()) { + delete allocator_pool; allocator_pool=0; + } } - else if(allocator_pool.last_free_allocator && allocator_pool.last_free_allocator->object_size==size) { + allocator_pool_t *allocator_pool; + fixed_size_allocator *last_allocate_allocator; + fixed_size_allocator *last_free_allocator; #ifdef LOG_POOL_ALLOCATOR_MEMORY_USAGE - allocator_pool.last_ok++; + int last_ok; + int last_access; #endif - return allocator_pool.last_free_allocator; +}allocator_pool; + +//#define WITH_TIME_LOGGER +#include "time_logger.h" + +TimeLoggerCreate(alloc, false); +TimeLoggerCreate(free, false); +fixed_size_allocator_lock *fixed_size_allocator::locker = 0; +class lock_help { +public: + lock_help() { if(fixed_size_allocator::locker) fixed_size_allocator::locker->lock(); } + ~lock_help() { if(fixed_size_allocator::locker) fixed_size_allocator::locker->unlock(); } +}; +void* fixed_size_allocator::alloc(size_t size, const char *for_class) { + TimeLoggerHelper(alloc); + lock_help lock; + if(!allocator_pool.allocator_pool) { + allocator_pool.allocator_pool = new allocator_pool_t(); + allocator_pool.last_allocate_allocator = allocator_pool.last_free_allocator = 0; } - allocator_pool_it it = allocator_pool.allocator_pool.find(size); - if(it != allocator_pool.allocator_pool.end()) - return (for_alloc ? allocator_pool.last_allocate_allocator : allocator_pool.last_free_allocator) = it->second; - else if(for_alloc) { - return allocator_pool.last_allocate_allocator = allocator_pool.allocator_pool[size] = new fixed_size_allocator(64, size, for_class); - } else + fixed_size_allocator *last = allocator_pool.checkLastAllocator(size); + if(last) + return last->_alloc(size); + else { + allocator_pool_it it; if(allocator_pool.findAllocator(size, it)) + return (allocator_pool.last_allocate_allocator = *(it))->_alloc(size); + else { + return (allocator_pool.last_allocate_allocator = (*allocator_pool.allocator_pool->insert(it, new fixed_size_allocator(64, size, for_class))))->_alloc(size); + } + } +} +void fixed_size_allocator::free(void *p, size_t size) { + TimeLoggerHelper(free); + lock_help lock; + if(!allocator_pool.allocator_pool) { ASSERT(0/* free called but not allocator defined*/); - return 0; + return; + } + fixed_size_allocator *last = allocator_pool.checkLastAllocator(size); + if(last) { + if( last->_free(p, size) ) { + allocator_pool_it it; if(allocator_pool.findAllocator(size, it)) + allocator_pool.removeAllocator(it); + } + } else { + allocator_pool_it it; if(allocator_pool.findAllocator(size, it)) { + if( (allocator_pool.last_free_allocator = *it)->_free(p, size) ) + allocator_pool.removeAllocator(it); + } else + ASSERT(0/* free called but not allocator defined*/); + } } diff --git a/pool_allocator.h b/pool_allocator.h index 42b198b..8c839fb 100644 --- a/pool_allocator.h +++ b/pool_allocator.h @@ -1,3 +1,32 @@ +/* + * 42TinyJS + * + * A fork of TinyJS with the goal to makes a more JavaScript/ECMA compliant engine + * + * Authored By Armin Diedering + * + * Copyright (C) 2010 ardisoft + * + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #ifndef pool_allocator_h__ #define pool_allocator_h__ @@ -6,27 +35,73 @@ #include #include + +/************************************************************************ + * TinyJS must many many times allocates and frees objects + * To prevent memory fragmentation and speed ups allocates & frees, i have + * added to 42TinyJS a pool_allocator. This allocator allocates every 64 objects + * as a pool of objects. Is an object needed it can faster allocated from this pool as + * from the heap. + ************************************************************************/ + //#define LOG_POOL_ALLOCATOR_MEMORY_USAGE #if defined(_DEBUG) || defined(LOG_POOL_ALLOCATOR_MEMORY_USAGE) # define DEBUG_POOL_ALLOCATOR #endif +/************************************************************************/ +/* M U L T I - T H R E A D I N G */ +/* The fixed_size_allocator is alone not thread-save. */ +/* For multi-threading you must make it explicit thread-save. */ +/* To do this, you must declare a class derived from "class */ +/* fixed_size_allocator_lock" and overload the member-methods */ +/* "lock" and "unlock" and assign a pointer of a instance of this class */ +/* to "fixed_size_allocator::locker" */ +/* */ +/* Example: */ +/* class my_fixed_size_lock : public fixed_size_allocator_lock { */ +/* public: */ +/* my_fixed_size_lock() { create_mutex(&mutex); } */ +/* ~my_fixed_size_lock() { delete_mutex(&mutex); } */ +/* virtual void lock() { lock_mutex(&mutex); } */ +/* virtual void unlock() { unlock_mutex(&mutex); } */ +/* private: */ +/* mutex_t mutex; */ +/* }; */ +/* */ +/* int main() { */ +/* my_fixed_size_lock lock; */ +/* fixed_size_allocator::locker = &lock; */ +/* .... */ +/* } */ +/* */ +/************************************************************************/ +class fixed_size_allocator_lock { +public: + virtual void lock()=0; + virtual void unlock()=0; +}; + struct block_head; class fixed_size_allocator { public: ~fixed_size_allocator(); - void* alloc(size_t); - void free(void* p, size_t); - static fixed_size_allocator *get(size_t,bool,const char* for_class=0); + static void *alloc(size_t,const char* for_class=0); + static void free(void *, size_t); + size_t objectSize() { return object_size; } + static fixed_size_allocator_lock *locker; private: fixed_size_allocator(size_t num_objects, size_t object_size, const char* for_class); fixed_size_allocator(const fixed_size_allocator&); fixed_size_allocator& operator=(const fixed_size_allocator&); + void *_alloc(size_t); + bool _free(void* p, size_t); size_t num_objects; size_t object_size; void *head_of_free_list; block_head *head; + int refs; #ifdef DEBUG_POOL_ALLOCATOR // Debug std::string name; @@ -43,16 +118,16 @@ class fixed_size_object { public: static void* operator new(size_t size) { #ifdef DEBUG_POOL_ALLOCATOR - return fixed_size_allocator::get(size, true, typeid(T).name())->alloc(size); + return fixed_size_allocator::alloc(size, typeid(T).name()); #else - return fixed_size_allocator::get(size, true)->alloc(size); + return fixed_size_allocator::alloc(size); #endif } static void* operator new(size_t size, void* p) { return p; } static void operator delete(void* p, size_t size) { - fixed_size_allocator::get(size, false)->free(p, size); + fixed_size_allocator::free(p, size); } private: }; @@ -116,4 +191,4 @@ public : }; #endif -#endif // pool_allocator_h__ \ No newline at end of file +#endif // pool_allocator_h__ diff --git a/run_tests.cpp b/run_tests.cpp index 16aa3c4..ae28e69 100644 --- a/run_tests.cpp +++ b/run_tests.cpp @@ -7,22 +7,36 @@ * * Copyright (C) 2009 Pur3 Ltd * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + + * 42TinyJS + * + * A fork of TinyJS with the goal to makes a more JavaScript/ECMA compliant engine + * + * Authored / Changed By Armin Diedering * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. + * Copyright (C) 2010 ardisoft * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + /* * This is a program to run all the tests in the tests folder... */ @@ -38,6 +52,7 @@ # define DEBUG_MEMORY 1 # endif #endif +#define _CRT_SECURE_NO_WARNINGS #include "TinyJS.h" #include "TinyJS_Functions.h" @@ -47,15 +62,16 @@ #include #include #include -#include +#include +#include //#define WITH_TIME_LOGGER - -#ifdef WITH_TIME_LOGGER -# include "time_logger.h" -#endif //#define INSANE_MEMORY_DEBUG + + +#include "time_logger.h" + #ifdef INSANE_MEMORY_DEBUG // needs -rdynamic when compiling/linking #include @@ -244,10 +260,10 @@ bool run_test(const char *filename) { try { s.execute(buffer); } catch (CScriptException *e) { - printf("ERROR: %s\n", e->text.c_str()); + printf("%s\n", e->toString().c_str()); delete e; } - bool pass = s.getRoot()->getParameter("result")->getBool(); + bool pass = (*s.getRoot()->findChild("result"))->getBool(); #ifdef WITH_TIME_LOGGER TimeLoggerLogprint(Test); #endif diff --git a/run_tests.vcxproj b/run_tests.vcxproj index e34f207..ac0ead4 100644 --- a/run_tests.vcxproj +++ b/run_tests.vcxproj @@ -51,7 +51,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WITH_TIME_LOGGER;%(PreprocessorDefinitions) Console @@ -66,7 +66,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + WIN32;NDEBUG;_CONSOLE;WITH_TIME_LOGGER;%(PreprocessorDefinitions) Console diff --git a/time_logger.h b/time_logger.h index 653d5e8..0daaec8 100644 --- a/time_logger.h +++ b/time_logger.h @@ -1,6 +1,36 @@ +/* + * 42TinyJS + * + * A fork of TinyJS with the goal to makes a more JavaScript/ECMA compliant engine + * + * Authored By Armin Diedering + * + * Copyright (C) 2010 ardisoft + * + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + #ifndef time_logger_h__ #define time_logger_h__ -#ifdef _DEBUG +#if defined(WITH_TIME_LOGGER) #include #include @@ -90,8 +120,9 @@ class _TimeLoggerHelper { # define TimeLoggerStart(...) do{}while(0) # define TimeLoggerStop(...) do{}while(0) # define TimeLoggerLogprint(a) do{}while(0) +# define TimeLoggerHelper(a) do{}while(0) #endif /* _DEBUG */ -#endif // time_logger_h__ \ No newline at end of file +#endif // time_logger_h__ From 818cf27dd383ee7017a40d283021b805d8401265 Mon Sep 17 00:00:00 2001 From: armin Date: Mon, 31 Dec 2012 09:18:28 +0000 Subject: [PATCH 42/43] 42TinyJS: Milestone-2 (New Year release 2013) for changelog visit http://code.google.com/p/42tiny-js/source/list Release 27-60 --- Script.cpp | 18 +- Script.vcxproj | 10 +- Script.vcxproj.filters | 8 - TinyJS.cpp | 4927 ++++++++++++++++++++++------------- TinyJS.h | 1485 +++++++---- TinyJS_Functions.cpp | 42 +- TinyJS_Functions.h | 6 +- TinyJS_MathFunctions.cpp | 422 +-- TinyJS_MathFunctions.h | 6 +- TinyJS_StringFunctions.cpp | 441 +++- TinyJS_StringFunctions.h | 6 +- TinyJS_Threading.cpp | 88 + TinyJS_Threading.h | 70 + config.h | 128 + lib-tiny-js.vcxproj | 5 +- lib-tiny-js.vcxproj.filters | 12 +- pool_allocator.cpp | 17 +- pool_allocator.h | 46 +- run_tests.cpp | 34 +- run_tests.vcxproj | 7 +- run_tests.vcxproj.filters | 3 - time_logger.h | 2 +- 22 files changed, 4963 insertions(+), 2820 deletions(-) create mode 100644 TinyJS_Threading.cpp create mode 100644 TinyJS_Threading.h create mode 100644 config.h diff --git a/Script.cpp b/Script.cpp index 4a51e6f..85e142e 100755 --- a/Script.cpp +++ b/Script.cpp @@ -14,7 +14,7 @@ * * Authored / Changed By Armin Diedering * - * Copyright (C) 2010 ardisoft + * Copyright (C) 2010-2012 ardisoft * * * Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -42,12 +42,12 @@ */ #include "TinyJS.h" -#include "TinyJS_Functions.h" -#include "TinyJS_StringFunctions.h" -#include "TinyJS_MathFunctions.h" +//#include "TinyJS_Functions.h" +//#include "TinyJS_StringFunctions.h" +//#include "TinyJS_MathFunctions.h" #include #include - + #ifdef _DEBUG # ifndef _MSC_VER # define DEBUG_MEMORY 1 @@ -60,7 +60,7 @@ const char *code = "function myfunc(x, y) { return x + y; } var a = myfunc(1,2); print(a);"; void js_print(const CFunctionsScopePtr &v, void *) { - printf("> %s\n", v->getArgument("text")->getString().c_str()); + printf("> %s\n", v->getArgument("text")->toString().c_str()); } void js_dump(const CFunctionsScopePtr &v, void *) { @@ -72,9 +72,9 @@ int main(int , char **) { CTinyJS *js = new CTinyJS(); /* add the functions from TinyJS_Functions.cpp */ - registerFunctions(js); - registerStringFunctions(js); - registerMathFunctions(js); +// registerFunctions(js); +// registerStringFunctions(js); +// registerMathFunctions(js); /* Add a native function */ js->addNative("function print(text)", &js_print, 0); // js->addNative("function dump()", &js_dump, js); diff --git a/Script.vcxproj b/Script.vcxproj index 92bb1c2..94ee2ab 100644 --- a/Script.vcxproj +++ b/Script.vcxproj @@ -52,7 +52,7 @@ Disabled %(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;_AFXDLL;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true EnableFastChecks MultiThreadedDebugDLL @@ -72,7 +72,7 @@ MaxSpeed true %(AdditionalIncludeDirectories) - WIN32;NDEBUG;_CONSOLE;_AFXDLL;%(PreprocessorDefinitions) + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) MultiThreadedDLL true @@ -91,12 +91,6 @@ - - - - - - {9751c465-0294-43cd-a5d9-bd038aba3961} diff --git a/Script.vcxproj.filters b/Script.vcxproj.filters index b432d09..7a7dc59 100644 --- a/Script.vcxproj.filters +++ b/Script.vcxproj.filters @@ -19,12 +19,4 @@ Quelldateien - - - Headerdateien - - - - - \ No newline at end of file diff --git a/TinyJS.cpp b/TinyJS.cpp index 7aa69e6..55c4200 100755 --- a/TinyJS.cpp +++ b/TinyJS.cpp @@ -14,7 +14,7 @@ * * Authored / Changed By Armin Diedering * - * Copyright (C) 2010 ardisoft + * Copyright (C) 2010-2012 ardisoft * * * Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -41,68 +41,32 @@ # define DEBUG_MEMORY 1 # endif #endif -#include -#include #include -#include -#include -#include -#include -#include // for auto_ptr -#include #include "TinyJS.h" #ifndef ASSERT # define ASSERT(X) assert(X) #endif -#if 0 -inline CScriptVarSmartLink &CScriptVarSmartLink::operator = (CScriptVarPtr Var) { - if(link && !link->isOwned()) delete link; - link = new CScriptVarLink(Var); -/* - if (!link || link->isOwned()) - link = new CScriptVarLink(Var); - else { - link->replaceWith(Var); - link->name=TINYJS_TEMP_NAME; - } -*/ - return *this; -} -inline CScriptVarSmartLink &CScriptVarSmartLink::operator = (const CScriptVarSmartLink &Link) { - if(link && !link->isOwned()) delete link; - link = Link.link; - ((CScriptVarSmartLink &)Link).link = 0; // explicit cast to a non const ref - return *this; -} -// this operator corresponds "CLEAN(link); link = Link;" -inline CScriptVarSmartLink &CScriptVarSmartLink::operator = (CScriptVarLink *Link) { - if(link && !link->isOwned()) delete link; - link = Link; - return *this; -} -// this operator corresponds "link->replaceWith(Link->get())" -inline CScriptVarSmartLink &CScriptVarSmartLink::operator <<(CScriptVarSmartLink &Link) { - ASSERT(link && Link.link); - link->replaceWith(Link.link); - return *this; -} +#ifndef NO_REGEXP +# if defined HAVE_TR1_REGEX +# include + using namespace std::tr1; +# elif defined HAVE_BOOST_REGEX +# include + using namespace boost; +# else +# include +# endif #endif using namespace std; -#ifdef __GNUC__ -# define vsprintf_s vsnprintf -# define vsnprintf_s(buffer, size, len, format, args) vsnprintf(buffer, size, format, args) -# define sprintf_s snprintf -# define _strdup strdup -#else -# define __attribute__(x) -#endif - -// ----------------------------------------------------------------------------------- Memory Debug +// ----------------------------------------------------------------------------------- +////////////////////////////////////////////////////////////////////////// +/// Memory Debug +////////////////////////////////////////////////////////////////////////// //#define DEBUG_MEMORY 1 @@ -143,35 +107,42 @@ void show_allocated() { allocatedVars[i]->trace(" "); } for (size_t i=0;igetName().c_str(), (*allocatedLinks[i])->getRefs()); - (*allocatedLinks[i])->trace(" "); + printf("ALLOCATED LINK %s, allocated[%d] to \n", allocatedLinks[i]->getName().c_str(), allocatedLinks[i]->getVarPtr()->getRefs()); + allocatedLinks[i]->getVarPtr()->trace(" "); } allocatedVars.clear(); allocatedLinks.clear(); } #endif -// ----------------------------------------------------------------------------------- Utils -bool isWhitespace(char ch) { + +////////////////////////////////////////////////////////////////////////// +/// Utils +////////////////////////////////////////////////////////////////////////// + +inline bool isWhitespace(char ch) { return (ch==' ') || (ch=='\t') || (ch=='\n') || (ch=='\r'); } -bool isNumeric(char ch) { +inline bool isNumeric(char ch) { return (ch>='0') && (ch<='9'); } -bool isNumber(const string &str) { - for (size_t i=0;i1 && str[0]=='0') ) return -1; // empty or (more as 1 digit and beginning with '0') + CNumber idx; + const char *endptr; + idx.parseInt(str.c_str(), 10, &endptr); + if(*endptr || idx>uint32_t(0xFFFFFFFFUL)) return -1; + return idx.toUInt32(); } -bool isHexadecimal(char ch) { +inline bool isHexadecimal(char ch) { return ((ch>='0') && (ch<='9')) || ((ch>='a') && (ch<='f')) || ((ch>='A') && (ch<='F')); } -bool isOctal(char ch) { +inline bool isOctal(char ch) { return ((ch>='0') && (ch<='7')); } -bool isAlpha(char ch) { - return ((ch>='a') && (ch<='z')) || ((ch>='A') && (ch<='Z')) || ch=='_'; +inline bool isAlpha(char ch) { + return ((ch>='a') && (ch<='z')) || ((ch>='A') && (ch<='Z')) || ch=='_' || ch=='$'; } bool isIDString(const char *s) { @@ -193,24 +164,35 @@ void replace(string &str, char textFrom, const char *textTo) { p = str.find(textFrom, p+sLen); } } -string int2string(int intData) { - char buffer[32]; - sprintf_s(buffer, sizeof(buffer), "%d", intData); - return buffer; +string int2string(int32_t intData) { + ostringstream str; + str << intData; + return str.str(); +} +string int2string(uint32_t intData) { + ostringstream str; + str << intData; + return str.str(); } string float2string(const double &floatData) { - char buffer[32]; - sprintf_s(buffer, sizeof(buffer), "%f", floatData); - return buffer; + ostringstream str; + str.unsetf(ios::floatfield); +#if (defined(_MSC_VER) && _MSC_VER >= 1600) || __cplusplus >= 201103L + str.precision(numeric_limits::max_digits10); +#else + str.precision(numeric_limits::digits10+2); +#endif + str << floatData; + return str.str(); } /// convert the given string into a quoted string suitable for javascript string getJSString(const string &str) { - string nStr = str; - for (size_t i=0;i127) { - char buffer[5]; - sprintf_s(buffer, sizeof(buffer), "\\x%02X", nCh); + static char hex[] = "0123456789ABCDEF"; + buffer[2] = hex[(nCh>>4)&0x0f]; + buffer[3] = hex[nCh&0x0f]; replaceWith = buffer; - } else replace=false; + }; } } - - if (replace) { - nStr = nStr.substr(0, i) + replaceWith + nStr.substr(i+1); - i += strlen(replaceWith)-1; - } + if (replaceWith) + nStr.append(replaceWith); + else + nStr.push_back(*i); } - return "\"" + nStr + "\""; + nStr.push_back('\"'); + return nStr; } -/** Is the string alphanumeric */ -bool isAlphaNum(const string &str) { - if (str.size()==0) return true; - if (!isAlpha(str[0])) return false; - for (size_t i=0;i= 0) msg << " at Line:" << lineNumber; - if(column >=0) msg << " Column:" << column; + if(lineNumber >= 0) msg << " at Line:" << lineNumber+1; + if(column >=0) msg << " Column:" << column+1; if(fileName.length()) msg << " in " << fileName; return msg.str(); } + ////////////////////////////////////////////////////////////////////////// -/// CSCRIPTLEX +/// CScriptLex ////////////////////////////////////////////////////////////////////////// - CScriptLex::CScriptLex(const char *Code, const string &File, int Line, int Column) : data(Code) { currentFile = File; - currentLineStart = tokenStart = data; - reset(data, Line, data); + pos.currentLineStart = pos.tokenStart = data; + pos.currentLine = Line; + reset(pos); } - -void CScriptLex::reset(const char *toPos, int Line, const char *LineStart) { - dataPos = toPos; - tokenStart = data; +void CScriptLex::reset(const POS &toPos) { ///< Reset this lex so we can start again + dataPos = toPos.tokenStart; tk = last_tk = 0; tkStr = ""; - currentLine = Line; - currentLineStart = LineStart; + pos = toPos; lineBreakBeforeToken = false; currCh = nextCh = 0; getNextCh(); // currCh @@ -293,111 +270,24 @@ void CScriptLex::check(int expected_tk, int alternate_tk/*=-1*/) { if (tk!=expected_tk && tk!=alternate_tk) { ostringstream errorString; if(expected_tk == LEX_EOF) - errorString << "Got unexpected " << getTokenStr(tk); + errorString << "Got unexpected " << CScriptToken::getTokenStr(tk); else - errorString << "Got '" << getTokenStr(tk) << "' expected '" << getTokenStr(expected_tk) << "'"; - if(alternate_tk!=-1) errorString << " or '" << getTokenStr(alternate_tk) << "'"; - throw new CScriptException(SyntaxError, errorString.str(), currentFile, currentLine, currentColumn()); + errorString << "Got '" << CScriptToken::getTokenStr(tk) << "' expected '" << CScriptToken::getTokenStr(expected_tk) << "'"; + if(alternate_tk!=-1) errorString << " or '" << CScriptToken::getTokenStr(alternate_tk) << "'"; + throw new CScriptException(SyntaxError, errorString.str(), currentFile, pos.currentLine, currentColumn()); } } void CScriptLex::match(int expected_tk1, int alternate_tk/*=-1*/) { check(expected_tk1, alternate_tk); - int line = currentLine; + int line = pos.currentLine; getNextToken(); - lineBreakBeforeToken = line != currentLine; -} - -string CScriptLex::getTokenStr(int token) { - if (token>32 && token<128) { - // char buf[4] = "' '"; - // buf[1] = (char)token; - char buf[2] = " "; - buf[0] = (char)token; - return buf; - } - switch (token) { - case LEX_EOF : return "EOF"; - case LEX_ID : return "ID"; - case LEX_INT : return "INT"; - case LEX_FLOAT : return "FLOAT"; - case LEX_STR : return "STRING"; - case LEX_REGEXP : return "REGEXP"; - case LEX_EQUAL : return "=="; - case LEX_TYPEEQUAL : return "==="; - case LEX_NEQUAL : return "!="; - case LEX_NTYPEEQUAL : return "!=="; - case LEX_LEQUAL : return "<="; - case LEX_LSHIFT : return "<<"; - case LEX_LSHIFTEQUAL : return "<<="; - case LEX_GEQUAL : return ">="; - case LEX_RSHIFT : return ">>"; - case LEX_RSHIFTEQUAL : return ">>="; - case LEX_RSHIFTU : return ">>>"; - case LEX_RSHIFTUEQUAL : return ">>>="; - case LEX_PLUSEQUAL : return "+="; - case LEX_MINUSEQUAL : return "-="; - case LEX_PLUSPLUS : return "++"; - case LEX_MINUSMINUS : return "--"; - case LEX_ANDEQUAL : return "&="; - case LEX_ANDAND : return "&&"; - case LEX_OREQUAL : return "|="; - case LEX_OROR : return "||"; - case LEX_XOREQUAL : return "^="; - case LEX_ASTERISKEQUAL : return "*="; - case LEX_SLASHEQUAL : return "/="; - case LEX_PERCENTEQUAL : return "%="; - - // reserved words - case LEX_R_IF : return "if"; - case LEX_R_ELSE : return "else"; - case LEX_R_DO : return "do"; - case LEX_R_WHILE : return "while"; - case LEX_R_FOR : return "for"; - case LEX_T_FOR_IN : return "for"; - case LEX_T_FOR_EACH_IN : return "for each"; - case LEX_R_IN: return "in"; - case LEX_R_BREAK : return "break"; - case LEX_R_CONTINUE : return "continue"; - case LEX_R_FUNCTION : return "function"; - case LEX_T_FUNCTION_OPERATOR : return "function"; - case LEX_T_GET : return "get"; - case LEX_T_SET : return "set"; - - case LEX_R_RETURN : return "return"; - case LEX_R_VAR : return "var"; - case LEX_R_LET : return "let"; - case LEX_R_WITH : return "with"; - case LEX_R_TRUE : return "true"; - case LEX_R_FALSE : return "false"; - case LEX_R_NULL : return "null"; - case LEX_R_NEW : return "new"; - case LEX_R_TRY : return "try"; - case LEX_R_CATCH : return "catch"; - case LEX_R_FINALLY : return "finally"; - case LEX_R_THROW : return "throw"; - case LEX_R_TYPEOF : return "typeof"; - case LEX_R_VOID : return "void"; - case LEX_R_DELETE : return "delete"; - case LEX_R_INSTANCEOF : return "instanceof"; - case LEX_R_SWITCH : return "switch"; - case LEX_R_CASE : return "case"; - case LEX_R_DEFAULT : return "default"; - - case LEX_T_SKIP : return "LEX_SKIP"; - case LEX_T_DUMMY_LABEL: - case LEX_T_LABEL: return "LABEL"; - case LEX_T_LOOP_LABEL: return "LEX_LOOP_LABEL"; - } - - ostringstream msg; - msg << "?[" << token << "]"; - return msg.str(); + lineBreakBeforeToken = line != pos.currentLine; } void CScriptLex::getNextCh() { if(currCh == '\n') { // Windows or Linux - currentLine++; - tokenStart = currentLineStart = dataPos - (nextCh == LEX_EOF ? 0 : 1); + pos.currentLine++; + pos.tokenStart = pos.currentLineStart = dataPos - (nextCh == LEX_EOF ? 0 : 1); } currCh = nextCh; if ( (nextCh = *dataPos) != LEX_EOF ) dataPos++; // stay on EOF @@ -408,6 +298,7 @@ void CScriptLex::getNextCh() { currCh = '\n'; // Mac (only '\r') --> convert '\r' to '\n' } } + static uint16_t not_allowed_tokens_befor_regexp[] = {LEX_ID, LEX_INT, LEX_FLOAT, LEX_STR, LEX_R_TRUE, LEX_R_FALSE, LEX_R_NULL, ']', ')', '.', LEX_EOF}; void CScriptLex::getNextToken() { while (currCh && isWhitespace(currCh)) getNextCh(); @@ -430,56 +321,31 @@ void CScriptLex::getNextToken() { tk = LEX_EOF; tkStr.clear(); // record beginning of this token - tokenStart = dataPos - (nextCh == LEX_EOF ? (currCh == LEX_EOF ? 0 : 1) : 2); + pos.tokenStart = dataPos - (nextCh == LEX_EOF ? (currCh == LEX_EOF ? 0 : 1) : 2); // tokens if (isAlpha(currCh)) { // IDs while (isAlpha(currCh) || isNumeric(currCh)) { tkStr += currCh; getNextCh(); } - tk = LEX_ID; - if (tkStr=="if") tk = LEX_R_IF; - else if (tkStr=="else") tk = LEX_R_ELSE; - else if (tkStr=="do") tk = LEX_R_DO; - else if (tkStr=="while") tk = LEX_R_WHILE; - else if (tkStr=="for") tk = LEX_R_FOR; - else if (tkStr=="in") tk = LEX_R_IN; - else if (tkStr=="break") tk = LEX_R_BREAK; - else if (tkStr=="continue") tk = LEX_R_CONTINUE; - else if (tkStr=="function") tk = LEX_R_FUNCTION; - else if (tkStr=="return") tk = LEX_R_RETURN; - else if (tkStr=="var") tk = LEX_R_VAR; - else if (tkStr=="let") tk = LEX_R_LET; - else if (tkStr=="with") tk = LEX_R_WITH; - else if (tkStr=="true") tk = LEX_R_TRUE; - else if (tkStr=="false") tk = LEX_R_FALSE; - else if (tkStr=="null") tk = LEX_R_NULL; - else if (tkStr=="new") tk = LEX_R_NEW; - else if (tkStr=="try") tk = LEX_R_TRY; - else if (tkStr=="catch") tk = LEX_R_CATCH; - else if (tkStr=="finally") tk = LEX_R_FINALLY; - else if (tkStr=="throw") tk = LEX_R_THROW; - else if (tkStr=="typeof") tk = LEX_R_TYPEOF; - else if (tkStr=="void") tk = LEX_R_VOID; - else if (tkStr=="delete") tk = LEX_R_DELETE; - else if (tkStr=="instanceof") tk = LEX_R_INSTANCEOF; - else if (tkStr=="switch") tk = LEX_R_SWITCH; - else if (tkStr=="case") tk = LEX_R_CASE; - else if (tkStr=="default") tk = LEX_R_DEFAULT; + tk = CScriptToken::isReservedWord(tkStr); } else if (isNumeric(currCh) || (currCh=='.' && isNumeric(nextCh))) { // Numbers if(currCh=='.') tkStr+='0'; - bool isHex = false; - if (currCh=='0') { tkStr += currCh; getNextCh(); } + bool isHex = false, isOct=false; + if (currCh=='0') { + tkStr += currCh; getNextCh(); + if(isOctal(currCh)) isOct = true; + } if (currCh=='x' || currCh=='X') { isHex = true; tkStr += currCh; getNextCh(); } tk = LEX_INT; - while (isNumeric(currCh) || (isHex && isHexadecimal(currCh))) { + while (isOctal(currCh) || (!isOct && isNumeric(currCh)) || (isHex && isHexadecimal(currCh))) { tkStr += currCh; getNextCh(); } - if (!isHex && currCh=='.') { + if (!isHex && !isOct && currCh=='.') { tk = LEX_FLOAT; tkStr += '.'; getNextCh(); @@ -489,7 +355,7 @@ void CScriptLex::getNextToken() { } } // do fancy e-style floating point - if (!isHex && (currCh=='e' || currCh=='E')) { + if (!isHex && !isOct && (currCh=='e' || currCh=='E')) { tk = LEX_FLOAT; tkStr += currCh; getNextCh(); if (currCh=='-') { tkStr += currCh; getNextCh(); } @@ -514,19 +380,22 @@ void CScriptLex::getNextToken() { case 'v': tkStr += '\v'; break; case 'x': { // hex digits getNextCh(); - char buf[3]="\0\0"; - for(int i=0; i<2 && isHexadecimal(currCh); i++) - { - buf[i] = currCh; getNextCh(); - } - tkStr += (char)strtol(buf, 0, 16); + if(isHexadecimal(currCh)) { + char buf[3]="\0\0"; + buf[0] = currCh; + for(int i=0; i<2 && isHexadecimal(nextCh); i++) { + getNextCh(); buf[i] = currCh; + } + tkStr += (char)strtol(buf, 0, 16); + } else + throw new CScriptException(SyntaxError, "malformed hexadezimal character escape sequence", currentFile, pos.currentLine, currentColumn()); } default: { if(isOctal(currCh)) { char buf[4]="\0\0\0"; - for(int i=0; i<3 && isOctal(currCh); i++) - { - buf[i] = currCh; getNextCh(); + buf[0] = currCh; + for(int i=1; i<3 && isOctal(nextCh); i++) { + getNextCh(); buf[i] = currCh; } tkStr += (char)strtol(buf, 0, 8); } @@ -539,7 +408,7 @@ void CScriptLex::getNextToken() { getNextCh(); } if(currCh != endCh) - throw new CScriptException(SyntaxError, "unterminated string literal", currentFile, currentLine, currentColumn()); + throw new CScriptException(SyntaxError, "unterminated string literal", currentFile, pos.currentLine, currentColumn()); getNextCh(); tk = LEX_STR; } else { @@ -630,12 +499,15 @@ void CScriptLex::getNextToken() { tk = LEX_ASTERISKEQUAL; getNextCh(); } else if (tk=='/') { - // check if it's a Regex-Literal + // check if it's a RegExp-Literal tk = LEX_REGEXP; for(uint16_t *p = not_allowed_tokens_befor_regexp; *p; p++) { if(*p==last_tk) { tk = '/'; break; } } if(tk == LEX_REGEXP) { +#ifdef NO_REGEXP + throw new CScriptException(Error, "42TinyJS was built without support for regular expressions", currentFile, pos.currentLine, currentColumn()); +#endif tkStr = "/"; while (currCh && currCh!='/' && currCh!='\n') { if (currCh == '\\' && nextCh == '/') { @@ -646,12 +518,17 @@ void CScriptLex::getNextToken() { getNextCh(); } if(currCh == '/') { +#ifndef NO_REGEXP + try { regex(tkStr.substr(1), regex_constants::ECMAScript); } catch(regex_error e) { + throw new CScriptException(SyntaxError, string(e.what())+" - "+CScriptVarRegExp::ErrorStr(e.code()), currentFile, pos.currentLine, currentColumn()); + } +#endif /* NO_REGEXP */ do { tkStr.append(1, currCh); getNextCh(); - } while (currCh && currCh=='g' && currCh=='i' && currCh=='m' && currCh=='y'); + } while (currCh=='g' || currCh=='i' || currCh=='m' || currCh=='y'); } else - throw new CScriptException(SyntaxError, "unterminated regular expression literal", currentFile, currentLine, currentColumn()); + throw new CScriptException(SyntaxError, "unterminated regular expression literal", currentFile, pos.currentLine, currentColumn()); } else if(currCh=='=') { tk = LEX_SLASHEQUAL; getNextCh(); @@ -664,40 +541,313 @@ void CScriptLex::getNextToken() { /* This isn't quite right yet */ } -// ----------------------------------------------------------------------------------- CSCRIPTTOKEN -CScriptToken::CScriptToken(CScriptLex *l, int Match, int Alternate) : line(l->currentLine), column(l->currentColumn()), token(l->tk), intData(0) + +////////////////////////////////////////////////////////////////////////// +// CScriptTokenDataForwards +////////////////////////////////////////////////////////////////////////// + +bool CScriptTokenDataForwards::compare_fnc_token_by_name::operator()(const CScriptToken& lhs, const CScriptToken& rhs) const { + return lhs.Fnc().name < rhs.Fnc().name; +} +bool CScriptTokenDataForwards::checkRedefinition(const string &Str, bool checkVarsInLetScope) { + STRING_SET_it it = lets.find(Str); + if(it!=lets.end()) return false; + else if(checkVarsInLetScope) { + STRING_SET_it it = vars_in_letscope.find(Str); + if(it!=vars_in_letscope.end()) return false; + } + return true; +} + +void CScriptTokenDataForwards::addVars( STRING_VECTOR_t &Vars ) { + vars.insert(Vars.begin(), Vars.end()); +} +std::string CScriptTokenDataForwards::addVarsInLetscope( STRING_VECTOR_t &Vars ) +{ + for(STRING_VECTOR_it it=Vars.begin(); it!=Vars.end(); ++it) { + if(!checkRedefinition(*it, false)) return *it; + vars_in_letscope.insert(*it); + } + return ""; +} + +std::string CScriptTokenDataForwards::addLets( STRING_VECTOR_t &Lets ) +{ + for(STRING_VECTOR_it it=Lets.begin(); it!=Lets.end(); ++it) { + if(!checkRedefinition(*it, true)) return *it; + lets.insert(*it); + } + return ""; +} + + +////////////////////////////////////////////////////////////////////////// +// CScriptTokenDataFnc +////////////////////////////////////////////////////////////////////////// + +string CScriptTokenDataFnc::getArgumentsString() +{ + ostringstream destination; + destination << "("; + if(arguments.size()) { + const char *comma = ""; + for(TOKEN_VECT_it argument=arguments.begin(); argument!=arguments.end(); ++argument, comma=", ") { + if(argument->token == LEX_ID) + destination << comma << argument->String(); + else { + vector isObject(1, false); + for(DESTRUCTURING_VARS_it it=argument->DestructuringVar().vars.begin(); it!=argument->DestructuringVar().vars.end(); ++it) { + if(it->second == "}" || it->second == "]") { + destination << it->second; + isObject.pop_back(); + } else { + destination << comma; + if(it->second == "[" || it->second == "{") { + comma = ""; + if(isObject.back() && it->first.length()) + destination << getIDString(it->first) << ":"; + destination << it->second; + isObject.push_back(it->second == "{"); + } else { + comma = ", "; + if(it->second.empty()) + continue; // skip empty entries + if(isObject.back() && it->first!=it->second) + destination << getIDString(it->first) << ":"; + destination << it->second; + } + } + } + } + } + } + destination << ") "; + return destination.str(); +} + + +////////////////////////////////////////////////////////////////////////// +// CScriptTokenDataDestructuringVar +////////////////////////////////////////////////////////////////////////// + +void CScriptTokenDataDestructuringVar::getVarNames(STRING_VECTOR_t Names) { + for(DESTRUCTURING_VARS_it it = vars.begin(); it != vars.end(); ++it) { + if(it->second.size() && it->second.find_first_of("{[]}") == string::npos) + Names.push_back(it->second); + } +} + +std::string CScriptTokenDataDestructuringVar::getParsableString() +{ + string out; + const char *comma = ""; + vector isObject(1, false); + for(DESTRUCTURING_VARS_it it=vars.begin(); it!=vars.end(); ++it) { + if(it->second == "}" || it->second == "]") { + out.append(it->second); + isObject.pop_back(); + } else { + out.append(comma); + if(it->second == "[" || it->second == "{") { + comma = ""; + if(isObject.back() && it->first.length()) + out.append(getIDString(it->first)).append(":"); + out.append(it->second); + isObject.push_back(it->second == "{"); + } else { + comma = ", "; + if(it->second.empty()) + continue; // skip empty entries + if(isObject.back() && it->first!=it->second) + out.append(getIDString(it->first)).append(":"); + out.append(it->second); + } + } + } + return out; +} + + +////////////////////////////////////////////////////////////////////////// +// CScriptTokenDataObjectLiteral +////////////////////////////////////////////////////////////////////////// + +void CScriptTokenDataObjectLiteral::setMode(bool Destructuring) { + structuring = !(destructuring = Destructuring); + for(vector::iterator it=elements.begin(); it!=elements.end(); ++it) { + if(it->value.size() && it->value.front().token == LEX_T_OBJECT_LITERAL) { + CScriptTokenDataObjectLiteral& e = it->value.front().Object(); + if(e.destructuring && e.structuring) + e.setMode(Destructuring); + } + } +} + +string CScriptTokenDataObjectLiteral::getParsableString() { - if(token == LEX_INT) - intData = strtol(l->tkStr.c_str(),0,0); - else if(LEX_TOKEN_DATA_FLOAT(token)) - floatData = new double(strtod(l->tkStr.c_str(),0)); - else if(LEX_TOKEN_DATA_STRING(token)) { - stringData = new CScriptTokenDataString(l->tkStr); - stringData->ref(); - } else if(LEX_TOKEN_DATA_FUNCTION(token)) { - fncData = new CScriptTokenDataFnc; - fncData->ref(); + string out = type == OBJECT ? "{ " : "[ "; + const char *comma = ""; + for(vector::iterator it=elements.begin(); it!=elements.end(); ++it) { + out.append(comma); comma=", "; + if(it->value.empty()) continue; + if(type == OBJECT) + out.append(getIDString(it->id)).append(" : "); + out.append(CScriptToken::getParsableString(it->value)); + } + out.append(type == OBJECT ? " }" : " ]"); + return out; +} + + +////////////////////////////////////////////////////////////////////////// +// CScriptToken +////////////////////////////////////////////////////////////////////////// + +typedef struct { int id; const char *str; bool need_space; } token2str_t; +static token2str_t reserved_words_begin[] ={ + // reserved words + { LEX_R_IF, "if", true }, + { LEX_R_ELSE, "else", true }, + { LEX_R_DO, "do", true }, + { LEX_R_WHILE, "while", true }, + { LEX_R_FOR, "for", true }, + { LEX_R_IN, "in", true }, + { LEX_R_BREAK, "break", true }, + { LEX_R_CONTINUE, "continue", true }, + { LEX_R_FUNCTION, "function", true }, + { LEX_R_RETURN, "return", true }, + { LEX_R_VAR, "var", true }, + { LEX_R_LET, "let", true }, + { LEX_R_WITH, "with", true }, + { LEX_R_TRUE, "true", true }, + { LEX_R_FALSE, "false", true }, + { LEX_R_NULL, "null", true }, + { LEX_R_NEW, "new", true }, + { LEX_R_TRY, "try", true }, + { LEX_R_CATCH, "catch", true }, + { LEX_R_FINALLY, "finally", true }, + { LEX_R_THROW, "throw", true }, + { LEX_R_TYPEOF, "typeof", true }, + { LEX_R_VOID, "void", true }, + { LEX_R_DELETE, "delete", true }, + { LEX_R_INSTANCEOF, "instanceof", true }, + { LEX_R_SWITCH, "switch", true }, + { LEX_R_CASE, "case", true }, + { LEX_R_DEFAULT, "default", true }, +}; +#define ARRAY_LENGTH(array) (sizeof(array)/sizeof(array[0])) +#define ARRAY_END(array) (&array[ARRAY_LENGTH(array)]) +static token2str_t *reserved_words_end = ARRAY_END(reserved_words_begin);//&reserved_words_begin[ARRAY_LENGTH(reserved_words_begin)]; +static token2str_t *str2reserved_begin[sizeof(reserved_words_begin)/sizeof(reserved_words_begin[0])]; +static token2str_t **str2reserved_end = &str2reserved_begin[sizeof(str2reserved_begin)/sizeof(str2reserved_begin[0])]; +static token2str_t tokens2str_begin[] = { + { LEX_EOF, "EOF", false }, + { LEX_ID, "ID", true }, + { LEX_INT, "INT", true }, + { LEX_FLOAT, "FLOAT", true }, + { LEX_STR, "STRING", true }, + { LEX_REGEXP, "REGEXP", true }, + { LEX_EQUAL, "==", false }, + { LEX_TYPEEQUAL, "===", false }, + { LEX_NEQUAL, "!=", false }, + { LEX_NTYPEEQUAL, "!==", false }, + { LEX_LEQUAL, "<=", false }, + { LEX_LSHIFT, "<<", false }, + { LEX_LSHIFTEQUAL, "<<=", false }, + { LEX_GEQUAL, ">=", false }, + { LEX_RSHIFT, ">>", false }, + { LEX_RSHIFTEQUAL, ">>=", false }, + { LEX_RSHIFTU, ">>>", false }, + { LEX_RSHIFTUEQUAL, ">>>=", false }, + { LEX_PLUSEQUAL, "+=", false }, + { LEX_MINUSEQUAL, "-=", false }, + { LEX_PLUSPLUS, "++", false }, + { LEX_MINUSMINUS, "--", false }, + { LEX_ANDEQUAL, "&=", false }, + { LEX_ANDAND, "&&", false }, + { LEX_OREQUAL, "|=", false }, + { LEX_OROR, "||", false }, + { LEX_XOREQUAL, "^=", false }, + { LEX_ASTERISKEQUAL, "*=", false }, + { LEX_SLASHEQUAL, "/=", false }, + { LEX_PERCENTEQUAL, "%=", false }, + // special tokens + { LEX_T_FOR_IN, "for", true }, + { LEX_T_OF, "of", true }, + { LEX_T_FOR_EACH_IN, "for each", true }, + { LEX_T_FUNCTION_OPERATOR, "function", true }, + { LEX_T_GET, "get", true }, + { LEX_T_SET, "set", true }, + { LEX_T_SKIP, "LEX_SKIP", true }, + { LEX_T_DUMMY_LABEL, "LABEL", true }, + { LEX_T_LABEL, "LABEL", true }, + { LEX_T_LOOP_LABEL, "LEX_LOOP_LABEL", true }, + { LEX_T_OBJECT_LITERAL, "LEX_OBJECT_LITERAL", false }, + { LEX_T_DESTRUCTURING_VAR, "Destructuring Var", false }, +}; +static token2str_t *tokens2str_end = &tokens2str_begin[sizeof(tokens2str_begin)/sizeof(tokens2str_begin[0])]; +struct token2str_cmp_t { + bool operator()(const token2str_t &lhs, const token2str_t &rhs) { + return lhs.id < rhs.id; } + bool operator()(const token2str_t &lhs, int rhs) { + return lhs.id < rhs; + } + bool operator()(const token2str_t *lhs, const token2str_t *rhs) { + return strcmp(lhs->str, rhs->str)<0; + } + bool operator()(const token2str_t *lhs, const char *rhs) { + return strcmp(lhs->str, rhs)<0; + } +}; +static bool tokens2str_sort() { +// printf("tokens2str_sort called\n"); + sort(tokens2str_begin, tokens2str_end, token2str_cmp_t()); + sort(reserved_words_begin, reserved_words_end, token2str_cmp_t()); + for(unsigned int i=0; icurrentLine()), column(l->currentColumn()), token(l->tk), intData(0) +{ + if(token == LEX_INT || LEX_TOKEN_DATA_FLOAT(token)) { + CNumber number(l->tkStr); + if(number.isInfinity()) + token=LEX_ID, (tokenData=new CScriptTokenDataString("Infinity"))->ref(); + else if(number.isInt32()) + token=LEX_INT, intData=number.toInt32(); + else + token=LEX_FLOAT, floatData=new double(number.toDouble()); + } else if(LEX_TOKEN_DATA_STRING(token)) + (tokenData = new CScriptTokenDataString(l->tkStr))->ref(); + else if(LEX_TOKEN_DATA_FUNCTION(token)) + (tokenData = new CScriptTokenDataFnc)->ref(); if(Match>=0) l->match(Match, Alternate); else l->match(l->tk); } -CScriptToken::CScriptToken(uint16_t Tk, int IntData) : line(0), column(0), token(Tk) { - if (LEX_TOKEN_DATA_SIMPLE(token)) { +CScriptToken::CScriptToken(uint16_t Tk, int IntData) : line(0), column(0), token(Tk), intData(0) { + if (LEX_TOKEN_DATA_SIMPLE(token)) intData = IntData; - } else if (LEX_TOKEN_DATA_FUNCTION(token)) { - fncData = new CScriptTokenDataFnc; - fncData->ref(); - } else + else if (LEX_TOKEN_DATA_FUNCTION(token)) + (tokenData = new CScriptTokenDataFnc)->ref(); + else if (LEX_TOKEN_DATA_DESTRUCTURING_VAR(token)) + (tokenData = new CScriptTokenDataDestructuringVar)->ref(); + else if (LEX_TOKEN_DATA_OBJECT_LITERAL(token)) + (tokenData = new CScriptTokenDataObjectLiteral)->ref(); + else if (LEX_TOKEN_DATA_FORWARDER(token)) + (tokenData = new CScriptTokenDataForwards)->ref(); + else ASSERT(0); } - -CScriptToken::CScriptToken(uint16_t Tk, const string &TkStr) : line(0), column(0), token(Tk) { +CScriptToken::CScriptToken(uint16_t Tk, const string &TkStr) : line(0), column(0), token(Tk), intData(0) { ASSERT(LEX_TOKEN_DATA_STRING(token)); - stringData = new CScriptTokenDataString(TkStr); - stringData->ref(); + (tokenData = new CScriptTokenDataString(TkStr))->ref(); } CScriptToken &CScriptToken::operator =(const CScriptToken &Copy) @@ -708,108 +858,140 @@ CScriptToken &CScriptToken::operator =(const CScriptToken &Copy) token = Copy.token; if(LEX_TOKEN_DATA_FLOAT(token)) floatData = new double(*Copy.floatData); - else if(LEX_TOKEN_DATA_STRING(token)) { - stringData = Copy.stringData; - stringData->ref(); - } else if(LEX_TOKEN_DATA_FUNCTION(token)) { - fncData = Copy.fncData; - fncData->ref(); - } else + else if(!LEX_TOKEN_DATA_SIMPLE(token)) + (tokenData = Copy.tokenData)->ref(); + else intData = Copy.intData; return *this; } -string CScriptToken::getParsableString(string &indentString, int &newln, const string &indent) { - string OutString; - string nl = indent.size() ? "\n" : " "; - int last_newln = newln; - if(newln&2) OutString.append(nl); - if(newln&1) OutString.append(indentString); - newln = 0; - - if(LEX_TOKEN_DATA_STRING(token)) - OutString.append(String()).append(" "); - else if(LEX_TOKEN_DATA_FLOAT(token)) - OutString.append(float2string(Float())).append(" "); - else if(token == LEX_INT) - OutString.append(int2string(Int())).append(" "); - else if(LEX_TOKEN_DATA_FUNCTION(token)) { - OutString.append("function "); - if(token == LEX_R_FUNCTION) - OutString.append(Fnc().name); - OutString.append("("); - if(Fnc().arguments.size()) { - OutString.append(Fnc().arguments.front()); - for(STRING_VECTOR_it it=Fnc().arguments.begin()+1; it!=Fnc().arguments.end(); ++it) - OutString.append(", ").append(*it); - } - OutString.append(") "); - for(TOKEN_VECT_it it=Fnc().body.begin(); it != Fnc().body.end(); ++it) { - OutString.append(it->getParsableString(indentString, newln, indent)); - } - } else if(token == '{') { - OutString.append("{"); - indentString.append(indent); - newln = 1|2|4; - } else if(token == '}') { - if(last_newln==(1|2|4)) OutString = ""; - indentString.resize(indentString.size() - min(indentString.size(),indent.size())); - OutString.append("}"); - newln = 1|2; - } else { - OutString.append(CScriptLex::getTokenStr(token)); - if(token==';') newln=1|2; else OutString.append(" "); - } - return OutString; -} -string CScriptToken::getParsableString(const string indent) { - string indentString; - int newln = 0; - return getParsableString(indentString, newln, indent); +string CScriptToken::getParsableString(TOKEN_VECT &Tokens, const string &IndentString, const string &Indent) { + return getParsableString(Tokens.begin(), Tokens.end(), IndentString, Indent); } -void CScriptToken::print(string &indent ) -{ - if(LEX_TOKEN_DATA_STRING(token)) - printf("%s%s ", indent.c_str(), String().c_str()); - else if(LEX_TOKEN_DATA_FLOAT(token)) - printf("%s%f ", indent.c_str(), Float()); - else if(token == LEX_INT) - printf("%s%d ", indent.c_str(), Int()); - else if(LEX_TOKEN_DATA_FUNCTION(token)) { - if(token == LEX_R_FUNCTION) - printf("%sfunction %s(", indent.c_str(), Fnc().name.c_str()); - else - printf("%sfunction (", indent.c_str()); - if(Fnc().arguments.size()) { - printf("%s", Fnc().arguments.front().c_str()); - for(STRING_VECTOR_it it=Fnc().arguments.begin()+1; it!=Fnc().arguments.end(); ++it) - printf(",%s", it->c_str()); +string CScriptToken::getParsableString(TOKEN_VECT_it Begin, TOKEN_VECT_it End, const string &IndentString, const string &Indent) { + ostringstream destination; + string nl = Indent.size() ? "\n" : " "; + string my_indentString = IndentString; + bool add_nl=false, block_start=false, need_space=false; + int skip_collon = 0; + + for(TOKEN_VECT_it it=Begin; it != End; ++it) { + string OutString; + if(add_nl) OutString.append(nl).append(my_indentString); + bool old_block_start = block_start; + bool old_need_space = need_space; + add_nl = block_start = need_space =false; + if(it->token == LEX_T_LOOP_LABEL) { + // ignore BLIND_LABLE + } else if(it->token == LEX_STR) + OutString.append(getJSString(it->String())), need_space=true; + else if(LEX_TOKEN_DATA_STRING(it->token)) + OutString.append(it->String()), need_space=true; + else if(LEX_TOKEN_DATA_FLOAT(it->token)) + OutString.append(CNumber(it->Float()).toString()), need_space=true; + else if(it->token == LEX_INT) + OutString.append(CNumber(it->Int()).toString()), need_space=true; + else if(LEX_TOKEN_DATA_FUNCTION(it->token)) { + OutString.append("function "); + if(it->Fnc().name.size() ) + OutString.append(it->Fnc().name); + OutString.append(it->Fnc().getArgumentsString()); + OutString.append(getParsableString(it->Fnc().body, IndentString, Indent)); + if(it->Fnc().body.front().token != '{') { + OutString.append(";"); +// token; + } + } else if(LEX_TOKEN_DATA_DESTRUCTURING_VAR(it->token)) { + OutString.append(it->DestructuringVar().getParsableString()); + } else if(LEX_TOKEN_DATA_OBJECT_LITERAL(it->token)) { + OutString.append(it->Object().getParsableString()); + } else if(it->token == '{') { + OutString.append("{"); + my_indentString.append(Indent); + add_nl = block_start = true; + } else if(it->token == '}') { + my_indentString.resize(my_indentString.size() - min(my_indentString.size(),Indent.size())); + if(old_block_start) + OutString = "}"; + else + OutString = nl + my_indentString + "}"; + add_nl = true; + } else if(it->token == LEX_T_SKIP) { + // ignore SKIP-Token + } else if(it->token == LEX_T_FORWARD) { + // ignore Forwarder-Token + } else if(it->token == LEX_R_FOR) { + OutString.append(CScriptToken::getTokenStr(it->token)); + skip_collon=2; + } else { + OutString.append(CScriptToken::getTokenStr(it->token,&need_space)); + if(it->token==';') { + if(skip_collon) { --skip_collon; } + else add_nl=true; + } } - printf(")"); - for(TOKEN_VECT_it it=Fnc().body.begin(); it != Fnc().body.end(); ++it) - it->print (indent); - } else if(token == '{') { - printf("%s{\n", indent.c_str()); - indent += " "; - } else if(token == '}') { - indent.resize(indent.size()-3); - printf("}\n"); - } else { - printf("%s%s ", indent.c_str(), CScriptLex::getTokenStr(token).c_str()); - if(token==';') printf("\n"); + if(need_space && old_need_space) destination << " "; + destination << OutString; } + return destination.str(); + } + void CScriptToken::clear() { - if(LEX_TOKEN_DATA_STRING(token)) - stringData->unref(); - else if(LEX_TOKEN_DATA_FLOAT(token)) + if(LEX_TOKEN_DATA_FLOAT(token)) delete floatData; - else if(LEX_TOKEN_DATA_FUNCTION(token)) - fncData->unref(); + else if(!LEX_TOKEN_DATA_SIMPLE(token)) + tokenData->unref(); token = 0; } +string CScriptToken::getTokenStr( int token, bool *need_space/*=0*/ ) +{ + if(!tokens2str_sorted) tokens2str_sorted=tokens2str_sort(); + token2str_t *found = lower_bound(reserved_words_begin, reserved_words_end, token, token2str_cmp_t()); + if(found != reserved_words_end && found->id==token) { + if(need_space) *need_space=found->need_space; + return found->str; + } + found = lower_bound(tokens2str_begin, tokens2str_end, token, token2str_cmp_t()); + if(found != tokens2str_end && found->id==token) { + if(need_space) *need_space=found->need_space; + return found->str; + } + if(need_space) *need_space=false; + + if (token>32 && token<128) { + char buf[2] = " "; + buf[0] = (char)token; + return buf; + } + + ostringstream msg; + msg << "?[" << token << "]"; + return msg.str(); +} +const char *CScriptToken::isReservedWord(int Token) { + if(!tokens2str_sorted) tokens2str_sorted=tokens2str_sort(); + token2str_t *found = lower_bound(reserved_words_begin, reserved_words_end, Token, token2str_cmp_t()); + if(found != reserved_words_end && found->id==Token) { + return found->str; + } + return 0; +} +int CScriptToken::isReservedWord(const string &Str) { + const char *str = Str.c_str(); + if(!tokens2str_sorted) tokens2str_sorted=tokens2str_sort(); + token2str_t **found = lower_bound(str2reserved_begin, str2reserved_end, str, token2str_cmp_t()); + if(found != str2reserved_end && strcmp((*found)->str, str)==0) { + return (*found)->id; + } + return LEX_ID; +} + + +////////////////////////////////////////////////////////////////////////// +/// CScriptTokenizer +////////////////////////////////////////////////////////////////////////// -// ----------------------------------------------------------------------------------- CSCRIPTTOKENIZER CScriptTokenizer::CScriptTokenizer() : l(0), prevPos(&tokens) { } CScriptTokenizer::CScriptTokenizer(CScriptLex &Lexer) : l(0), prevPos(&tokens) { @@ -824,15 +1006,18 @@ void CScriptTokenizer::tokenizeCode(CScriptLex &Lexer) { l=&Lexer; tokens.clear(); tokenScopeStack.clear(); - vector blockStart(1, tokens.size()), marks; + vector blockStart(1, 0), marks; + pushForwarder(tokens, blockStart); STRING_VECTOR_t labels, loopLabels; if(l->tk == '�') { // special-Token at Start means the code begins not at Statement-Level l->match('�'); - tokenizeLiteral(tokens, blockStart, marks, labels, loopLabels, 0); + int State=0; + tokenizeLiteral(tokens, blockStart, marks, labels, loopLabels, 0, State); } else do { tokenizeStatement(tokens, blockStart, marks, labels, loopLabels, 0); } while (l->tk!=LEX_EOF); pushToken(tokens, LEX_EOF); // add LEX_EOF-Token + removeEmptyForwarder(tokens, blockStart, marks); TOKEN_VECT(tokens).swap(tokens);// tokens.shrink_to_fit(); pushTokenScope(tokens); currentFile = l->currentFile; @@ -867,10 +1052,10 @@ bool CScriptTokenizer::check(int ExpectedToken, int AlternateToken/*=-1*/) { if (currentToken!=ExpectedToken && currentToken!=AlternateToken) { ostringstream errorString; if(ExpectedToken == LEX_EOF) - errorString << "Got unexpected " << CScriptLex::getTokenStr(currentToken); + errorString << "Got unexpected " << CScriptToken::getTokenStr(currentToken); else { - errorString << "Got '" << CScriptLex::getTokenStr(currentToken) << "' expected '" << CScriptLex::getTokenStr(ExpectedToken) << "'"; - if(AlternateToken!=-1) errorString << " or '" << CScriptLex::getTokenStr(AlternateToken) << "'"; + errorString << "Got '" << CScriptToken::getTokenStr(currentToken) << "' expected '" << CScriptToken::getTokenStr(ExpectedToken) << "'"; + if(AlternateToken!=-1) errorString << " or '" << CScriptToken::getTokenStr(AlternateToken) << "'"; } throw new CScriptException(SyntaxError, errorString.str(), currentFile, currentLine(), currentColumn()); } @@ -904,16 +1089,21 @@ static void fix_BlockStarts_Marks(vector &BlockStart, vector &Marks, i } enum { - TOKENIZE_FLAGS_canLabel = 1<<0, - TOKENIZE_FLAGS_canBreak = 1<<1, - TOKENIZE_FLAGS_canContinue = 1<<2, - TOKENIZE_FLAGS_canReturn = 1<<3, - TOKENIZE_FLAGS_asStatement = 1<<4, - TOKENIZE_FLAGS_forFor = 1<<5, - TOKENIZE_FLAGS_isAccessor = 1<<6, - TOKENIZE_FLAGS_callForNew = 1<<7, + TOKENIZE_FLAGS_canLabel = 1<<0, + TOKENIZE_FLAGS_canBreak = 1<<1, + TOKENIZE_FLAGS_canContinue = 1<<2, + TOKENIZE_FLAGS_canReturn = 1<<3, + TOKENIZE_FLAGS_asStatement = 1<<4, + TOKENIZE_FLAGS_forFor = 1<<5, + TOKENIZE_FLAGS_isAccessor = 1<<6, + TOKENIZE_FLAGS_callForNew = 1<<7, + TOKENIZE_FLAGS_noBlockStart = 1<<8, + TOKENIZE_FLAGS_nestedObject = 1<<9, +}; +enum { + TOKENIZE_STATE_leftHand = 1<<0, + TOKENIZE_STATE_Destructuring = 1<<1, }; - void CScriptTokenizer::tokenizeCatch(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { Marks.push_back(pushToken(Tokens, LEX_R_CATCH)); // push Token & push tokenBeginIdx pushToken(Tokens, '('); @@ -963,35 +1153,38 @@ void CScriptTokenizer::tokenizeSwitch(TOKEN_VECT &Tokens, vector &BlockStar pushToken(Tokens, ')'); Marks.push_back(pushToken(Tokens, '{')); // push Token & push blockBeginIdx - BlockStart.push_back(Tokens.size()); // push Block-Start (one Token after '{') + pushForwarder(Tokens, BlockStart); + + vector::size_type MarksSize = Marks.size(); Flags |= TOKENIZE_FLAGS_canBreak; for(bool hasDefault=false;;) { if( l->tk == LEX_R_CASE || l->tk == LEX_R_DEFAULT) { if(l->tk == LEX_R_CASE) { Marks.push_back(pushToken(Tokens)); // push Token & push caseBeginIdx + Marks.push_back(pushToken(Tokens,CScriptToken(LEX_T_SKIP))); // skipper to skip case-expression tokenizeExpression(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); setTokenSkip(Tokens, Marks); } else { // default - if(hasDefault) throw new CScriptException(SyntaxError, "more than one switch default", l->currentFile, l->currentLine, l->currentColumn()); + Marks.push_back(pushToken(Tokens)); // push Token & push caseBeginIdx + if(hasDefault) throw new CScriptException(SyntaxError, "more than one switch default", l->currentFile, l->currentLine(), l->currentColumn()); hasDefault = true; - pushToken(Tokens); } Marks.push_back(pushToken(Tokens, ':')); while(l->tk != '}' && l->tk != LEX_R_CASE && l->tk != LEX_R_DEFAULT && l->tk != LEX_EOF ) tokenizeStatement(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); setTokenSkip(Tokens, Marks); - } else if(l->tk == '}') { + } else if(l->tk == '}') break; - } else - throw new CScriptException(SyntaxError, "invalid switch statement", l->currentFile, l->currentLine, l->currentColumn()); + else + throw new CScriptException(SyntaxError, "invalid switch statement", l->currentFile, l->currentLine(), l->currentColumn()); } + while(MarksSize < Marks.size()) setTokenSkip(Tokens, Marks); + removeEmptyForwarder(Tokens, BlockStart, Marks); // remove Forwarder if empty pushToken(Tokens, '}'); - BlockStart.pop_back(); - - setTokenSkip(Tokens, Marks); - setTokenSkip(Tokens, Marks); + setTokenSkip(Tokens, Marks); // switch-block + setTokenSkip(Tokens, Marks); // switch-statement } void CScriptTokenizer::tokenizeWith(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { @@ -1044,12 +1237,12 @@ void CScriptTokenizer::tokenizeWhile(TOKEN_VECT &Tokens, vector &BlockStart tokenizeStatement(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags | TOKENIZE_FLAGS_canBreak | TOKENIZE_FLAGS_canContinue); BlockStart.pop_back(); - setTokenSkip(Tokens, Marks); + setTokenSkip(Tokens, Marks); // statement // pop LEX_T_LOOP_LABEL PopLoopLabels(label_count, LoopLabels); - setTokenSkip(Tokens, Marks); + setTokenSkip(Tokens, Marks); // while } void CScriptTokenizer::tokenizeDo(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { Marks.push_back(pushToken(Tokens)); // push Token & push tokenBeginIdx @@ -1105,13 +1298,11 @@ void CScriptTokenizer::tokenizeIf(TOKEN_VECT &Tokens, vector &BlockStart, v void CScriptTokenizer::tokenizeFor(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { - const char *prev_pos=l->tokenStart; - const char *prev_line_start=l->currentLineStart; - int prev_line = l->currentLine; - bool for_in, for_each_in; + CScriptLex::POS prev_pos = l->pos; + bool for_in, for_of, for_each_in; l->match(LEX_R_FOR); if((for_in = for_each_in = (l->tk == LEX_ID && l->tkStr == "each"))) { - l->match(LEX_ID); + l->match(LEX_ID); // match "each" } if(!for_in) { l->match('('); @@ -1123,32 +1314,32 @@ void CScriptTokenizer::tokenizeFor(TOKEN_VECT &Tokens, vector &BlockStart, l->match(LEX_ID); if(l->tk == LEX_R_IN) for_in = true; + else if(l->tk == LEX_ID && l->tkStr == "of") + for_in = for_of = true; } } - l->reset(prev_pos, prev_line, prev_line_start); + l->reset(prev_pos); Marks.push_back(pushToken(Tokens)); // push Token & push tokenBeginIdx if(for_in) Tokens[Tokens.size()-1].token = for_each_in?LEX_T_FOR_EACH_IN:LEX_T_FOR_IN; - if(for_each_in) l->match(LEX_ID); + if(for_each_in) l->match(LEX_ID); // match "each" // inject & push LEX_T_LOOP_LABEL int label_count = PushLoopLabels(Tokens, &LoopLabels); - BlockStart.push_back(pushToken(Tokens, '(')+1); // set BlockStart - no forwarde for(let ... + pushToken(Tokens, '('); + pushForwarder(Tokens, BlockStart); if(for_in) { if(l->tk == LEX_R_VAR) { - l->match(LEX_R_VAR); - - int tokenInsertIdx = BlockStart.front(); - Tokens.insert(Tokens.begin()+tokenInsertIdx++, CScriptToken(LEX_R_VAR)); - Tokens.insert(Tokens.begin()+tokenInsertIdx++, CScriptToken(LEX_ID, l->tkStr)); - Tokens.insert(Tokens.begin()+tokenInsertIdx++, CScriptToken(';')); - fix_BlockStarts_Marks(BlockStart, Marks, BlockStart.front(), 3); + pushToken(Tokens, LEX_R_VAR); + Tokens[BlockStart.front()].Forwarder().vars.insert(l->tkStr); } else if(l->tk == LEX_R_LET) { pushToken(Tokens, LEX_R_LET); + Tokens[BlockStart.back()].Forwarder().lets.insert(l->tkStr); } pushToken(Tokens, LEX_ID); - pushToken(Tokens, LEX_R_IN); + if(for_of) l->tk = LEX_T_OF; // fake token + pushToken(Tokens, LEX_R_IN, LEX_T_OF); } else { if(l->tk == LEX_R_VAR) tokenizeVar(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags | TOKENIZE_FLAGS_forFor); @@ -1162,19 +1353,76 @@ void CScriptTokenizer::tokenizeFor(TOKEN_VECT &Tokens, vector &BlockStart, l->check(';'); // no automatic ;-injection pushToken(Tokens, ';'); } - if(l->tk != ')') tokenizeExpression(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); - BlockStart.pop_back(); // pop_back / "no forwarde for(let ... "-prevention + if(for_in || l->tk != ')') tokenizeExpression(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); pushToken(Tokens, ')'); BlockStart.push_back(Tokens.size()); // set a blockStart tokenizeStatement(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags | TOKENIZE_FLAGS_canBreak | TOKENIZE_FLAGS_canContinue); BlockStart.pop_back(); + removeEmptyForwarder(Tokens, BlockStart, Marks); + // pop LEX_T_LOOP_LABEL PopLoopLabels(label_count, LoopLabels); setTokenSkip(Tokens, Marks); } + +void CScriptTokenizer::_tokenizeDeconstructionVarObject(DESTRUCTURING_VARS_t &Vars, STRING_VECTOR_t &VarNames) { + l->match('{'); + while(l->tk != '}') { + CScriptLex::POS prev_pos = l->pos; + string Path = l->tkStr; + l->match(LEX_ID, LEX_STR); + if(l->tk == ':') { + l->match(':'); + _tokenizeDestructionVar(Vars, Path, VarNames); + } else { + l->reset(prev_pos); + VarNames.push_back(l->tkStr); + Vars.push_back(DESTRUCTURING_VAR_t(l->tkStr, l->tkStr)); + l->match(LEX_ID); + } + if (l->tk!='}') l->match(',', '}'); + } + l->match('}'); +} +void CScriptTokenizer::_tokenizeDeconstructionVarArray(DESTRUCTURING_VARS_t &Vars, STRING_VECTOR_t &VarNames) { + int idx = 0; + l->match('['); + while(l->tk != ']') { + if(l->tk == ',') + Vars.push_back(DESTRUCTURING_VAR_t("", "")); // empty + else + _tokenizeDestructionVar(Vars, int2string(idx), VarNames); + ++idx; + if (l->tk!=']') l->match(',',']'); + } + l->match(']'); +} +void CScriptTokenizer::_tokenizeDestructionVar(DESTRUCTURING_VARS_t &Vars, const string &Path, STRING_VECTOR_t &VarNames) { + if(l->tk == '[') { + Vars.push_back(DESTRUCTURING_VAR_t(Path, "[")); // marks array begin + _tokenizeDeconstructionVarArray(Vars, VarNames); + Vars.push_back(DESTRUCTURING_VAR_t("", "]")); // marks array end + } else if(l->tk == '{') { + Vars.push_back(DESTRUCTURING_VAR_t(Path, "{")); // marks object begin + _tokenizeDeconstructionVarObject(Vars, VarNames); + Vars.push_back(DESTRUCTURING_VAR_t("", "}")); // marks object end + } else { + VarNames.push_back(l->tkStr); + Vars.push_back(DESTRUCTURING_VAR_t(Path, l->tkStr)); + l->match(LEX_ID); + } +} +CScriptToken CScriptTokenizer::tokenizeDestructionVar(STRING_VECTOR_t &VarNames) { + CScriptToken token(LEX_T_DESTRUCTURING_VAR); + token.column = l->currentColumn(); + token.line = l->currentLine(); + _tokenizeDestructionVar(token.DestructuringVar().vars, "", VarNames); + return token; +} + void CScriptTokenizer::tokenizeFunction(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { bool forward = false; bool Statement = (Flags & TOKENIZE_FLAGS_asStatement) != 0; @@ -1190,60 +1438,80 @@ void CScriptTokenizer::tokenizeFunction(TOKEN_VECT &Tokens, vector &BlockSt if(!Statement) tk = LEX_T_FUNCTION_OPERATOR; } if(tk == LEX_R_FUNCTION) // only forward functions - forward = TOKEN_VECT::size_type(BlockStart.front()) != Tokens.size(); + forward = BlockStart.front() == BlockStart.back(); CScriptToken FncToken(tk); CScriptTokenDataFnc &FncData = FncToken.Fnc(); - if(l->tk == LEX_ID) { - FncData.name = l->tkStr; - l->match(LEX_ID); - } else { - //ASSERT(Statement == false); - } + if(l->tk == LEX_ID || Accessor) { + FncData.name = l->tkStr; + l->match(LEX_ID, LEX_STR); + } else if(Statement) + throw new CScriptException(SyntaxError, "Function statement requires a name.", l->currentFile, l->currentLine(), l->currentColumn()); l->match('('); while(l->tk != ')') { - FncData.arguments.push_back(l->tkStr); - l->match(LEX_ID); + if(l->tk == '[' || l->tk=='{') { + STRING_VECTOR_t names; + FncData.arguments.push_back(tokenizeDestructionVar(names)); + } else + pushToken(FncData.arguments, LEX_ID); if (l->tk!=')') l->match(',',')'); } - l->match(')'); + // l->match(')'); + // to allow regexp at the beginning of a lambda-function fake last token + l->tk = '{'; + l->match('{'); FncData.file = l->currentFile; - FncData.line = l->currentLine; + FncData.line = l->currentLine(); vector functionBlockStart, marks; STRING_VECTOR_t labels, loopLabels; - functionBlockStart.push_back(FncData.body.size()+1); - l->check('{'); - tokenizeBlock(FncData.body, functionBlockStart, marks, labels, loopLabels, TOKENIZE_FLAGS_canReturn); + if(l->tk == '{' || tk==LEX_T_GET || tk==LEX_T_SET) + tokenizeBlock(FncData.body, functionBlockStart, marks, labels, loopLabels, TOKENIZE_FLAGS_canReturn); + else { +// FncToken.token+=2; // SHORT-Version +// FncData.body.push_back(CScriptToken('{')); +// FncData.body.push_back(CScriptToken(LEX_R_RETURN)); + tokenizeExpression(FncData.body, functionBlockStart, marks, labels, loopLabels, 0); + l->match(';'); +// pushToken(FncData.body, ';'); +// FncData.body.push_back(CScriptToken(';')); +// FncData.body.push_back(CScriptToken('}')); + } if(forward) { - int tokenInsertIdx = BlockStart.back(); - Tokens.insert(Tokens.begin()+BlockStart.front(), FncToken); - fix_BlockStarts_Marks(BlockStart, Marks, tokenInsertIdx, 1); + Tokens[BlockStart.front()].Forwarder().functions.insert(FncToken); + FncToken.token = LEX_R_FUNCTION_PLACEHOLDER; } - else - Tokens.push_back(FncToken); + Tokens.push_back(FncToken); } void CScriptTokenizer::tokenizeLet(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { bool forFor = (Flags & TOKENIZE_FLAGS_forFor)!=0; - bool Statement = (Flags & TOKENIZE_FLAGS_asStatement)!=0; Flags &= ~(TOKENIZE_FLAGS_forFor | TOKENIZE_FLAGS_asStatement); - bool forward = !forFor && TOKEN_VECT::size_type(BlockStart.back()) != Tokens.size(); + bool Statement = (Flags & TOKENIZE_FLAGS_asStatement)!=0; + Flags &= ~(TOKENIZE_FLAGS_forFor | TOKENIZE_FLAGS_asStatement); bool expression=false; + int currLine = l->currentLine(), currColumn = l->currentColumn(); Marks.push_back(pushToken(Tokens)); // push Token & push BeginIdx if(!forFor && (l->tk == '(' || !Statement)) { expression = true; pushToken(Tokens, '('); + pushForwarder(Tokens, BlockStart); } STRING_VECTOR_t vars; for(;;) { - vars.push_back(l->tkStr); - pushToken(Tokens, LEX_ID); - if(l->tk=='=') { - pushToken(Tokens); + bool isDestruction = false; + if(l->tk == '[' || l->tk=='{') { + isDestruction = true; + Tokens.push_back(tokenizeDestructionVar(vars)); + } else { + vars.push_back(l->tkStr); + pushToken(Tokens, LEX_ID); + } + if(isDestruction || l->tk=='=') { + pushToken(Tokens, '='); tokenizeAssignment(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); } if(l->tk==',') @@ -1252,48 +1520,63 @@ void CScriptTokenizer::tokenizeLet(TOKEN_VECT &Tokens, vector &BlockStart, break; } if(expression) { + string redeclared = Tokens[BlockStart.back()].Forwarder().addLets(vars); + if(redeclared.size()) + throw new CScriptException(TypeError, "redeclaration of variable '"+redeclared+"'", l->currentFile, currLine, currColumn); pushToken(Tokens, ')'); - if(Statement) - tokenizeStatement(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); - setTokenSkip(Tokens, Marks); + if(Statement) { + if(l->tk == '{') // no extra BlockStart by expression + tokenizeBlock(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags|=TOKENIZE_FLAGS_noBlockStart); + else + tokenizeStatement(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + } else + tokenizeExpression(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + + // never remove Forwarder-token here -- popForwarder(Tokens, BlockStart, Marks); + Tokens[BlockStart.back()].Forwarder().vars_in_letscope.clear(); // only clear vars_in_letscope } else { if(!forFor) pushToken(Tokens, ';'); - setTokenSkip(Tokens, Marks); + string redeclared; + if(BlockStart.front() == BlockStart.back()) { + // Currently it is allowed in javascript, to redeclare "let"-declared vars + // in root- or function-scopes. In this case, "let" handled like "var" + // To prevent redeclaration in root- or function-scopes define PREVENT_REDECLARATION_IN_FUNCTION_SCOPES +#ifdef PREVENT_REDECLARATION_IN_FUNCTION_SCOPES + redeclared = Tokens[BlockStart.front()].Forwarder().addLets(vars); +#else + Tokens[BlockStart.front()].Forwarder().addVars(vars); +#endif + } else if(Tokens[BlockStart.back()].token == LEX_T_FORWARD){ + redeclared = Tokens[BlockStart.back()].Forwarder().addLets(vars); + } else + throw new CScriptException(SyntaxError, "let declaration not directly within block", l->currentFile, currLine, currColumn); + if(redeclared.size()) + throw new CScriptException(TypeError, "redeclaration of variable '"+redeclared+"'", l->currentFile, currLine, currColumn); + } + setTokenSkip(Tokens, Marks); +} - if(forward) // copy a let-deklaration at the begin of the last block - { - int tokenBeginIdx = BlockStart.back(), tokenInsertIdx = tokenBeginIdx; - - Tokens.insert(Tokens.begin()+tokenInsertIdx++, CScriptToken(LEX_R_VAR)); - Tokens.insert(Tokens.begin()+tokenInsertIdx++, CScriptToken(LEX_ID, vars.front())); - for(STRING_VECTOR_it it = vars.begin()+1; it != vars.end(); ++it) { - Tokens.insert(Tokens.begin()+tokenInsertIdx++, CScriptToken(',')); - Tokens.insert(Tokens.begin()+tokenInsertIdx++, CScriptToken(LEX_ID, *it)); - } - Tokens.insert(Tokens.begin()+tokenInsertIdx++, CScriptToken(';'));// insert at the begin var name; - Tokens[tokenBeginIdx].Int() = tokenInsertIdx-tokenBeginIdx; - - fix_BlockStarts_Marks(BlockStart, Marks, tokenBeginIdx, tokenInsertIdx-tokenBeginIdx); - } - } -} - -void CScriptTokenizer::tokenizeVar(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { - - bool forFor = (Flags & TOKENIZE_FLAGS_forFor)!=0; Flags &= ~TOKENIZE_FLAGS_forFor; - - bool forward = TOKEN_VECT::size_type(BlockStart.front()) != Tokens.size(); // forwarde only if the var-Statement not the first Statement of the Scope +void CScriptTokenizer::tokenizeVar(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { + bool forFor = (Flags & TOKENIZE_FLAGS_forFor)!=0; + Flags &= ~TOKENIZE_FLAGS_forFor; + int currLine = l->currentLine(), currColumn = l->currentColumn(); Marks.push_back(pushToken(Tokens)); // push Token & push BeginIdx STRING_VECTOR_t vars; for(;;) { - vars.push_back(l->tkStr); - pushToken(Tokens, LEX_ID); - if(l->tk=='=') { - pushToken(Tokens); + bool isDestruction = false; + if(l->tk == '[' || l->tk=='{') { + isDestruction = true; + Tokens.push_back(tokenizeDestructionVar(vars)); + } else { + vars.push_back(l->tkStr); + pushToken(Tokens, LEX_ID); + } + if(isDestruction || l->tk=='=') { + pushToken(Tokens, '='); tokenizeAssignment(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); } if(l->tk==',') @@ -1305,75 +1588,204 @@ void CScriptTokenizer::tokenizeVar(TOKEN_VECT &Tokens, vector &BlockStart, setTokenSkip(Tokens, Marks); - if(forward) // copy a var-deklaration at the begin of the scope - { - int tokenBeginIdx = BlockStart.front(), tokenInsertIdx = tokenBeginIdx; + Tokens[BlockStart.front()].Forwarder().addVars(vars); + string redeclared; + if(BlockStart.front() != BlockStart.back()) { + if(Tokens[BlockStart.back()].token == LEX_T_FORWARD) + redeclared = Tokens[BlockStart.back()].Forwarder().addVarsInLetscope(vars); + } +#ifdef PREVENT_REDECLARATION_IN_FUNCTION_SCOPES + else { + redeclared = Tokens[BlockStart.front()].Forwarder().addVarsInLetscope(vars); + } +#endif + if(redeclared.size()) + throw new CScriptException(TypeError, "redeclaration of variable '"+redeclared+"'", l->currentFile, currLine, currColumn); +} - Tokens.insert(Tokens.begin()+tokenInsertIdx++, CScriptToken(LEX_R_VAR)); - Tokens.insert(Tokens.begin()+tokenInsertIdx++, CScriptToken(LEX_ID, vars.front())); - for(STRING_VECTOR_it it = vars.begin()+1; it != vars.end(); ++it) { - Tokens.insert(Tokens.begin()+tokenInsertIdx++, CScriptToken(',')); - Tokens.insert(Tokens.begin()+tokenInsertIdx++, CScriptToken(LEX_ID, *it)); - } - Tokens.insert(Tokens.begin()+tokenInsertIdx++, CScriptToken(';'));// insert at the begin var name; - Tokens[tokenBeginIdx].Int() = tokenInsertIdx-tokenBeginIdx; +void CScriptTokenizer::_tokenizeLiteralObject(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags, int &State) { + bool nestedObject = (Flags & TOKENIZE_FLAGS_nestedObject) != 0; Flags &= ~TOKENIZE_FLAGS_nestedObject; + CScriptToken ObjectToken(LEX_T_OBJECT_LITERAL); + CScriptTokenDataObjectLiteral &Objc = ObjectToken.Object(); + + Objc.type = CScriptTokenDataObjectLiteral::OBJECT; + Objc.destructuring = Objc.structuring = true; - fix_BlockStarts_Marks(BlockStart, Marks, tokenBeginIdx, tokenInsertIdx-tokenBeginIdx); + string msg, msgFile; + int msgLine=0, msgColumn=0; + + l->match('{'); + while (l->tk != '}') { + CScriptTokenDataObjectLiteral::ELEMENT element; + bool assign = false; + if(l->tk == LEX_ID) { + element.id = l->tkStr; + CScriptToken Token(l, LEX_ID); + if((l->tk==LEX_ID || l->tk==LEX_STR ) && (element.id=="get" || element.id=="set")) { + element.id = l->tkStr; + element.value.push_back(Token); + tokenizeFunction(element.value, BlockStart, Marks, Labels, LoopLabels, Flags|TOKENIZE_FLAGS_isAccessor); + Objc.destructuring = false; + } else { + if(Objc.destructuring && (l->tk == ',' || l->tk == '}')) { + if(!msg.size()) { + Objc.structuring = false; + msg.append("Got '").append(CScriptToken::getTokenStr(l->tk)).append("' expected ':'"); + msgFile = l->currentFile; + msgLine = l->currentLine(); + msgColumn = l->currentColumn(); + ; + } + element.value.push_back(Token); + } else + assign = true; + } + } else if(l->tk == LEX_INT) { + element.id = int2string((int32_t)strtol(l->tkStr.c_str(),0,0)); + l->match(LEX_INT); + assign = true; + } else if(l->tk == LEX_FLOAT) { + element.id = float2string(strtod(l->tkStr.c_str(),0)); + l->match(LEX_FLOAT); + assign = true; + } else if(LEX_TOKEN_DATA_STRING(l->tk) && l->tk != LEX_REGEXP) { + element.id = l->tkStr; + l->match(l->tk); + assign = true; + } else + l->match(LEX_ID, LEX_STR); + if(assign) { + l->match(':'); + int dFlags = Flags | (l->tk == '{' || l->tk == '[') ? TOKENIZE_FLAGS_nestedObject : 0; + int dState = TOKENIZE_STATE_Destructuring; + tokenizeAssignment(element.value, BlockStart, Marks, Labels, LoopLabels, dFlags, dState); + if(Objc.destructuring) Objc.destructuring = dState == (TOKENIZE_STATE_leftHand | TOKENIZE_STATE_Destructuring); + } + + if(!Objc.destructuring && msg.size()) + throw new CScriptException(SyntaxError, msg, msgFile, msgLine, msgColumn); + Objc.elements.push_back(element); + if (l->tk != '}') l->match(',', '}'); + } + l->match('}'); + if(Objc.destructuring && Objc.structuring) { + if(nestedObject) { + if(l->tk!=',' && l->tk!='}' && l->tk!='=') + Objc.destructuring = false; + } + else + Objc.setMode(l->tk=='='); + } else if(!Objc.destructuring && msg.size()) + throw new CScriptException(SyntaxError, msg, msgFile, msgLine, msgColumn); + + if(Objc.destructuring) + State |= TOKENIZE_STATE_leftHand | TOKENIZE_STATE_Destructuring; + Tokens.push_back(ObjectToken); +} +void CScriptTokenizer::_tokenizeLiteralArray(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags, int &State) { + bool nestedObject = (Flags & TOKENIZE_FLAGS_nestedObject) != 0; Flags &= ~TOKENIZE_FLAGS_nestedObject; + CScriptToken ObjectToken(LEX_T_OBJECT_LITERAL); + CScriptTokenDataObjectLiteral &Objc = ObjectToken.Object(); + + Objc.type = CScriptTokenDataObjectLiteral::ARRAY; + Objc.destructuring = Objc.structuring = true; + int idx = 0; + + l->match('['); + while (l->tk != ']') { + CScriptTokenDataObjectLiteral::ELEMENT element; + element.id = int2string(idx++); + if(l->tk != ',') { + int dFlags = Flags | (l->tk == '{' || l->tk == '[') ? TOKENIZE_FLAGS_nestedObject : 0; + int dState = TOKENIZE_STATE_Destructuring; + tokenizeAssignment(element.value, BlockStart, Marks, Labels, LoopLabels, dFlags, dState); + if(Objc.destructuring) Objc.destructuring = dState == (TOKENIZE_STATE_leftHand | TOKENIZE_STATE_Destructuring); + } + Objc.elements.push_back(element); + if (l->tk != ']') l->match(',', ']'); + } + l->match(']'); + if(Objc.destructuring && Objc.structuring) { + if(nestedObject) { + if(l->tk!=',' && l->tk!=']' && l->tk!='=') + Objc.destructuring = false; + } + else + Objc.setMode(l->tk=='='); } + if(Objc.destructuring) + State |= TOKENIZE_STATE_leftHand | TOKENIZE_STATE_Destructuring; + Tokens.push_back(ObjectToken); } -void CScriptTokenizer::tokenizeLiteral(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { +void CScriptTokenizer::tokenizeObjectLiteral(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags, int &State) { +//--> begin testing + CScriptLex::POS pos = l->pos; + _tokenizeLiteralObject(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags, State); + l->reset(pos); + Tokens.pop_back(); + Flags &= ~TOKENIZE_FLAGS_nestedObject; +//--> end testing + + Marks.push_back(pushToken(Tokens)); // push Token & push BeginIdx + while (l->tk != '}') { + if(l->tk == LEX_ID) { + string id = l->tkStr; + pushToken(Tokens); + if((l->tk==LEX_ID || l->tk==LEX_STR ) && (id=="get" || id=="set")) + tokenizeFunction(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags|TOKENIZE_FLAGS_isAccessor); + else { + pushToken(Tokens, ':'); + tokenizeAssignment(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + } + } else if(l->tk == LEX_STR) { + pushToken(Tokens); + pushToken(Tokens, ':'); + tokenizeAssignment(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + } + if (l->tk != '}') pushToken(Tokens, ',', '}'); + } + pushToken(Tokens); + setTokenSkip(Tokens, Marks); +} +void CScriptTokenizer::tokenizeLiteral(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags, int &State) { + State &= ~TOKENIZE_STATE_leftHand; + bool canLabel = Flags & TOKENIZE_FLAGS_canLabel; + Flags &= ~TOKENIZE_FLAGS_canLabel; switch(l->tk) { case LEX_ID: { string label = l->tkStr; pushToken(Tokens); - if(l->tk==':' && Flags & TOKENIZE_FLAGS_canLabel) { + if(l->tk==':' && canLabel) { if(find(Labels.begin(), Labels.end(), label) != Labels.end()) - throw new CScriptException(SyntaxError, "dublicate label '"+label+"'", l->currentFile, l->currentLine, l->currentColumn()-label.size()); + throw new CScriptException(SyntaxError, "dublicate label '"+label+"'", l->currentFile, l->currentLine(), l->currentColumn()-label.size()); Tokens[Tokens.size()-1].token = LEX_T_LABEL; // change LEX_ID to LEX_T_LABEL Labels.push_back(label); - } + } else if(label=="this") { + if( l->tk == '=' || (l->tk >= LEX_ASSIGNMENTS_BEGIN && l->tk <= LEX_ASSIGNMENTS_END) ) + throw new CScriptException(SyntaxError, "invalid assignment left-hand side", l->currentFile, l->currentLine(), l->currentColumn()-label.size()); + if( l->tk==LEX_PLUSPLUS || l->tk==LEX_MINUSMINUS ) + throw new CScriptException(SyntaxError, l->tk==LEX_PLUSPLUS?"invalid increment operand":"invalid decrement operand", l->currentFile, l->currentLine(), l->currentColumn()-label.size()); + } else + State |= TOKENIZE_STATE_leftHand; } break; case LEX_INT: case LEX_FLOAT: case LEX_STR: + case LEX_REGEXP: case LEX_R_TRUE: case LEX_R_FALSE: case LEX_R_NULL: pushToken(Tokens); break; case '{': - Marks.push_back(pushToken(Tokens)); // push Token & push BeginIdx - while (l->tk != '}') { - if(l->tk == LEX_STR) { - string id = l->tkStr; - pushToken(Tokens); - if(l->tk==LEX_ID && (id=="get" || id=="set")) - tokenizeFunction(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); - else { - pushToken(Tokens, ':'); - tokenizeAssignment(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); - } - } else if(l->tk == LEX_ID) { - pushToken(Tokens); - pushToken(Tokens, ':'); - tokenizeAssignment(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); - } - if (l->tk != '}') pushToken(Tokens, ',', '}'); - } - pushToken(Tokens); - setTokenSkip(Tokens, Marks); + _tokenizeLiteralObject(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags, State); +// tokenizeObjectLiteral(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags, State); break; case '[': - Marks.push_back(pushToken(Tokens)); // push Token & push BeginIdx - while (l->tk != ']') { - tokenizeAssignment(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); - if (l->tk != ']') pushToken(Tokens, ',', ']'); - } - pushToken(Tokens); - setTokenSkip(Tokens, Marks); + _tokenizeLiteralArray(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags, State); break; case LEX_R_LET: // let as expression tokenizeLet(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); @@ -1383,7 +1795,10 @@ void CScriptTokenizer::tokenizeLiteral(TOKEN_VECT &Tokens, vector &BlockSta break; case LEX_R_NEW: Marks.push_back(pushToken(Tokens)); // push Token & push BeginIdx - tokenizeFunctionCall(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags | TOKENIZE_FLAGS_callForNew); + { + int State; + tokenizeFunctionCall(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags | TOKENIZE_FLAGS_callForNew, State); + } setTokenSkip(Tokens, Marks); break; case '(': @@ -1396,36 +1811,39 @@ void CScriptTokenizer::tokenizeLiteral(TOKEN_VECT &Tokens, vector &BlockSta l->check(LEX_EOF); } } -void CScriptTokenizer::tokenizeMember(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { +void CScriptTokenizer::tokenizeMember(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags, int &State) { while(l->tk == '.' || l->tk == '[') { if(l->tk == '.') { pushToken(Tokens); pushToken(Tokens , LEX_ID); } else { - pushToken(Tokens); + Marks.push_back(pushToken(Tokens)); tokenizeExpression(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); pushToken(Tokens, ']'); + setTokenSkip(Tokens, Marks); } + State |= TOKENIZE_STATE_leftHand; } } -void CScriptTokenizer::tokenizeFunctionCall(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { +void CScriptTokenizer::tokenizeFunctionCall(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags, int &State) { bool for_new = (Flags & TOKENIZE_FLAGS_callForNew)!=0; Flags &= ~TOKENIZE_FLAGS_callForNew; - tokenizeLiteral(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); - tokenizeMember(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + tokenizeLiteral(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags, State); + tokenizeMember(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags, State); while(l->tk == '(') { + State &= ~TOKENIZE_STATE_leftHand; Marks.push_back(pushToken(Tokens)); // push Token & push BeginIdx while(l->tk!=')') { tokenizeAssignment(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); - if (l->tk!=')') pushToken(Tokens, ',', ')' ); + if (l->tk!=')') pushToken(Tokens, ',', ')'); } pushToken(Tokens); setTokenSkip(Tokens, Marks); if(for_new) break; - tokenizeMember(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + tokenizeMember(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags, State); } } -void CScriptTokenizer::tokenizeSubExpression(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { +void CScriptTokenizer::tokenizeSubExpression(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags, int &State) { static int Left2Right_begin[] = { /* Precedence 5 */ '*', '/', '%', /* Precedence 6 */ '+', '-', @@ -1438,7 +1856,7 @@ void CScriptTokenizer::tokenizeSubExpression(TOKEN_VECT &Tokens, vector &Bl static int *Left2Right_end = &Left2Right_begin[sizeof(Left2Right_begin)/sizeof(Left2Right_begin[0])]; static bool Left2Right_sorted = false; if(!Left2Right_sorted) Left2Right_sorted = (sort(Left2Right_begin, Left2Right_end), true); - + bool noLeftHand = false; for(;;) { bool right2left_end = false; while(!right2left_end) { @@ -1451,41 +1869,62 @@ void CScriptTokenizer::tokenizeSubExpression(TOKEN_VECT &Tokens, vector &Bl case LEX_R_VOID: case LEX_R_DELETE: Flags &= ~TOKENIZE_FLAGS_canLabel; + noLeftHand = true; pushToken(Tokens); // Precedence 3 break; case LEX_PLUSPLUS: // pre-increment case LEX_MINUSMINUS: // pre-decrement - Flags &= ~TOKENIZE_FLAGS_canLabel; - pushToken(Tokens); // Precedence 4 + { + int tk = l->tk; + Flags &= ~TOKENIZE_FLAGS_canLabel; + noLeftHand = true; + pushToken(Tokens); // Precedence 4 + if(l->tk == LEX_ID && l->tkStr == "this") + throw new CScriptException(SyntaxError, tk==LEX_PLUSPLUS?"invalid increment operand":"invalid decrement operand", l->currentFile, l->currentLine(), l->currentColumn()); + } default: right2left_end = true; } } - tokenizeFunctionCall(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + tokenizeFunctionCall(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags, State); if (!l->lineBreakBeforeToken && (l->tk==LEX_PLUSPLUS || l->tk==LEX_MINUSMINUS)) { // post-in-/de-crement + noLeftHand = true;; pushToken(Tokens); // Precedence 4 } int *found = lower_bound(Left2Right_begin, Left2Right_end, l->tk); - if(found != Left2Right_end && *found == l->tk) + if(found != Left2Right_end && *found == l->tk) { + noLeftHand = true; pushToken(Tokens); // Precedence 5-14 + } else break; } + if(noLeftHand) State &= ~TOKENIZE_STATE_leftHand; } -void CScriptTokenizer::tokenizeCondition(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { - tokenizeSubExpression(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); +void CScriptTokenizer::tokenizeCondition(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags, int &State) { + tokenizeSubExpression(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags, State); if(l->tk == '?') { pushToken(Tokens); - tokenizeCondition(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + tokenizeCondition(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags, State); pushToken(Tokens, ':'); - tokenizeCondition(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + tokenizeCondition(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags, State); + State &= ~TOKENIZE_STATE_leftHand; } } void CScriptTokenizer::tokenizeAssignment(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { - tokenizeCondition(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + int State=0; + tokenizeAssignment(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags, State); +} +void CScriptTokenizer::tokenizeAssignment(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags, int &State) { + State &= ~TOKENIZE_STATE_leftHand; + tokenizeCondition(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags, State); if (l->tk=='=' || (l->tk>=LEX_ASSIGNMENTS_BEGIN && l->tk<=LEX_ASSIGNMENTS_END) ) { + State &= ~TOKENIZE_STATE_Destructuring; + if( !(State & TOKENIZE_STATE_leftHand) ) { + throw new CScriptException(ReferenceError, "invalid assignment left-hand side", l->currentFile, l->currentLine(), l->currentColumn()); + } pushToken(Tokens); tokenizeAssignment(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); } @@ -1498,14 +1937,16 @@ void CScriptTokenizer::tokenizeExpression(TOKEN_VECT &Tokens, vector &Block } } void CScriptTokenizer::tokenizeBlock(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { + bool addBlockStart = (Flags&TOKENIZE_FLAGS_noBlockStart)==0; + Flags&=~TOKENIZE_FLAGS_noBlockStart; Marks.push_back(pushToken(Tokens, '{')); // push Token & push BeginIdx - BlockStart.push_back(Tokens.size()); // push Block-Start (one Token after '{') + if(addBlockStart) pushForwarder(Tokens, BlockStart); while(l->tk != '}' && l->tk != LEX_EOF) tokenizeStatement(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); pushToken(Tokens, '}'); - BlockStart.pop_back(); // clean-up BlockStarts + if(addBlockStart) removeEmptyForwarder(Tokens, BlockStart, Marks); // clean-up BlockStarts setTokenSkip(Tokens, Marks); } @@ -1528,7 +1969,7 @@ void CScriptTokenizer::tokenizeStatement(TOKEN_VECT &Tokens, vector &BlockS case LEX_R_TRY: tokenizeTry(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); break; case LEX_R_RETURN: if( (Flags & TOKENIZE_FLAGS_canReturn)==0) - throw new CScriptException(SyntaxError, "'return' statement, but not in a function.", l->currentFile, l->currentLine, l->currentColumn()); + throw new CScriptException(SyntaxError, "'return' statement, but not in a function.", l->currentFile, l->currentLine(), l->currentColumn()); case LEX_R_THROW: Marks.push_back(pushToken(Tokens)); // push Token & push BeginIdx if(l->tk != ';' && !l->lineBreakBeforeToken) { @@ -1546,12 +1987,12 @@ void CScriptTokenizer::tokenizeStatement(TOKEN_VECT &Tokens, vector &BlockS l->check(LEX_ID); STRING_VECTOR_t &L = isBreak ? Labels : LoopLabels; if(find(L.begin(), L.end(), l->tkStr) == L.end()) - throw new CScriptException(SyntaxError, "label '"+l->tkStr+"' not found", l->currentFile, l->currentLine, l->currentColumn()); + throw new CScriptException(SyntaxError, "label '"+l->tkStr+"' not found", l->currentFile, l->currentLine(), l->currentColumn()); pushToken(Tokens); // push 'Label' } else if((Flags & (isBreak ? TOKENIZE_FLAGS_canBreak : TOKENIZE_FLAGS_canContinue) )==0) throw new CScriptException(SyntaxError, isBreak ? "'break' must be inside loop or switch" : "'continue' must be inside loop", - l->currentFile, l->currentLine, l->currentColumn()); + l->currentFile, l->currentLine(), l->currentColumn()); pushToken(Tokens, ';'); // push ';' setTokenSkip(Tokens, Marks); } @@ -1579,44 +2020,47 @@ int CScriptTokenizer::pushToken(TOKEN_VECT &Tokens, int Match, int Alternate) { Tokens.push_back(CScriptToken(l, Match, Alternate)); return Tokens.size()-1; } - -void CScriptTokenizer::throwTokenNotExpected() { - throw new CScriptException(SyntaxError, "'"+CScriptLex::getTokenStr(l->tk)+"' was not expected", l->currentFile, l->currentLine, l->currentColumn()); +int CScriptTokenizer::pushToken(TOKEN_VECT &Tokens, const CScriptToken &Token) { + int ret = Tokens.size(); + Tokens.push_back(Token); + return ret; } - -// ----------------------------------------------------------------------------------- CSCRIPTVARLINK - -CScriptVarLink::CScriptVarLink(const CScriptVarPtr &Var, const string &Name /*=TINYJS_TEMP_NAME*/, int Flags /*=SCRIPTVARLINK_DEFAULT*/) - : name(Name), owner(0), flags(Flags&~SCRIPTVARLINK_OWNED) { -#if DEBUG_MEMORY - mark_allocated(this); -#endif - var = Var; +CScriptTokenDataForwards &CScriptTokenizer::pushForwarder(TOKEN_VECT &Tokens, std::vector &BlockStarts) { + BlockStarts.push_back(Tokens.size()); + Tokens.push_back(CScriptToken(LEX_T_FORWARD)); + return Tokens.back().Forwarder(); } - -CScriptVarLink::CScriptVarLink(const CScriptVarLink &link) - : name(link.name), owner(0), flags(SCRIPTVARLINK_DEFAULT) { - // Copy constructor -#if DEBUG_MEMORY - mark_allocated(this); -#endif - var = link.var; +void CScriptTokenizer::throwTokenNotExpected() { + throw new CScriptException(SyntaxError, "'"+CScriptToken::getTokenStr(l->tk)+"' was not expected", l->currentFile, l->currentLine(), l->currentColumn()); } -CScriptVarLink::~CScriptVarLink() { -#if DEBUG_MEMORY - mark_deallocated(this); -#endif +void CScriptTokenizer::removeEmptyForwarder( TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks ) +{ + CScriptTokenDataForwards &forwarder = Tokens[BlockStart.back()].Forwarder(); + forwarder.vars_in_letscope.clear(); + if(forwarder.vars.empty() && forwarder.lets.empty() && forwarder.functions.empty()) { + Tokens.erase(Tokens.begin()+BlockStart.back()); + fix_BlockStarts_Marks(BlockStart, Marks, BlockStart.back(), -1); + } + BlockStart.pop_back(); } -void CScriptVarLink::replaceWith(const CScriptVarPtr &newVar) { - if(!newVar) ASSERT(0);//newVar = new CScriptVar(); - var = newVar; +CScriptTokenDataForwards &CScriptTokenizer::__getForwarder( TOKEN_VECT &Tokens, int Pos, vector &BlockStart, vector &Marks ) +{ + if( (Tokens.begin()+Pos)->token != LEX_T_FORWARD) { + Tokens.insert(Tokens.begin()+Pos, CScriptToken(LEX_T_FORWARD)); + fix_BlockStarts_Marks(BlockStart, Marks, Pos+1, 1); + } + return (Tokens.begin()+Pos)->Forwarder(); } -// ----------------------------------------------------------------------------------- CSCRIPTVAR + +////////////////////////////////////////////////////////////////////////// +/// CScriptVar +////////////////////////////////////////////////////////////////////////// CScriptVar::CScriptVar(CTinyJS *Context, const CScriptVarPtr &Prototype) { + extensible = true; context = Context; temporaryID = 0; if(context->first) { @@ -1635,6 +2079,7 @@ CScriptVar::CScriptVar(CTinyJS *Context, const CScriptVarPtr &Prototype) { #endif } CScriptVar::CScriptVar(const CScriptVar &Copy) { + extensible = Copy.extensible; context = Copy.context; temporaryID = 0; if(context->first) { @@ -1648,7 +2093,7 @@ CScriptVar::CScriptVar(const CScriptVar &Copy) { refs = 0; SCRIPTVAR_CHILDS_cit it; for(it = Copy.Childs.begin(); it!= Copy.Childs.end(); ++it) { - addChild((*it)->getName(), (*it), (*it)->getFlags()); + addChild((*it)->getName(), (*it)->getVarPtr(), (*it)->getFlags()); } #if DEBUG_MEMORY @@ -1659,6 +2104,8 @@ CScriptVar::~CScriptVar(void) { #if DEBUG_MEMORY mark_deallocated(this); #endif + for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it) + (*it)->setOwner(0); removeAllChildren(); if(prev) prev->next = next; @@ -1673,6 +2120,7 @@ CScriptVar::~CScriptVar(void) { bool CScriptVar::isObject() {return false;} bool CScriptVar::isError() {return false;} bool CScriptVar::isArray() {return false;} +bool CScriptVar::isRegExp() {return false;} bool CScriptVar::isAccessor() {return false;} bool CScriptVar::isNull() {return false;} bool CScriptVar::isUndefined() {return false;} @@ -1682,41 +2130,63 @@ bool CScriptVar::isInt() {return false;} bool CScriptVar::isBool() {return false;} int CScriptVar::isInfinity() { return 0; } ///< +1==POSITIVE_INFINITY, -1==NEGATIVE_INFINITY, 0==is not an InfinityVar bool CScriptVar::isDouble() {return false;} +bool CScriptVar::isRealNumber() {return false;} bool CScriptVar::isNumber() {return false;} -bool CScriptVar::isNumeric() {return false;} -bool CScriptVar::isPrimitive() {return true;} +bool CScriptVar::isPrimitive() {return false;} bool CScriptVar::isFunction() {return false;} bool CScriptVar::isNative() {return false;} +bool CScriptVar::isBounded() {return false;} - +////////////////////////////////////////////////////////////////////////// /// Value -int CScriptVar::getInt() {return 0;} -bool CScriptVar::getBool() {return false;} -double CScriptVar::getDouble() {return 0.0;} -CScriptTokenDataFnc *CScriptVar::getFunctionData() { return 0; } -string CScriptVar::getString() {return "";} +////////////////////////////////////////////////////////////////////////// -string CScriptVar::getParsableString(const string &indentString, const string &indent) { - return getString(); +CScriptVarPrimitivePtr CScriptVar::getRawPrimitive() { + return CScriptVarPrimitivePtr(); // default NULL-Ptr } +CScriptVarPrimitivePtr CScriptVar::toPrimitive() { + return toPrimitive_hintNumber(); +} + +CScriptVarPrimitivePtr CScriptVar::toPrimitive(bool &execute) { + return toPrimitive_hintNumber(execute); +} -CScriptVarPtr CScriptVar::getNumericVar() { - return constScriptVar(NaN); +CScriptVarPrimitivePtr CScriptVar::toPrimitive_hintString(int32_t radix) { + bool execute=true; + CScriptVarPrimitivePtr var = toPrimitive_hintString(execute, radix); + if(!execute) throw (CScriptVarPtr)context->removeExeptionVar(); + return var; +} +CScriptVarPrimitivePtr CScriptVar::toPrimitive_hintString(bool &execute, int32_t radix) { + if(execute) { + if(!isPrimitive()) { + CScriptVarPtr ret = callJS_toString(execute, radix); + if(execute && !ret->isPrimitive()) { + ret = callJS_valueOf(execute); + if(execute && !ret->isPrimitive()) + context->throwError(execute, TypeError, "can't convert b to primitive type"); + } + return ret; + } + return this; + } + return constScriptVar(Undefined); } -CScriptVarPtr CScriptVar::getPrimitivVar() { +CScriptVarPrimitivePtr CScriptVar::toPrimitive_hintNumber() { bool execute=true; - CScriptVarPtr var = getPrimitivVar(execute); -// if(!execute) TODO + CScriptVarPrimitivePtr var = toPrimitive_hintNumber(execute); + if(!execute) throw (CScriptVarPtr)context->removeExeptionVar(); return var; } -CScriptVarPtr CScriptVar::getPrimitivVar(bool execute) { +CScriptVarPrimitivePtr CScriptVar::toPrimitive_hintNumber(bool &execute) { if(execute) { if(!isPrimitive()) { - CScriptVarPtr ret = valueOf(execute); + CScriptVarPtr ret = callJS_valueOf(execute); if(execute && !ret->isPrimitive()) { - ret = toString(execute); + ret = callJS_toString(execute); if(execute && !ret->isPrimitive()) - do{}while(0); // TODO error can't convert in primitive type + context->throwError(execute, TypeError, "can't convert to primitive type"); } return ret; } @@ -1725,63 +2195,206 @@ CScriptVarPtr CScriptVar::getPrimitivVar(bool execute) { return constScriptVar(Undefined); } -CScriptVarPtr CScriptVar::valueOf(bool execute) { +CScriptVarPtr CScriptVar::callJS_valueOf(bool &execute) { if(execute) { - CScriptVarPtr FncValueOf = findChildWithPrototypeChain("valueOf"); + CScriptVarPtr FncValueOf = findChildWithPrototypeChain("valueOf").getter(execute); if(FncValueOf != context->objectPrototype_valueOf) { // custom valueOf in JavaScript - vector Params; - return context->callFunction(execute, FncValueOf, Params, this); - } else { - return _valueOf(execute); - } + if(FncValueOf->isFunction()) { // no Error if toString not callable + vector Params; + return context->callFunction(execute, FncValueOf, Params, this); + } + } else + return valueOf_CallBack(); } return this; } -CScriptVarPtr CScriptVar::_valueOf(bool execute) { +CScriptVarPtr CScriptVar::valueOf_CallBack() { return this; } -CScriptVarPtr CScriptVar::toString(bool execute, int radix/*=0*/) { +CScriptVarPtr CScriptVar::callJS_toString(bool &execute, int radix/*=0*/) { if(execute) { - CScriptVarPtr FncToString = findChildWithPrototypeChain("toString"); + CScriptVarPtr FncToString = findChildWithPrototypeChain("toString").getter(execute); if(FncToString != context->objectPrototype_toString) { // custom valueOf in JavaScript - vector Params; - Params.push_back(newScriptVar(radix)); - return context->callFunction(execute, FncToString, Params, this); - } else { - return _toString(execute, radix); - } + if(FncToString->isFunction()) { // no Error if toString not callable + vector Params; + Params.push_back(newScriptVar(radix)); + return context->callFunction(execute, FncToString, Params, this); + } + } else + return toString_CallBack(execute, radix); } return this; } -CScriptVarPtr CScriptVar::_toString(bool execute, int radix/*=0*/) { +CScriptVarPtr CScriptVar::toString_CallBack(bool &execute, int radix/*=0*/) { return this; } +CNumber CScriptVar::toNumber() { return toPrimitive_hintNumber()->toNumber_Callback(); }; +CNumber CScriptVar::toNumber(bool &execute) { return toPrimitive_hintNumber(execute)->toNumber_Callback(); }; +bool CScriptVar::toBoolean() { return true; } +string CScriptVar::toString(int32_t radix) { return toPrimitive_hintString(radix)->toCString(radix); } +string CScriptVar::toString(bool &execute, int32_t radix) { return toPrimitive_hintString(execute, radix)->toCString(radix); } + +int CScriptVar::getInt() { return toNumber().toInt32(); } +double CScriptVar::getDouble() { return toNumber().toDouble(); } +bool CScriptVar::getBool() { return toBoolean(); } +string CScriptVar::getString() { return toPrimitive_hintString()->toCString(); } + +CScriptTokenDataFnc *CScriptVar::getFunctionData() { return 0; } + +string CScriptVar::getParsableString(const string &indentString, const string &indent, uint32_t uniqueID, bool &hasRecursion) { + getParsableStringRecursionsCheck(); + return toString(); +} + +CScriptVarPtr CScriptVar::getNumericVar() { return newScriptVar(toNumber()); } + +////// Flags + +void CScriptVar::seal() { + preventExtensions(); + for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it) + (*it)->setConfigurable(false); +} +bool CScriptVar::isSealed() const { + if(isExtensible()) return false; + for(SCRIPTVAR_CHILDS_cit it = Childs.begin(); it != Childs.end(); ++it) + if((*it)->isConfigurable()) return false; + return true; +} +void CScriptVar::freeze() { + preventExtensions(); + for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it) + (*it)->setConfigurable(false), (*it)->setWritable(false); +} +bool CScriptVar::isFrozen() const { + if(isExtensible()) return false; + for(SCRIPTVAR_CHILDS_cit it = Childs.begin(); it != Childs.end(); ++it) + if((*it)->isConfigurable() || (*it)->isWritable()) return false; + return true; +} + ////// Childs -/// find -static bool compare_child_name(CScriptVarLink *Link, const string &Name) { - return Link->getName() < Name; +CScriptVarPtr CScriptVar::getOwnPropertyDescriptor(const string &Name) { + CScriptVarLinkPtr child = findChild(Name); + if(!child) { + CScriptVarStringPtr strVar = getRawPrimitive(); + uint32_t Idx; + if (strVar && (Idx=isArrayIndex(Name))!=uint32_t(-1) && IdxstringLength()) { + int Char = strVar->getChar(Idx); + CScriptVarPtr ret = newScriptVar(Object); + ret->addChild("value", newScriptVar(string(1, (char)Char))); + ret->addChild("writable", constScriptVar(false)); + ret->addChild("enumerable", constScriptVar(true)); + ret->addChild("configurable", constScriptVar(false)); + return ret; + } + } + + if(!child || child->getVarPtr()->isUndefined()) return constScriptVar(Undefined); + CScriptVarPtr ret = newScriptVar(Object); + if(child->getVarPtr()->isAccessor()) { + CScriptVarLinkPtr value = child->getVarPtr()->findChild(TINYJS_ACCESSOR_GET_VAR); + ret->addChild("get", value ? value->getVarPtr() : constScriptVar(Undefined)); + value = child->getVarPtr()->findChild(TINYJS_ACCESSOR_SET_VAR); + ret->addChild("set", value ? value->getVarPtr() : constScriptVar(Undefined)); + } else { + ret->addChild("value", child->getVarPtr()->valueOf_CallBack()); + ret->addChild("writable", constScriptVar(child->isWritable())); + } + ret->addChild("enumerable", constScriptVar(child->isEnumerable())); + ret->addChild("configurable", constScriptVar(child->isConfigurable())); + return ret; +} +const char *CScriptVar::defineProperty(const string &Name, CScriptVarPtr Attributes) { + CScriptVarPtr attr; + CScriptVarLinkPtr child = findChildWithStringChars(Name); + + CScriptVarPtr attr_value = Attributes->findChild("value"); + CScriptVarPtr attr_writable = Attributes->findChild("writable"); + CScriptVarPtr attr_get = Attributes->findChild("get"); + CScriptVarPtr attr_set = Attributes->findChild("set"); + CScriptVarPtr attr_enumerable = Attributes->findChild("enumerable"); + CScriptVarPtr attr_configurable = Attributes->findChild("configurable"); + bool attr_isDataDescriptor = !attr_get && !attr_set; + if(!attr_isDataDescriptor && (attr_value || attr_writable)) return "property descriptors must not specify a value or be writable when a getter or setter has been specified"; + if(attr_isDataDescriptor) { + if(attr_get && (!attr_get->isUndefined() || !attr_get->isFunction())) return "property descriptor's getter field is neither undefined nor a function"; + if(attr_set && (!attr_set->isUndefined() || !attr_set->isFunction())) return "property descriptor's setter field is neither undefined nor a function"; + } + if(!child) { + if(!isExtensible()) return "is not extensible"; + if(attr_isDataDescriptor) { + child = addChild(Name, attr_value?attr_value:constScriptVar(Undefined), 0); + if(attr_writable) child->setWritable(attr_writable->toBoolean()); + } else { + child = addChild(Name, newScriptVarAccessor(context, attr_get, attr_set), SCRIPTVARLINK_WRITABLE); + } + } else { + if(!child->isConfigurable()) { + if(attr_configurable && attr_configurable->toBoolean()) goto cant_redefine; + if(attr_enumerable && attr_enumerable->toBoolean() != child->isEnumerable()) goto cant_redefine; + if(child->getVarPtr()->isAccessor()) { + if(attr_isDataDescriptor) goto cant_redefine; + if(attr_get && attr_get != child->getVarPtr()->findChild(TINYJS_ACCESSOR_GET_VAR)) goto cant_redefine; + if(attr_set && attr_set != child->getVarPtr()->findChild(TINYJS_ACCESSOR_SET_VAR)) goto cant_redefine; + } else if(!attr_isDataDescriptor) goto cant_redefine; + else if(!child->isWritable()) { + if(attr_writable && attr_writable->toBoolean()) goto cant_redefine; + if(attr_value && !attr_value->mathsOp(child, LEX_EQUAL)->toBoolean()) goto cant_redefine; + } + } + if(attr_isDataDescriptor) { + if(child->getVarPtr()->isAccessor()) child->setWritable(false); + child->setVarPtr(attr_value?attr_value:constScriptVar(Undefined)); + if(attr_writable) child->setWritable(attr_writable->toBoolean()); + } else { + if(child->getVarPtr()->isAccessor()) { + if(!attr_get) attr_get = child->getVarPtr()->findChild(TINYJS_ACCESSOR_GET_VAR); + if(!attr_set) attr_set = child->getVarPtr()->findChild(TINYJS_ACCESSOR_SET_VAR); + } + child->setVarPtr(newScriptVarAccessor(context, attr_get, attr_set)); + child->setWritable(true); + } + } + if(attr_enumerable) child->setEnumerable(attr_enumerable->toBoolean()); + if(attr_configurable) child->setConfigurable(attr_configurable->toBoolean()); + return 0; +cant_redefine: + return "can't redefine non-configurable property"; } -CScriptVarLink *CScriptVar::findChild(const string &childName) { +CScriptVarLinkPtr CScriptVar::findChild(const string &childName) { if(Childs.empty()) return 0; - SCRIPTVAR_CHILDS_it it = lower_bound(Childs.begin(), Childs.end(), - childName.c_str(), - compare_child_name); + SCRIPTVAR_CHILDS_it it = lower_bound(Childs.begin(), Childs.end(), childName); if(it != Childs.end() && (*it)->getName() == childName) return *it; return 0; } -CScriptVarLink *CScriptVar::findChildInPrototypeChain(const string &childName) { +CScriptVarLinkWorkPtr CScriptVar::findChildWithStringChars(const string &childName) { + CScriptVarLinkWorkPtr child = findChild(childName); + if(child) return child; + CScriptVarStringPtr strVar = getRawPrimitive(); + uint32_t Idx; + if (strVar && (Idx=isArrayIndex(childName))!=uint32_t(-1) && IdxstringLength()) { + int Char = strVar->getChar(Idx); + child(newScriptVar(string(1, (char)Char)), childName, SCRIPTVARLINK_ENUMERABLE); + child.setReferencedOwner(this); // fake referenced Owner + return child; + } + return 0; +} + +CScriptVarLinkPtr CScriptVar::findChildInPrototypeChain(const string &childName) { unsigned int uniqueID = context->getUniqueID(); // Look for links to actual parent classes CScriptVarPtr object = this; - CScriptVarLink *__proto__; + CScriptVarLinkPtr __proto__; while( object->getTempraryID() != uniqueID && (__proto__ = object->findChild(TINYJS___PROTO___VAR)) ) { - CScriptVarLink *implementation = (*__proto__)->findChild(childName); + CScriptVarLinkPtr implementation = __proto__->getVarPtr()->findChild(childName); if (implementation) return implementation; object->setTemporaryID(uniqueID); // prevents recursions object = __proto__; @@ -1789,65 +2402,64 @@ CScriptVarLink *CScriptVar::findChildInPrototypeChain(const string &childName) { return 0; } -CScriptVarLink *CScriptVar::findChildWithPrototypeChain(const string &childName) { - unsigned int uniqueID = context->getUniqueID(); - CScriptVarPtr object = this; - while( object && object->getTempraryID() != uniqueID) { - CScriptVarLink *implementation = object->findChild(childName); - if (implementation) return implementation; - object->setTemporaryID(uniqueID); // prevents recursions - object = object->findChild(TINYJS___PROTO___VAR); +CScriptVarLinkWorkPtr CScriptVar::findChildWithPrototypeChain(const string &childName) { + CScriptVarLinkWorkPtr child = findChildWithStringChars(childName); + if(child) return child; + child = findChildInPrototypeChain(childName); + if(child) { + child(child->getVarPtr(), child->getName(), child->getFlags()); // recreate implementation + child.setReferencedOwner(this); // fake referenced Owner } - return 0; + return child; } -CScriptVarLink *CScriptVar::findChildByPath(const string &path) { +CScriptVarLinkPtr CScriptVar::findChildByPath(const string &path) { string::size_type p = path.find('.'); - CScriptVarLink *child; + CScriptVarLinkPtr child; if (p == string::npos) return findChild(path); if( (child = findChild(path.substr(0,p))) ) - (*child)->findChildByPath(path.substr(p+1)); + return child->getVarPtr()->findChildByPath(path.substr(p+1)); return 0; } -CScriptVarLink *CScriptVar::findChildOrCreate(const string &childName/*, int varFlags*/) { - CScriptVarLink *l = findChild(childName); +CScriptVarLinkPtr CScriptVar::findChildOrCreate(const string &childName/*, int varFlags*/) { + CScriptVarLinkPtr l = findChild(childName); if (l) return l; return addChild(childName, constScriptVar(Undefined)); // return addChild(childName, new CScriptVar(context, TINYJS_BLANK_DATA, varFlags)); } -CScriptVarLink *CScriptVar::findChildOrCreateByPath(const string &path) { +CScriptVarLinkPtr CScriptVar::findChildOrCreateByPath(const string &path) { string::size_type p = path.find('.'); if (p == string::npos) return findChildOrCreate(path); string childName(path, 0, p); - CScriptVarLink *l = findChild(childName); + CScriptVarLinkPtr l = findChild(childName); if (!l) l = addChild(childName, newScriptVar(Object)); - return (*l)->findChildOrCreateByPath(path.substr(p+1)); + return l->getVarPtr()->findChildOrCreateByPath(path.substr(p+1)); } -void CScriptVar::keys(std::set &Keys, bool OnlyEnumerable/*=true*/, uint32_t ID/*=0*/) +void CScriptVar::keys(set &Keys, bool OnlyEnumerable/*=true*/, uint32_t ID/*=0*/) { setTemporaryID(ID); for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it) { if(!OnlyEnumerable || (*it)->isEnumerable()) Keys.insert((*it)->getName()); } - CScriptVarLink *__proto__ = 0; - if( ID && (__proto__ = findChild(TINYJS___PROTO___VAR)) && (*__proto__)->getTempraryID() != ID ) - (*__proto__)->keys(Keys, OnlyEnumerable, ID); + CScriptVarLinkPtr __proto__; + if( ID && (__proto__ = findChild(TINYJS___PROTO___VAR)) && __proto__->getVarPtr()->getTempraryID() != ID ) + __proto__->getVarPtr()->keys(Keys, OnlyEnumerable, ID); } /// add & remove -CScriptVarLink *CScriptVar::addChild(const string &childName, const CScriptVarPtr &child, int linkFlags /*= SCRIPTVARLINK_DEFAULT*/) { -CScriptVarLink *link = 0; - SCRIPTVAR_CHILDS_it it = lower_bound(Childs.begin(), Childs.end(), childName, compare_child_name); +CScriptVarLinkPtr CScriptVar::addChild(const string &childName, const CScriptVarPtr &child, int linkFlags /*= SCRIPTVARLINK_DEFAULT*/) { + CScriptVarLinkPtr link; + SCRIPTVAR_CHILDS_it it = lower_bound(Childs.begin(), Childs.end(), childName); if(it == Childs.end() || (*it)->getName() != childName) { - link = new CScriptVarLink(child?child:constScriptVar(Undefined), childName, linkFlags); + link = CScriptVarLinkPtr(child?child:constScriptVar(Undefined), childName, linkFlags); link->setOwner(this); - link->setOwned(true); - Childs.insert(it, link); + + Childs.insert(it, 1, link); #ifdef _DEBUG } else { ASSERT(0); // addChild - the child exists @@ -1855,29 +2467,25 @@ CScriptVarLink *link = 0; } return link; } -CScriptVarLink *CScriptVar::addChildNoDup(const string &childName, const CScriptVarPtr &child, int linkFlags /*= SCRIPTVARLINK_DEFAULT*/) { - SCRIPTVAR_CHILDS_it it = lower_bound(Childs.begin(), Childs.end(), childName, compare_child_name); +CScriptVarLinkPtr CScriptVar::addChildNoDup(const string &childName, const CScriptVarPtr &child, int linkFlags /*= SCRIPTVARLINK_DEFAULT*/) { + return addChildOrReplace(childName, child, linkFlags); +} +CScriptVarLinkPtr CScriptVar::addChildOrReplace(const string &childName, const CScriptVarPtr &child, int linkFlags /*= SCRIPTVARLINK_DEFAULT*/) { + SCRIPTVAR_CHILDS_it it = lower_bound(Childs.begin(), Childs.end(), childName); if(it == Childs.end() || (*it)->getName() != childName) { - CScriptVarLink *link = new CScriptVarLink(child, childName, linkFlags); + CScriptVarLinkPtr link(child, childName, linkFlags); link->setOwner(this); - link->setOwned(true); - Childs.insert(it, link); + Childs.insert(it, 1, link); return link; } else { - (*it)->replaceWith(child); + (*it)->setVarPtr(child); return (*it); } } -bool CScriptVar::removeLink(CScriptVarLinkPtr &Link) { - CScriptVarLink *link = Link.getRealLink(); - Link.clear(); - return removeLink(link); -} - -bool CScriptVar::removeLink(CScriptVarLink *&link) { +bool CScriptVar::removeLink(CScriptVarLinkPtr &link) { if (!link) return false; - SCRIPTVAR_CHILDS_it it = lower_bound(Childs.begin(), Childs.end(), link->getName(), compare_child_name); + SCRIPTVAR_CHILDS_it it = lower_bound(Childs.begin(), Childs.end(), link->getName()); if(it != Childs.end() && (*it) == link) { Childs.erase(it); #ifdef _DEBUG @@ -1885,50 +2493,33 @@ bool CScriptVar::removeLink(CScriptVarLink *&link) { ASSERT(0); // removeLink - the link is not atached to this var #endif } - delete link; - link = 0; + link.clear(); return true; } void CScriptVar::removeAllChildren() { - SCRIPTVAR_CHILDS_it it; - for(it = Childs.begin(); it!= Childs.end(); ++it) { - delete *it; - } Childs.clear(); } -CScriptVarPtr CScriptVar::getArrayIndex(int idx) { - CScriptVarLink *link = findChild(int2string(idx)); +CScriptVarPtr CScriptVar::getArrayIndex(uint32_t idx) { + CScriptVarLinkPtr link = findChild(int2string(idx)); if (link) return link; else return constScriptVar(Undefined); // undefined } -void CScriptVar::setArrayIndex(int idx, const CScriptVarPtr &value) { +void CScriptVar::setArrayIndex(uint32_t idx, const CScriptVarPtr &value) { string sIdx = int2string(idx); - CScriptVarLink *link = findChild(sIdx); + CScriptVarLinkPtr link = findChild(sIdx); if (link) { - if (value->isUndefined()) - removeLink(link); - else - link->replaceWith(value); + link->setVarPtr(value); } else { - if (!value->isUndefined()) - addChild(sIdx, value); + addChild(sIdx, value); } } -int CScriptVar::getArrayLength() { - int highest = -1; - if (!isArray()) return 0; - - for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it) { - if (::isNumber((*it)->getName())) { - int val = atoi((*it)->getName().c_str()); - if (val > highest) highest = val; - } - } - return highest+1; +uint32_t CScriptVar::getArrayLength() { + if (!isArray() || Childs.size()==0) return 0; + return isArrayIndex(Childs.back()->getName())+1; } CScriptVarPtr CScriptVar::mathsOp(const CScriptVarPtr &b, int op) { @@ -1947,17 +2538,17 @@ void CScriptVar::trace(string &indentStr, uint32_t uniqueID, const string &name) if(temporaryID == uniqueID) extra = " recursion detected"; TRACE("%s'%s' = '%s' %s%s\n", - indentStr.c_str(), - name.c_str(), - getString().c_str(), - getFlagsAsString().c_str(), - extra); + indentStr.c_str(), + name.c_str(), + toString().c_str(), + getFlagsAsString().c_str(), + extra); if(temporaryID != uniqueID) { temporaryID = uniqueID; indentStr+=indent; for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it) { if((*it)->isEnumerable()) - (*(*it))->trace(indentStr, uniqueID, (*it)->getName()); + (*it)->getVarPtr()->trace(indentStr, uniqueID, (*it)->getName()); } indentStr = indentStr.substr(0, indentStr.length()-2); } @@ -1973,49 +2564,12 @@ string CScriptVar::getFlagsAsString() { if (isInt()) flagstr = flagstr + "INTEGER "; if (isBool()) flagstr = flagstr + "BOOLEAN "; if (isString()) flagstr = flagstr + "STRING "; + if (isRegExp()) flagstr = flagstr + "REGEXP "; if (isNaN()) flagstr = flagstr + "NaN "; if (isInfinity()) flagstr = flagstr + "INFINITY "; return flagstr; } -#ifdef TODO -void CScriptVar::getJSON(ostringstream &destination, const string linePrefix) { - if (isObject()) { - string indentedLinePrefix = linePrefix+" "; - // children - handle with bracketed list - destination << "{ \n"; - int count = Childs.size(); - for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it) { - destination << indentedLinePrefix; - if (isAlphaNum((*it)->getName())) - destination << (*it)->getName(); - else - destination << getJSString((*it)->getName()); - destination << " : "; - (*(*it))->getJSON(destination, indentedLinePrefix); - if (--count) destination << ",\n"; - } - destination << "\n" << linePrefix << "}"; - } else if (isArray()) { - string indentedLinePrefix = linePrefix+" "; - destination << "[\n"; - int len = getArrayLength(); - if (len>10000) len=10000; // we don't want to get stuck here! - - for (int i=0;igetJSON(destination, indentedLinePrefix); - if (isetTemporaryID_recursive(ID); + (*it)->getVarPtr()->setTemporaryID_recursive(ID); } } } + ////////////////////////////////////////////////////////////////////////// -/// CScriptVarLinkPtr +/// CScriptVarLink ////////////////////////////////////////////////////////////////////////// -CScriptVarLinkPtr &CScriptVarLinkPtr::operator()(CScriptVarLink *Link) { - bool execute = true; - (*this)(execute, Link); - return *this; -} -CScriptVarLinkPtr &CScriptVarLinkPtr::operator()(bool &execute, CScriptVarLink *Link) { - ASSERT(!Link || Link->owner); // no add not Owned Links - link = Link; - if(link && (*link)->isAccessor()) { - CScriptVarLink *getter = (*link)->findChild(TINYJS_ACCESSOR_GET_VAR); - if(getter) { - std::vector Params; - ASSERT(link->owner); - set_tmp_link((*getter)->getContext()->callFunction(execute, **getter, Params, link->owner)); - } else - set_tmp_link((*link)->constScriptVar(Undefined)); - }else - clear_tmp_link(); - return *this; +CScriptVarLink::CScriptVarLink(const CScriptVarPtr &Var, const string &Name /*=TINYJS_TEMP_NAME*/, int Flags /*=SCRIPTVARLINK_DEFAULT*/) + : name(Name), owner(0), flags(Flags), refs(0) { +#if DEBUG_MEMORY + mark_allocated(this); +#endif + var = Var; } -void CScriptVarLinkPtr::replaceVar(bool &execute, const CScriptVarPtr &Var) { - if(link && (*link)->isAccessor()) { - CScriptVarLink *setter = (*link)->findChild(TINYJS_ACCESSOR_SET_VAR); - if(setter) { - std::vector Params; - Params.push_back(Var); - bool execute; - ASSERT(link->owner); - (*setter)->getContext()->callFunction(execute, **setter, Params, link->owner); - } - } else - getLink()->var = Var; +CScriptVarLink::~CScriptVarLink() { +#if DEBUG_MEMORY + mark_deallocated(this); +#endif } -void CScriptVarLinkPtr::swap(CScriptVarLinkPtr &Link){ - CScriptVarLink *_link = link; link = Link.link; Link.link = _link; - CScriptVarLinkTmpPtr _tmp_link = tmp_link; tmp_link = Link.tmp_link; Link.tmp_link = _tmp_link; +CScriptVarLink *CScriptVarLink::ref() { + refs++; + return this; } - -void CScriptVarLinkPtr::set_tmp_link(const CScriptVarPtr &Var, const std::string &Name /*= TINYJS_TEMP_NAME*/, int Flags /*= SCRIPTVARLINK_DEFAULT*/) { - // refs()==1 makes more like a pointer - // if refs==1 then only this LinkPtr owns the tmp_link - // otherwise we creates a new one - if(tmp_link.refs()==1) { - tmp_link->name = Name; - tmp_link->owner = 0; - tmp_link->flags = Flags&~SCRIPTVARLINK_OWNED; - tmp_link->var = Var; - } else if(tmp_link.refs()==0) - tmp_link = CScriptVarLinkTmpPtr(Var, Name, Flags); - else - tmp_link = CScriptVarLinkTmpPtr(Var, Name, Flags); +void CScriptVarLink::unref() { + refs--; + ASSERT(refs>=0); // printf("OMFG, we have unreffed too far!\n"); + if (refs==0) + delete this; } -void CScriptVarLinkPtr::clear_tmp_link(){ - if(tmp_link.refs()==1) - tmp_link->var.clear(); - else if(tmp_link.refs()>1) - tmp_link = CScriptVarLinkTmpPtr(); -} -////////////////////////////////////////////////////////////////////////// -/// CScriptVarObject +////////////////////////////////////////////////////////////////////////// +/// CScriptVarLinkPtr ////////////////////////////////////////////////////////////////////////// -declare_dummy_t(Object); -CScriptVarObject::CScriptVarObject(CTinyJS *Context) : CScriptVar(Context, Context->objectPrototype) { } -CScriptVarObject::~CScriptVarObject() {} -CScriptVarPtr CScriptVarObject::clone() { return new CScriptVarObject(*this); } -bool CScriptVarObject::isObject() { return true; } -bool CScriptVarObject::isPrimitive() { return false; } +CScriptVarLinkPtr & CScriptVarLinkPtr::operator()( const CScriptVarPtr &var, const std::string &name /*= TINYJS_TEMP_NAME*/, int flags /*= SCRIPTVARLINK_DEFAULT*/ ) { + if(link && link->refs == 1) { // the link is only refered by this + link->name = name; + link->owner = 0; + link->flags = flags; + link->var = var; + } else { + if(link) link->unref(); + link = (new CScriptVarLink(var, name, flags))->ref(); + } + return *this; +} -int CScriptVarObject::getInt() { return getPrimitivVar()->getInt(); } -bool CScriptVarObject::getBool() { return getPrimitivVar()->getBool(); } -double CScriptVarObject::getDouble() { return getPrimitivVar()->getDouble(); } -std::string CScriptVarObject::getString() { return getPrimitivVar()->getString(); } +CScriptVarLinkWorkPtr CScriptVarLinkPtr::getter() { + return CScriptVarLinkWorkPtr(*this).getter(); +} -string CScriptVarObject::getParsableString(const string &indentString, const string &indent) { - ostringstream destination; - string nl = indent.size() ? "\n" : " "; +CScriptVarLinkWorkPtr CScriptVarLinkPtr::getter( bool &execute ) { + return CScriptVarLinkWorkPtr(*this).getter(execute); +} - destination << "{"; - if(Childs.size()) { - string new_indentString = indentString + indent; - int count = 0; - destination << nl; - for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it) { - if((*it)->isEnumerable()) { - if (count++) destination << "," << nl; - if (isAlphaNum((*it)->getName())) - destination << new_indentString << (*it)->getName(); - else - destination << new_indentString << "\"" << getJSString((*it)->getName()) << "\""; - destination << " : "; - destination << (*(*it))->getParsableString(new_indentString, indent); - } - } - destination << nl << indentString; - } - destination << "}"; - return destination.str(); +CScriptVarLinkWorkPtr CScriptVarLinkPtr::setter( const CScriptVarPtr &Var ) { + return CScriptVarLinkWorkPtr(*this).setter(Var); } -string CScriptVarObject::getVarType() { return "object"; } -CScriptVarPtr CScriptVarObject::_toString(bool execute, int radix) { return newScriptVar("[ Object ]"); }; -////////////////////////////////////////////////////////////////////////// -/// CScriptVarObjectWrap -////////////////////////////////////////////////////////////////////////// +CScriptVarLinkWorkPtr CScriptVarLinkPtr::setter( bool &execute, const CScriptVarPtr &Var ) { + return CScriptVarLinkWorkPtr(*this).setter(execute, Var); +} + +bool CScriptVarLinkPtr::operator <(const string &rhs) const { + uint32_t lhs_int = isArrayIndex(link->getName()); + uint32_t rhs_int = isArrayIndex(rhs); + if(lhs_int==uint32_t(-1)) { + if(rhs_int==uint32_t(-1)) + return link->getName() < rhs; + else + return true; + } else if(rhs_int==uint32_t(-1)) + return false; + return lhs_int < rhs_int; +} -declare_dummy_t(ObjectWrap); -CScriptVarObjectWrap::CScriptVarObjectWrap(CTinyJS *Context, const CScriptVarPtr &Value) : CScriptVarObject(Context), value(Value) { - ASSERT(value); - CScriptVarLink *proto = Value->findChild(TINYJS___PROTO___VAR); - this->addChildNoDup(TINYJS___PROTO___VAR, proto, SCRIPTVARLINK_WRITABLE); +////////////////////////////////////////////////////////////////////////// +/// CScriptVarLinkWorkPtr +////////////////////////////////////////////////////////////////////////// + +CScriptVarLinkWorkPtr CScriptVarLinkWorkPtr::getter() { + if(link && link->getVarPtr()) { + bool execute=true; + CScriptVarPtr ret = getter(execute); + if(!execute) throw link->getVarPtr()->getContext()->removeExeptionVar(); + return ret; + } + return *this; } -CScriptVarObjectWrap::~CScriptVarObjectWrap(){} -CScriptVarPtr CScriptVarObjectWrap::clone() { return new CScriptVarObjectWrap(*this); } -CScriptVarPtr CScriptVarObjectWrap::_valueOf(bool execute) { return value; } -CScriptVarPtr CScriptVarObjectWrap::_toString(bool execute, int radix/*=0*/) { return value; } -std::string CScriptVarObjectWrap::getParsableString( const std::string &indentString, const std::string &indent ) { - return value->getParsableString(indentString, indent); +CScriptVarLinkWorkPtr CScriptVarLinkWorkPtr::getter(bool &execute) { + if(execute && link && link->getVarPtr() && link->getVarPtr()->isAccessor()) { + const CScriptVarPtr &var = link->getVarPtr(); + CScriptVarLinkPtr getter = var->findChild(TINYJS_ACCESSOR_GET_VAR); + if(getter) { + vector Params; + ASSERT(getReferencedOwner()); + return getter->getVarPtr()->getContext()->callFunction(execute, getter->getVarPtr(), Params, getReferencedOwner()); + } else + return var->constScriptVar(Undefined); + } else + return *this; +} +CScriptVarLinkWorkPtr CScriptVarLinkWorkPtr::setter( const CScriptVarPtr &Var ) { + if(link && link->getVarPtr()) { + bool execute=true; + CScriptVarPtr ret = setter(execute, Var); + if(!execute) throw link->getVarPtr()->getContext()->removeExeptionVar(); + return ret; + } + return *this; } -void CScriptVarObjectWrap::setTemporaryID_recursive(uint32_t ID) { - CScriptVar::setTemporaryID_recursive(ID); - value->setTemporaryID_recursive(ID); + +CScriptVarLinkWorkPtr CScriptVarLinkWorkPtr::setter( bool &execute, const CScriptVarPtr &Var ) { + if(execute) { + if(link && link->getVarPtr() && link->getVarPtr()->isAccessor()) { + const CScriptVarPtr &var = link->getVarPtr(); + CScriptVarLinkPtr setter = var->findChild(TINYJS_ACCESSOR_SET_VAR); + if(setter) { + vector Params; + Params.push_back(Var); + ASSERT(getReferencedOwner()); + setter->getVarPtr()->getContext()->callFunction(execute, setter->getVarPtr(), Params, getReferencedOwner()); + } + } else if(link->isWritable()) + link->setVarPtr(Var); + } + return *this; } + ////////////////////////////////////////////////////////////////////////// -/// CScriptVarError +/// CScriptVarPrimitive ////////////////////////////////////////////////////////////////////////// -const char *ERROR_NAME[] = {"Error", "EvalError", "RangeError", "ReferenceError", "SyntaxError", "TypeError"}; -CScriptVarError::CScriptVarError(CTinyJS *Context, ERROR_TYPES type, const char *message, const char *file, int line, int column) : CScriptVarObject(Context, Context->getErrorPrototype(type)) { - if(message && *message) addChild("message", newScriptVar(message)); - if(file && *file) addChild("fileName", newScriptVar(file)); - if(line>=0) addChild("lineNumber", newScriptVar(line+1)); - if(column>=0) addChild("column", newScriptVar(column+1)); +CScriptVarPrimitive::~CScriptVarPrimitive(){} + +bool CScriptVarPrimitive::isPrimitive() { return true; } +CScriptVarPrimitivePtr CScriptVarPrimitive::getRawPrimitive() { return this; } +bool CScriptVarPrimitive::toBoolean() { return false; } +CScriptVarPtr CScriptVarPrimitive::toObject() { return this; } +CScriptVarPtr CScriptVarPrimitive::toString_CallBack( bool &execute, int radix/*=0*/ ) { + return newScriptVar(toCString(radix)); } -CScriptVarError::~CScriptVarError() {} -CScriptVarPtr CScriptVarError::clone() { return new CScriptVarError(*this); } -bool CScriptVarError::isError() { return true; } -CScriptVarPtr CScriptVarError::_toString(bool execute, int radix) { - CScriptVarLink *link; - string name = ERROR_NAME[Error]; - link = findChildWithPrototypeChain("name"); if(link) name = (*link)->getString(); - string message; link = findChildWithPrototypeChain("message"); if(link) - message = (*link)->getString(); - string fileName; link = findChildWithPrototypeChain("fileName"); if(link) fileName = (*link)->getString(); - int lineNumber=-1; link = findChildWithPrototypeChain("lineNumber"); if(link) lineNumber = (*link)->getInt(); - int column=-1; link = findChildWithPrototypeChain("column"); if(link) column = (*link)->getInt(); - ostringstream msg; - msg << name << ": " << message; - if(lineNumber >= 0) msg << " at Line:" << lineNumber+1; - if(column >=0) msg << " Column:" << column+1; - if(fileName.length()) msg << " in " << fileName; - return newScriptVar(msg.str()); -} +////////////////////////////////////////////////////////////////////////// +// CScriptVarUndefined +////////////////////////////////////////////////////////////////////////// + +declare_dummy_t(Undefined); +CScriptVarUndefined::CScriptVarUndefined(CTinyJS *Context) : CScriptVarPrimitive(Context, Context->objectPrototype) { } +CScriptVarUndefined::~CScriptVarUndefined() {} +CScriptVarPtr CScriptVarUndefined::clone() { return new CScriptVarUndefined(*this); } +bool CScriptVarUndefined::isUndefined() { return true; } + +CNumber CScriptVarUndefined::toNumber_Callback() { return NaN; } +string CScriptVarUndefined::toCString(int radix/*=0*/) { return "undefined"; } +string CScriptVarUndefined::getVarType() { return "undefined"; } + ////////////////////////////////////////////////////////////////////////// -/// CScriptVarAccessor +// CScriptVarNull ////////////////////////////////////////////////////////////////////////// -declare_dummy_t(Accessor); -CScriptVarAccessor::CScriptVarAccessor(CTinyJS *Context) : CScriptVar(Context, Context->objectPrototype) { } -CScriptVarAccessor::~CScriptVarAccessor() {} -CScriptVarPtr CScriptVarAccessor::clone() { return new CScriptVarAccessor(*this); } -bool CScriptVarAccessor::isAccessor() { return true; } -bool CScriptVarAccessor::isPrimitive() { return false; } -string CScriptVarAccessor::getString() { return "[ Object ]"; }; -string CScriptVarAccessor::getParsableString(const string &indentString, const string &indent) { +declare_dummy_t(Null); +CScriptVarNull::CScriptVarNull(CTinyJS *Context) : CScriptVarPrimitive(Context, Context->objectPrototype) { } +CScriptVarNull::~CScriptVarNull() {} +CScriptVarPtr CScriptVarNull::clone() { return new CScriptVarNull(*this); } +bool CScriptVarNull::isNull() { return true; } - /* - ostringstream destination; - string nl = indent.size() ? "\n" : " "; +CNumber CScriptVarNull::toNumber_Callback() { return 0; } +string CScriptVarNull::toCString(int radix/*=0*/) { return "null"; } +string CScriptVarNull::getVarType() { return "null"; } - destination << "{"; - int count = Childs.size(); - if(count) { - string new_indentString = indentString + indent; - destination << nl; - for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it) { - if (isAlphaNum((*it)->getName())) - destination << new_indentString << (*it)->getName(); - else - destination << new_indentString << "\"" << getJSString((*it)->getName()) << "\""; - destination << " : "; - destination << (*(*it))->getParsableString(new_indentString, indent); - if (--count) destination << "," << nl; - } - destination << nl << indentString; - } - destination << "}"; - return destination.str(); +////////////////////////////////////////////////////////////////////////// +/// CScriptVarString +////////////////////////////////////////////////////////////////////////// + +CScriptVarString::CScriptVarString(CTinyJS *Context, const string &Data) : CScriptVarPrimitive(Context, Context->stringPrototype), data(Data) { + addChild("length", newScriptVar(data.size()), SCRIPTVARLINK_CONSTANT); +/* + CScriptVarLinkPtr acc = addChild("length", newScriptVar(Accessor), 0); + CScriptVarFunctionPtr getter(::newScriptVar(Context, this, &CScriptVarString::native_Length, 0)); + getter->setFunctionData(new CScriptTokenDataFnc); + acc->getVarPtr()->addChild(TINYJS_ACCESSOR_GET_VAR, getter, 0); */ - return ""; } -string CScriptVarAccessor::getVarType() { return "accessor"; } +CScriptVarString::~CScriptVarString() {} +CScriptVarPtr CScriptVarString::clone() { return new CScriptVarString(*this); } +bool CScriptVarString::isString() { return true; } +bool CScriptVarString::toBoolean() { return data.length()!=0; } +CNumber CScriptVarString::toNumber_Callback() { return data.c_str(); } +string CScriptVarString::toCString(int radix/*=0*/) { return data; } +string CScriptVarString::getParsableString(const string &indentString, const string &indent, uint32_t uniqueID, bool &hasRecursion) { return getJSString(data); } +string CScriptVarString::getVarType() { return "string"; } -////////////////////////////////////////////////////////////////////////// CScriptVarArray +CScriptVarPtr CScriptVarString::toObject() { + CScriptVarPtr ret = newScriptVar(CScriptVarPrimitivePtr(this), context->stringPrototype); + ret->addChild("length", newScriptVar(data.size()), SCRIPTVARLINK_CONSTANT); + return ret; +} -declare_dummy_t(Array); -CScriptVarArray::CScriptVarArray(CTinyJS *Context) : CScriptVar(Context, Context->arrayPrototype) { - CScriptVarLink *acc = addChild("length", newScriptVar(Accessor), 0); - CScriptVarFunctionPtr getter(::newScriptVar(Context, this, &CScriptVarArray::native_Length, 0)); - getter->setFunctionData(new CScriptTokenDataFnc); - (*acc)->addChild(TINYJS_ACCESSOR_GET_VAR, getter, 0); +CScriptVarPtr CScriptVarString::toString_CallBack( bool &execute, int radix/*=0*/ ) { + return this; } -CScriptVarArray::~CScriptVarArray() {} -CScriptVarPtr CScriptVarArray::clone() { return new CScriptVarArray(*this); } -bool CScriptVarArray::isArray() { return true; } -bool CScriptVarArray::isPrimitive() { return false; } -string CScriptVarArray::getString() { - ostringstream destination; - int len = getArrayLength(); - for (int i=0;igetString(); - if (i= data.length()) + return -1; + else + return (unsigned char)data[Idx]; } -string CScriptVarArray::getVarType() { return "object"; } -string CScriptVarArray::getParsableString(const string &indentString, const string &indent) { - ostringstream destination; - string nl = indent.size() ? "\n" : " "; - destination << "["; - int len = getArrayLength(); - if(len) { - destination << nl; - string new_indentString = indentString + indent; - for (int i=0;igetParsableString(new_indentString, indent); - if (i::has_infinity && Value == numeric_limits::infinity()) + type=tInfinity, Int32=1; + else if(numeric_limits::has_infinity && Value == -numeric_limits::infinity()) + type=tInfinity, Int32=-1; + else if(::isNaN(Value) || Value == numeric_limits::quiet_NaN() || Value == std::numeric_limits::signaling_NaN()) + type=tNaN, Int32=0; + else if(modf(Value, &integral)==0.0 && numeric_limits::min()<=integral && integral<=numeric_limits::max()) + type=tInt32, Int32=int32_t(integral); + else + type=tDouble, Double=Value; + return *this; +} +CNumber &CNumber::operator=(const char *str) { + + while(isWhitespace(*str)) str++; + const char *start = str, *endptr; + if(*str == '-' || *str == '+') str++; + if(*str == '0' && ( str[1]=='x' || str[1]=='X')) + parseInt(start, 16, &endptr); + else if(*str == '0' && str[1]>='0' && str[1]<='7') + parseInt(start, 8, &endptr); + else + parseFloat(start, &endptr); + while(isWhitespace(*endptr)) endptr++; + if(*endptr != '\0') + type=tNaN, Int32=0; + return *this; + +#if 0 + double d; + bool sig=false; + while(isWhitespace(*str)) str++; + if(*str == '-') sig=true, str++; + char *ptr=(char*)str; + while(*ptr=='0' && ptr[1]>'0' && ptr[1]<'9') ptr++; // skip leading '0' to prevent parse as octal + char *endptr; + long i = strtol(ptr,&endptr,0); + if(ptr != endptr) { + ptr = endptr; + while(isWhitespace(*endptr)) endptr++; + if(*endptr == '\0') { + if(sig && i==0) + return operator=(negativeZero); + else + return operator=(int32_t(sig?-i:i)); + } + } + if(*ptr=='.' || *ptr=='e' || *ptr=='E') { + d = strtod(str,&endptr); + if(str!=endptr) { + while(isWhitespace(*endptr)) endptr++; + if(*endptr == '\0') { + if(sig && d==0) + return operator=(negativeZero); + else + return operator=(sig?-d:d); + } } - destination << nl << indentString; } - destination << "]"; - return destination.str(); + type=tNaN, Int32=0; + return *this; +#endif } - -void CScriptVarArray::native_Length(const CFunctionsScopePtr &c, void *data) -{ - c->setReturnVar(newScriptVar(c->getArgument("this")->getArrayLength())); +int32_t CNumber::parseInt(const char * str, int32_t radix/*=0*/, const char **endptr/*=0*/) { + type=tInt32, Int32=0; + if(endptr) *endptr = str; + bool stripPrefix = false; //< is true if radix==0 or radix==16 + if(radix == 0) { + radix=10; + stripPrefix = true; + } else if(radix < 2 || radix > 36) { + type = tNaN; + return 0; + } else + stripPrefix = radix == 16; + while(isWhitespace(*str)) str++; + int sign=1; + if(*str=='-') sign=-1,str++; + else if(*str=='+') str++; + if(stripPrefix && *str=='0' && (str[1]=='x' || str[1]=='X')) str+=2, radix=16; + else if(stripPrefix && *str=='0' && str[1]>='0' && str[1]<='7') str+=1, radix=8; + int32_t max = 0x7fffffff/radix; + const char *start = str; + for( ; *str; str++) { + if(*str >= '0' && *str <= '0'-1+radix) Int32 = Int32*radix+*str-'0'; + else if(*str>='a' && *str<='a'-11+radix) Int32 = Int32*radix+*str-'a'+10; + else if(*str>='A' && *str<='A'-11+radix) Int32 = Int32*radix+*str-'A'+10; + else break; + if(Int32 >= max) { + type=tDouble, Double=double(Int32); + for(str++ ; *str; str++) { + if(*str >= '0' && *str <= '0'-1+radix) Double = Double *radix+*str-'0'; + else if(*str>='a' && *str<='a'-11+radix) Double = Double *radix+*str-'a'+10; + else if(*str>='A' && *str<='A'-11+radix) Double = Double *radix+*str-'A'+10; + else break; + } + break; + } + } + if(str == start) { + type= tNaN; + return 0; + } + if(sign<0 && ((type==tInt32 && Int32==0) || (type==tDouble && Double==0.0))) { type=tnNULL,Int32=0; return radix; } + if(type==tInt32) operator=(sign<0 ? -Int32 : Int32); + else operator=(sign<0 ? -Double : Double); + if(endptr) *endptr = (char*)str; + return radix; +} + +void CNumber::parseFloat(const char * str, const char **endptr/*=0*/) { + type=tInt32, Int32=0; + if(endptr) *endptr = str; + while(isWhitespace(*str)) str++; + int sign=1; + if(*str=='-') sign=-1,str++; + else if(*str=='+') str++; + if(strncmp(str, "Infinity", 8) == 0) { type=tInfinity, Int32=sign; return; } + double d = strtod(str, (char**)endptr); + operator=(sign>0 ? d : -d); + return; +} + +CNumber CNumber::add(const CNumber &Value) const { + if(type==tNaN || Value.type==tNaN) + return CNumber(tNaN); + else if(type==tInfinity || Value.type==tInfinity) { + if(type!=tInfinity) + return Value; + else if(Value.type!=tInfinity || sign()==Value.sign()) + return *this; + else + return CNumber(tNaN); + } else if(type==tnNULL) + return Value; + else if(Value.type==tnNULL) + return *this; + else if(type==tDouble || Value.type==tDouble) + return CNumber(toDouble()+Value.toDouble()); + else { + int32_t range_max = numeric_limits::max(); + int32_t range_min = numeric_limits::min(); + if(Int32>0) range_max-=Int32; + else if(Int32<0) range_min-=Int32; + if(range_min<=Value.Int32 && Value.Int32<=range_max) + return CNumber(Int32+Value.Int32); + else + return CNumber(double(Int32)+double(Value.Int32)); + } } -////////////////////////////////////////////////////////////////////////// CScriptVarNull +CNumber CNumber::operator-() const { + switch(type) { + case tInt32: + if(Int32==0) + return CNumber(NegativeZero); + case tnNULL: + return CNumber(-Int32); + case tDouble: + return CNumber(-Double); + case tInfinity: + return CNumber(tInfinity, -Int32); + default: + return CNumber(tNaN); + } +} -declare_dummy_t(Null); -CScriptVarNull::CScriptVarNull(CTinyJS *Context) : CScriptVar(Context, Context->objectPrototype) { } -CScriptVarNull::~CScriptVarNull() {} -CScriptVarPtr CScriptVarNull::clone() { return new CScriptVarNull(*this); } -bool CScriptVarNull::isNull() { return true; } -bool CScriptVarNull::isNumeric() {return true; } -string CScriptVarNull::getString() { return "null"; }; -string CScriptVarNull::getVarType() { return "null"; } -CScriptVarPtr CScriptVarNull::getNumericVar() { return newScriptVar(0); } +static inline int bits(uint32_t Value) { + uint32_t b=0, mask=0xFFFF0000UL; + for(int shift=16; shift>0 && Value!=0; shift>>=1, mask>>=shift) { + if(Value & mask) { + b += shift; + Value>>=shift; + } + } + return b; +} +static inline int bits(int32_t Value) { + return bits(uint32_t(Value<0?-Value:Value)); +} +CNumber CNumber::multi(const CNumber &Value) const { + if(type==tNaN || Value.type==tNaN) + return CNumber(tNaN); + else if(type==tInfinity || Value.type==tInfinity) { + if(isZero() || Value.isZero()) + return CNumber(tNaN); + else + return CNumber(tInfinity, sign()==Value.sign()?1:-1); + } else if(isZero() || Value.isZero()) { + if(sign()==Value.sign()) + return CNumber(0); + else + return CNumber(NegativeZero); + } else if(type==tDouble || Value.type==tDouble) + return CNumber(toDouble()*Value.toDouble()); + else { + // Int32*Int32 + if(bits(Int32)+bits(Value.Int32) <= 29) + return CNumber(Int32*Value.Int32); + else + return CNumber(double(Int32)*double(Value.Int32)); + } +} -////////////////////////////////////////////////////////////////////////// CScriptVarUndefined -declare_dummy_t(Undefined); -CScriptVarUndefined::CScriptVarUndefined(CTinyJS *Context) : CScriptVar(Context, Context->objectPrototype) { } -CScriptVarUndefined::~CScriptVarUndefined() {} -CScriptVarPtr CScriptVarUndefined::clone() { return new CScriptVarUndefined(*this); } -bool CScriptVarUndefined::isUndefined() { return true; } -bool CScriptVarUndefined::isNumeric() {return true; } -string CScriptVarUndefined::getString() { return "undefined"; }; -string CScriptVarUndefined::getVarType() { return "undefined"; } +CNumber CNumber::div( const CNumber &Value ) const { + if(type==tNaN || Value.type==tNaN) return CNumber(tNaN); + int Sign = sign()*Value.sign(); + if(type==tInfinity) { + if(Value.type==tInfinity) return CNumber(tNaN); + else return CNumber(tInfinity, Sign); + } + if(Value.type==tInfinity) { + if(Sign<0) return CNumber(NegativeZero); + else return CNumber(0); + } else if(Value.isZero()) { + if(isZero()) return CNumber(tNaN); + else return CNumber(tInfinity, Sign); + } else + return CNumber(toDouble() / Value.toDouble()); +} +CNumber CNumber::modulo( const CNumber &Value ) const { + if(type==tNaN || type==tInfinity || Value.type==tNaN || Value.isZero()) return CNumber(tNaN); + if(Value.type==tInfinity) return CNumber(*this); + if(isZero()) return CNumber(0); + if(type==tDouble || Value.type==tDouble) { + double n = toDouble(), d = Value.toDouble(), q; + modf(n/d, &q); + return CNumber(n - (d * q)); + } else + return CNumber(Int32 % Value.Int32); +} -////////////////////////////////////////////////////////////////////////// CScriptVarNaN +CNumber CNumber::round() const { + if(type != tDouble) return CNumber(*this); + if(Double < 0.0 && Double >= -0.5) + return CNumber(NegativeZero); + return CNumber(::floor(Double+0.5)); +} -declare_dummy_t(NaN); -CScriptVarNaN::CScriptVarNaN(CTinyJS *Context) : CScriptVar(Context, Context->numberPrototype) {} -CScriptVarNaN::~CScriptVarNaN() {} -CScriptVarPtr CScriptVarNaN::clone() { return new CScriptVarNaN(*this); } -bool CScriptVarNaN::isNaN() { return true; } -bool CScriptVarNaN::isNumeric() {return true; } -string CScriptVarNaN::getString() { return "NaN"; }; -string CScriptVarNaN::getVarType() { return "number"; } +CNumber CNumber::floor() const { + if(type != tDouble) return CNumber(*this); + return CNumber(::floor(Double)); +} +CNumber CNumber::ceil() const { + if(type != tDouble) return CNumber(*this); + return CNumber(::ceil(Double)); +} -////////////////////////////////////////////////////////////////////////// CScriptVarString +CNumber CNumber::abs() const { + if(sign()<0) return -CNumber(*this); + else return CNumber(*this); +} -CScriptVarString::CScriptVarString(CTinyJS *Context, const std::string &Data) : CScriptVar(Context, Context->stringPrototype), data(Data) { - CScriptVarLink *acc = addChild("length", newScriptVar(Accessor), 0); - CScriptVarFunctionPtr getter(::newScriptVar(Context, this, &CScriptVarString::native_Length, 0)); - getter->setFunctionData(new CScriptTokenDataFnc); - (*acc)->addChild(TINYJS_ACCESSOR_GET_VAR, getter, 0); +CNumber CNumber::shift(const CNumber &Value, bool Right) const { + int32_t lhs = toInt32(); + uint32_t rhs = Value.toUInt32() & 0x1F; + return CNumber(Right ? lhs>>rhs : lhs<>rhs : lhs<setReturnVar(newScriptVar((int)this->data.size())); +CNumber CNumber::binary(const CNumber &Value, char Mode) const { + int32_t lhs = toInt32(); + int32_t rhs = Value.toInt32(); + + switch(Mode) { + case '&': lhs&=rhs; break; + case '|': lhs|=rhs; break; + case '^': lhs^=rhs; break; + } + return CNumber(lhs); } -////////////////////////////////////////////////////////////////////////// CScriptVarIntegerBase +int CNumber::less( const CNumber &Value ) const { + if(type==tNaN || Value.type==tNaN) return 0; + else if(type==tInfinity) { + if(Value.type==tInfinity) return Int32numberPrototype, Data) {} -CScriptVarInteger::~CScriptVarInteger() {} -CScriptVarPtr CScriptVarInteger::clone() { return new CScriptVarInteger(*this); } -bool CScriptVarInteger::isNumber() { return true; } -bool CScriptVarInteger::isInt() { return true; } -static char *tiny_ltoa(long val, unsigned radix) { +int CNumber::sign() const { + switch(type) { + case tInt32: + case tInfinity: + return Int32<0?-1:1; + case tnNULL: + return -1; + case tDouble: + return Double<0.0?-1:1; + default: + return 1; + } +} +char *tiny_ltoa(int32_t val, unsigned radix) { char *buf, *buf_end, *p, *firstdig, temp; unsigned digval; buf = (char*)malloc(64); if(!buf) return 0; buf_end = buf+64-1; // -1 for '\0' - + p = buf; if (val < 0) { *p++ = '-'; @@ -2422,6 +3216,7 @@ static char *tiny_ltoa(long val, unsigned radix) { // order. Thus we reverse them now. *p-- = '\0'; firstdig = buf; + if(*firstdig=='-') firstdig++; do { temp = *p; *p = *firstdig; @@ -2432,60 +3227,6 @@ static char *tiny_ltoa(long val, unsigned radix) { return buf; } - -CScriptVarPtr CScriptVarInteger::_toString(bool execute, int radix) { - if(2 <= radix && radix <= 36) - ; - else - radix = 10; // todo error; - char *str = tiny_ltoa(data, radix); - if(str) { - CScriptVarPtr val = newScriptVar(str); - free(str); - return val; - } - return constScriptVar(Undefined); // TODO throw Error -} - - -////////////////////////////////////////////////////////////////////////// CScriptVarBool - -CScriptVarBool::CScriptVarBool(CTinyJS *Context, bool Data) : CScriptVarIntegerBase(Context, Context->booleanPrototype, Data?1:0) {} -CScriptVarBool::~CScriptVarBool() {} -CScriptVarPtr CScriptVarBool::clone() { return new CScriptVarBool(*this); } -bool CScriptVarBool::isBool() { return true; } -string CScriptVarBool::getString() {return data!=0?"true":"false";} -string CScriptVarBool::getVarType() { return "boolean"; } -CScriptVarPtr CScriptVarBool::getNumericVar() { return newScriptVar(data); } - - -////////////////////////////////////////////////////////////////////////// CScriptVarInfinity - -Infinity InfinityPositive(1); -Infinity InfinityNegative(-1); -CScriptVarInfinity::CScriptVarInfinity(CTinyJS *Context, int Data) : CScriptVarIntegerBase(Context, Context->numberPrototype, Data<0?-1:1) {} -CScriptVarInfinity::~CScriptVarInfinity() {} -CScriptVarPtr CScriptVarInfinity::clone() { return new CScriptVarInfinity(*this); } -int CScriptVarInfinity::isInfinity() { return data; } -string CScriptVarInfinity::getString() {return data<0?"-Infinity":"Infinity";} - - -////////////////////////////////////////////////////////////////////////// CScriptVarDouble - -CScriptVarDouble::CScriptVarDouble(CTinyJS *Context, double Data) : CScriptVar(Context, Context->numberPrototype), data(Data) {} -CScriptVarDouble::~CScriptVarDouble() {} -CScriptVarPtr CScriptVarDouble::clone() { return new CScriptVarDouble(*this); } -bool CScriptVarDouble::isDouble() { return true; } -bool CScriptVarDouble::isNumber() { return true; } -bool CScriptVarDouble::isNumeric() {return true; } - -int CScriptVarDouble::getInt() {return (int)data; } -bool CScriptVarDouble::getBool() {return data!=0.0;} -double CScriptVarDouble::getDouble() {return data;} -string CScriptVarDouble::getString() {return float2string(data);} -string CScriptVarDouble::getVarType() { return "number"; } -CScriptVarPtr CScriptVarDouble::getNumericVar() { return this; } - static char *tiny_dtoa(double val, unsigned radix) { char *buf, *buf_end, *p, temp; unsigned digval; @@ -2550,129 +3291,390 @@ static char *tiny_dtoa(double val, unsigned radix) { *p = '\0'; return buf; } - - -CScriptVarPtr CScriptVarDouble::_toString(bool execute, int radix) { - if(2 <= radix && radix <= 36) - ; - else - radix = 10; // todo error; - if(radix == 10) - return newScriptVar(float2string(data)); - else { - char *str = tiny_dtoa(data, radix); - if(str) { - CScriptVarPtr val = newScriptVar(str); - free(str); - return val; +std::string CNumber::toString( uint32_t Radix/*=10*/ ) const { + char *str; + if(2 > Radix || Radix > 36) + Radix = 10; // todo error; + switch(type) { + case tInt32: + if( (str = tiny_ltoa(Int32, Radix)) ) { + string ret(str); free(str); + return ret; } - return constScriptVar(Undefined); // TODO throw Error + break; + case tnNULL: + return "0"; + case tDouble: + if(Radix==10) { + ostringstream str; + str.unsetf(ios::floatfield); +#if (defined(_MSC_VER) && _MSC_VER >= 1600) || __cplusplus >= 201103L + str.precision(numeric_limits::max_digits10); +#else + str.precision(numeric_limits::digits10+2); +#endif + str << Double; + return str.str(); + } else if( (str = tiny_dtoa(Double, Radix)) ) { + string ret(str); free(str); + return ret; + } + break; + case tInfinity: + return Int32<0?"-Infinity":"Infinity"; + case tNaN: + return "NaN"; } + return ""; } +double CNumber::toDouble() const +{ + switch(type) { + case tnNULL: + return -0.0; + case tInt32: + return double(Int32); + case tDouble: + return Double; + case tNaN: + return std::numeric_limits::quiet_NaN(); + case tInfinity: + return Int32<0 ? -std::numeric_limits::infinity():std::numeric_limits::infinity(); + } + return 0.0; +} -////////////////////////////////////////////////////////////////////////// CScriptVarFunction +#endif -CScriptVarFunction::CScriptVarFunction(CTinyJS *Context, CScriptTokenDataFnc *Data) : CScriptVarObject(Context, Context->functionPrototype), data(0) { - setFunctionData(Data); + +////////////////////////////////////////////////////////////////////////// +/// CScriptVarNumber +////////////////////////////////////////////////////////////////////////// + +CScriptVarNumber::CScriptVarNumber(CTinyJS *Context, const CNumber &Data) : CScriptVarPrimitive(Context, Context->numberPrototype), data(Data) {} +CScriptVarNumber::~CScriptVarNumber() {} +CScriptVarPtr CScriptVarNumber::clone() { return new CScriptVarNumber(*this); } +bool CScriptVarNumber::isNumber() { return true; } +bool CScriptVarNumber::isInt() { return data.isInt32(); } +bool CScriptVarNumber::isDouble() { return data.isDouble(); } +bool CScriptVarNumber::isRealNumber() { return isInt() || isDouble(); } +bool CScriptVarNumber::isNaN() { return data.isNaN(); } +int CScriptVarNumber::isInfinity() { return data.isInfinity(); } + +bool CScriptVarNumber::toBoolean() { return data.toBoolean(); } +CNumber CScriptVarNumber::toNumber_Callback() { return data; } +string CScriptVarNumber::toCString(int radix/*=0*/) { return data.toString(radix); } + +string CScriptVarNumber::getVarType() { return "number"; } + +CScriptVarPtr CScriptVarNumber::toObject() { return newScriptVar(CScriptVarPrimitivePtr(this), context->numberPrototype); } +inline define_newScriptVar_Fnc(Number, CTinyJS *Context, const CNumber &Obj) { + if(!Obj.isInt32() && !Obj.isDouble()) { + if(Obj.isNaN()) return Context->constScriptVar(NaN); + if(Obj.isInfinity()) return Context->constScriptVar(Infinity(Obj.sign())); + if(Obj.isNegativeZero()) return Context->constScriptVar(NegativeZero); + } + return new CScriptVarNumber(Context, Obj); } -CScriptVarFunction::~CScriptVarFunction() { setFunctionData(0); } -CScriptVarPtr CScriptVarFunction::clone() { return new CScriptVarFunction(*this); } -bool CScriptVarFunction::isObject() { return true; } -bool CScriptVarFunction::isFunction() { return true; } -bool CScriptVarFunction::isPrimitive() { return false; } -//string CScriptVarFunction::getString() {return "[ Function ]";} -string CScriptVarFunction::getVarType() { return "function"; } -string CScriptVarFunction::getParsableBlockString(TOKEN_VECT_it &it, TOKEN_VECT_it end, const string indentString, const string indent) { - ostringstream destination; - string nl = indent.size() ? "\n" : " "; - string my_indentString = indentString; - bool add_nl=false, block_start=false; - for(; it != end; ++it) { +////////////////////////////////////////////////////////////////////////// +// CScriptVarBool +////////////////////////////////////////////////////////////////////////// - string OutString; - if(add_nl) OutString.append(nl).append(my_indentString); - bool old_block_start = block_start; - add_nl = block_start = false; - if(it->token == LEX_T_LOOP_LABEL) { - // ignore BLIND_LABLE - }else if(LEX_TOKEN_DATA_STRING(it->token)) - OutString.append(it->String()).append(" "); - else if(LEX_TOKEN_DATA_FLOAT(it->token)) - OutString.append(float2string(it->Float())).append(" "); - else if(it->token == LEX_INT) - OutString.append(int2string(it->Int())).append(" "); - else if(LEX_TOKEN_DATA_FUNCTION(it->token)) { - OutString.append("function "); - if(it->token == LEX_R_FUNCTION) - OutString.append(data->name); - OutString.append("("); - if(data->arguments.size()) { - OutString.append(data->arguments.front()); - for(STRING_VECTOR_it it=data->arguments.begin()+1; it!=data->arguments.end(); ++it) - OutString.append(", ").append(*it); - } - OutString.append(") "); - TOKEN_VECT_it it=data->body.begin(); - OutString += getParsableBlockString(it, data->body.end(), indentString, indent); +CScriptVarBool::CScriptVarBool(CTinyJS *Context, bool Data) : CScriptVarPrimitive(Context, Context->booleanPrototype), data(Data) {} +CScriptVarBool::~CScriptVarBool() {} +CScriptVarPtr CScriptVarBool::clone() { return new CScriptVarBool(*this); } +bool CScriptVarBool::isBool() { return true; } - } else if(it->token == '{') { - OutString.append("{"); - my_indentString.append(indent); - add_nl = block_start = true; - } else if(it->token == '}') { - my_indentString.resize(my_indentString.size() - min(my_indentString.size(),indent.size())); - if(old_block_start) - OutString = "}"; - else - OutString = nl + my_indentString + "}"; - add_nl = true; - } else if(it->token == LEX_T_SKIP) { - // ignore SKIP-Token - } else { - OutString.append(CScriptLex::getTokenStr(it->token)); - if(it->token==';') add_nl=true; else OutString.append(" "); +bool CScriptVarBool::toBoolean() { return data; } +CNumber CScriptVarBool::toNumber_Callback() { return data?1:0; } +string CScriptVarBool::toCString(int radix/*=0*/) { return data ? "true" : "false"; } + +string CScriptVarBool::getVarType() { return "boolean"; } + +CScriptVarPtr CScriptVarBool::toObject() { return newScriptVar(CScriptVarPrimitivePtr(this), context->booleanPrototype); } + +////////////////////////////////////////////////////////////////////////// +/// CScriptVarObject +////////////////////////////////////////////////////////////////////////// + +declare_dummy_t(Object); +CScriptVarObject::CScriptVarObject(CTinyJS *Context) : CScriptVar(Context, Context->objectPrototype) { } +CScriptVarObject::~CScriptVarObject() {} +CScriptVarPtr CScriptVarObject::clone() { return new CScriptVarObject(*this); } +CScriptVarPrimitivePtr CScriptVarObject::getRawPrimitive() { return value; } +bool CScriptVarObject::isObject() { return true; } + +string CScriptVarObject::getParsableString(const string &indentString, const string &indent, uint32_t uniqueID, bool &hasRecursion) { + getParsableStringRecursionsCheck(); + string destination; + const char *nl = indent.size() ? "\n" : " "; + const char *comma = ""; + destination.append("{"); + if(Childs.size()) { + string new_indentString = indentString + indent; + for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it) { + if((*it)->isEnumerable()) { + destination.append(comma); comma=","; + destination.append(nl).append(new_indentString).append(getIDString((*it)->getName())); + destination.append(" : "); + destination.append((*it)->getVarPtr()->getParsableString(new_indentString, indent, uniqueID, hasRecursion)); + } } - destination << OutString; + destination.append(nl).append(indentString); } - return destination.str(); + destination.append("}"); + return destination; } +string CScriptVarObject::getVarType() { return "object"; } -string CScriptVarFunction::getParsableString(const string &indentString, const string &indent) { - ostringstream destination; - string nl = indent.size() ? "\n" : " "; +CScriptVarPtr CScriptVarObject::toObject() { return this; } + +CScriptVarPtr CScriptVarObject::valueOf_CallBack() { + if(value) + return value->valueOf_CallBack(); + return CScriptVar::valueOf_CallBack(); +} +CScriptVarPtr CScriptVarObject::toString_CallBack(bool &execute, int radix) { + if(value) + return value->toString_CallBack(execute, radix); + return newScriptVar("[object Object]"); +}; + +void CScriptVarObject::setTemporaryID_recursive( uint32_t ID ) { + CScriptVar::setTemporaryID_recursive(ID); + if(value) value->setTemporaryID_recursive(ID); +} + + +////////////////////////////////////////////////////////////////////////// +/// CScriptVarError +////////////////////////////////////////////////////////////////////////// + +const char *ERROR_NAME[] = {"Error", "EvalError", "RangeError", "ReferenceError", "SyntaxError", "TypeError"}; + +CScriptVarError::CScriptVarError(CTinyJS *Context, ERROR_TYPES type, const char *message, const char *file, int line, int column) : CScriptVarObject(Context, Context->getErrorPrototype(type)) { + if(message && *message) addChild("message", newScriptVar(message)); + if(file && *file) addChild("fileName", newScriptVar(file)); + if(line>=0) addChild("lineNumber", newScriptVar(line+1)); + if(column>=0) addChild("column", newScriptVar(column+1)); +} - destination << "function "<name<<"("; +CScriptVarError::~CScriptVarError() {} +CScriptVarPtr CScriptVarError::clone() { return new CScriptVarError(*this); } +bool CScriptVarError::isError() { return true; } - // get list of parameters - if(data->arguments.size()) { - destination << data->arguments.front(); - for(STRING_VECTOR_it it = data->arguments.begin()+1; it != data->arguments.end(); ++it) { - destination << ", " << *it; +CScriptVarPtr CScriptVarError::toString_CallBack(bool &execute, int radix) { + CScriptVarLinkPtr link; + string name = ERROR_NAME[Error]; + link = findChildWithPrototypeChain("name"); if(link) name = link->toString(execute); + string message; link = findChildWithPrototypeChain("message"); if(link) + message = link->toString(execute); + string fileName; link = findChildWithPrototypeChain("fileName"); if(link) fileName = link->toString(execute); + int lineNumber=-1; link = findChildWithPrototypeChain("lineNumber"); if(link) lineNumber = link->toNumber().toInt32(); + int column=-1; link = findChildWithPrototypeChain("column"); if(link) column = link->toNumber().toInt32(); + ostringstream msg; + msg << name << ": " << message; + if(lineNumber >= 0) msg << " at Line:" << lineNumber+1; + if(column >=0) msg << " Column:" << column+1; + if(fileName.length()) msg << " in " << fileName; + return newScriptVar(msg.str()); +} + + +////////////////////////////////////////////////////////////////////////// +// CScriptVarArray +////////////////////////////////////////////////////////////////////////// + +declare_dummy_t(Array); +CScriptVarArray::CScriptVarArray(CTinyJS *Context) : CScriptVarObject(Context, Context->arrayPrototype) { + CScriptVarLinkPtr acc = addChild("length", newScriptVar(Accessor), 0); + CScriptVarFunctionPtr getter(::newScriptVar(Context, this, &CScriptVarArray::native_Length, 0)); + getter->setFunctionData(new CScriptTokenDataFnc); + acc->getVarPtr()->addChild(TINYJS_ACCESSOR_GET_VAR, getter, 0); +} + +CScriptVarArray::~CScriptVarArray() {} +CScriptVarPtr CScriptVarArray::clone() { return new CScriptVarArray(*this); } +bool CScriptVarArray::isArray() { return true; } +string CScriptVarArray::getParsableString(const string &indentString, const string &indent, uint32_t uniqueID, bool &hasRecursion) { + getParsableStringRecursionsCheck(); + string destination; + const char *nl = indent.size() ? "\n" : " "; + const char *comma = ""; + destination.append("["); + int len = getArrayLength(); + if(len) { + string new_indentString = indentString + indent; + for (int i=0;igetParsableString(new_indentString, indent, uniqueID, hasRecursion)); } + destination.append(nl).append(indentString); + } + destination.append("]"); + return destination; +} +CScriptVarPtr CScriptVarArray::toString_CallBack( bool &execute, int radix/*=0*/ ) { + ostringstream destination; + int len = getArrayLength(); + for (int i=0;itoString(execute); + if (isetReturnVar(newScriptVar(c->getArgument("this")->getArrayLength())); +} - if(isNative()) { - destination << "{ /* native Code */ }"; - } else { - TOKEN_VECT_it it=data->body.begin(); - destination << getParsableBlockString(it, data->body.end(), indentString, indent); + +////////////////////////////////////////////////////////////////////////// +/// CScriptVarRegExp +////////////////////////////////////////////////////////////////////////// + +#ifndef NO_REGEXP + +CScriptVarRegExp::CScriptVarRegExp(CTinyJS *Context, const string &Regexp, const string &Flags) : CScriptVarObject(Context, Context->regexpPrototype), regexp(Regexp), flags(Flags) { + addChild("global", ::newScriptVarAccessor(Context, this, &CScriptVarRegExp::native_Global, 0, 0, 0), 0); + addChild("ignoreCase", ::newScriptVarAccessor(Context, this, &CScriptVarRegExp::native_IgnoreCase, 0, 0, 0), 0); + addChild("multiline", ::newScriptVarAccessor(Context, this, &CScriptVarRegExp::native_Multiline, 0, 0, 0), 0); + addChild("sticky", ::newScriptVarAccessor(Context, this, &CScriptVarRegExp::native_Sticky, 0, 0, 0), 0); + addChild("regexp", ::newScriptVarAccessor(Context, this, &CScriptVarRegExp::native_Source, 0, 0, 0), 0); + addChild("lastIndex", newScriptVar(0)); +} +CScriptVarRegExp::~CScriptVarRegExp() {} +CScriptVarPtr CScriptVarRegExp::clone() { return new CScriptVarRegExp(*this); } +bool CScriptVarRegExp::isRegExp() { return true; } +//int CScriptVarRegExp::getInt() {return strtol(regexp.c_str(),0,0); } +//bool CScriptVarRegExp::getBool() {return regexp.length()!=0;} +//double CScriptVarRegExp::getDouble() {return strtod(regexp.c_str(),0);} +//string CScriptVarRegExp::getString() { return "/"+regexp+"/"+flags; } +//string CScriptVarRegExp::getParsableString(const string &indentString, const string &indent, uint32_t uniqueID, bool &hasRecursion) { return getString(); } +CScriptVarPtr CScriptVarRegExp::toString_CallBack(bool &execute, int radix) { + return newScriptVar("/"+regexp+"/"+flags); +} +void CScriptVarRegExp::native_Global(const CFunctionsScopePtr &c, void *data) { + c->setReturnVar(constScriptVar(Global())); +} +void CScriptVarRegExp::native_IgnoreCase(const CFunctionsScopePtr &c, void *data) { + c->setReturnVar(constScriptVar(IgnoreCase())); +} +void CScriptVarRegExp::native_Multiline(const CFunctionsScopePtr &c, void *data) { + c->setReturnVar(constScriptVar(Multiline())); +} +void CScriptVarRegExp::native_Sticky(const CFunctionsScopePtr &c, void *data) { + c->setReturnVar(constScriptVar(Sticky())); +} +void CScriptVarRegExp::native_Source(const CFunctionsScopePtr &c, void *data) { + c->setReturnVar(newScriptVar(regexp)); +} +unsigned int CScriptVarRegExp::LastIndex() { + CScriptVarPtr lastIndex = findChild("lastIndex"); + if(lastIndex) return lastIndex->toNumber().toInt32(); + return 0; +} +void CScriptVarRegExp::LastIndex(unsigned int Idx) { + addChildOrReplace("lastIndex", newScriptVar((int)Idx)); +} + +CScriptVarPtr CScriptVarRegExp::exec( const string &Input, bool Test /*= false*/ ) +{ + regex::flag_type flags = regex_constants::ECMAScript; + if(IgnoreCase()) flags |= regex_constants::icase; + bool global = Global(), sticky = Sticky(); + unsigned int lastIndex = LastIndex(); + int offset = 0; + if(global || sticky) { + if(lastIndex > Input.length()) goto failed; + offset=lastIndex; + } + { + regex_constants::match_flag_type mflag = sticky?regex_constants::match_continuous:regex_constants::match_default; + if(offset) mflag |= regex_constants::match_prev_avail; + smatch match; + if(regex_search(Input.begin()+offset, Input.end(), match, regex(regexp, flags), mflag) ) { + LastIndex(offset+match.position()+match.str().length()); + if(Test) return constScriptVar(true); + + CScriptVarArrayPtr retVar = newScriptVar(Array); + retVar->addChild("input", newScriptVar(Input)); + retVar->addChild("index", newScriptVar(match.position())); + for(smatch::size_type idx=0; idxaddChild(int2string(idx), newScriptVar(match[idx].str())); + return retVar; + } + } +failed: + if(global || sticky) + LastIndex(0); + if(Test) return constScriptVar(false); + return constScriptVar(Null); +} + +const char * CScriptVarRegExp::ErrorStr( int Error ) +{ + switch(Error) { + case regex_constants::error_badbrace: return "the expression contained an invalid count in a { } expression"; + case regex_constants::error_badrepeat: return "a repeat expression (one of '*', '?', '+', '{' in most contexts) was not preceded by an expression"; + case regex_constants::error_brace: return "the expression contained an unmatched '{' or '}'"; + case regex_constants::error_brack: return "the expression contained an unmatched '[' or ']'"; + case regex_constants::error_collate: return "the expression contained an invalid collating element name"; + case regex_constants::error_complexity: return "an attempted match failed because it was too complex"; + case regex_constants::error_ctype: return "the expression contained an invalid character class name"; + case regex_constants::error_escape: return "the expression contained an invalid escape sequence"; + case regex_constants::error_paren: return "the expression contained an unmatched '(' or ')'"; + case regex_constants::error_range: return "the expression contained an invalid character range specifier"; + case regex_constants::error_space: return "parsing a regular expression failed because there were not enough resources available"; + case regex_constants::error_stack: return "an attempted match failed because there was not enough memory available"; + case regex_constants::error_backref: return "the expression contained an invalid back reference"; + default: return ""; } - return destination.str(); } -CScriptVarPtr CScriptVarFunction::_toString(bool execute, int radix){ - string indent; - return newScriptVar(getParsableString("", indent)); +#endif /* NO_REGEXP */ + + +////////////////////////////////////////////////////////////////////////// +// CScriptVarFunction +////////////////////////////////////////////////////////////////////////// + +CScriptVarFunction::CScriptVarFunction(CTinyJS *Context, CScriptTokenDataFnc *Data) : CScriptVarObject(Context, Context->functionPrototype), data(0) { + setFunctionData(Data); } +CScriptVarFunction::~CScriptVarFunction() { setFunctionData(0); } +CScriptVarPtr CScriptVarFunction::clone() { return new CScriptVarFunction(*this); } +bool CScriptVarFunction::isObject() { return true; } +bool CScriptVarFunction::isFunction() { return true; } +bool CScriptVarFunction::isPrimitive() { return false; } +//string CScriptVarFunction::getString() {return "[ Function ]";} +string CScriptVarFunction::getVarType() { return "function"; } +string CScriptVarFunction::getParsableString(const string &indentString, const string &indent, uint32_t uniqueID, bool &hasRecursion) { + getParsableStringRecursionsCheck(); + string destination; + destination.append("function ").append(data->name); + // get list of arguments + destination.append(data->getArgumentsString()); + + if(isNative() || isBounded()) + destination.append("{ [native code] }"); + else { + destination.append(CScriptToken::getParsableString(data->body, indentString, indent)); + if(!data->body.size() || data->body.front().token != '{') + destination.append(";"); + } + return destination; +} +CScriptVarPtr CScriptVarFunction::toString_CallBack(bool &execute, int radix){ + bool hasRecursion; + return newScriptVar(getParsableString("", "", 0, hasRecursion)); +} CScriptTokenDataFnc *CScriptVarFunction::getFunctionData() { return data; } @@ -2680,27 +3682,95 @@ void CScriptVarFunction::setFunctionData(CScriptTokenDataFnc *Data) { if(data) { data->unref(); data = 0; } if(Data) { data = Data; data->ref(); - addChildNoDup("length", newScriptVar((int)data->arguments.size()), 0); + addChildOrReplace("length", newScriptVar((int)data->arguments.size()), 0); + // can not add "name" here because name is a StingVar with length as getter + // length-getter is a function with a function name -> endless recursion + //addChildNoDup("name", newScriptVar(data->name), 0); } } -////////////////////////////////////////////////////////////////////////// CScriptVarFunctionNative +////////////////////////////////////////////////////////////////////////// +/// CScriptVarFunctionBounded +////////////////////////////////////////////////////////////////////////// + +CScriptVarFunctionBounded::CScriptVarFunctionBounded(CScriptVarFunctionPtr BoundedFunction, CScriptVarPtr BoundedThis, const std::vector &BoundedArguments) + : CScriptVarFunction(BoundedFunction->getContext(), new CScriptTokenDataFnc) , + boundedFunction(BoundedFunction), + boundedThis(BoundedThis), + boundedArguments(BoundedArguments) { + getFunctionData()->name = BoundedFunction->getFunctionData()->name; +} +CScriptVarFunctionBounded::~CScriptVarFunctionBounded(){} +CScriptVarPtr CScriptVarFunctionBounded::clone() { return new CScriptVarFunctionBounded(*this); } +bool CScriptVarFunctionBounded::isBounded() { return true; } +void CScriptVarFunctionBounded::setTemporaryID_recursive( uint32_t ID ) { + CScriptVarFunction::setTemporaryID_recursive(ID); + boundedThis->setTemporaryID_recursive(ID); + for(vector::iterator it=boundedArguments.begin(); it!=boundedArguments.end(); ++it) + (*it)->setTemporaryID_recursive(ID); +} + +CScriptVarPtr CScriptVarFunctionBounded::callFunction( bool &execute, vector &Arguments, const CScriptVarPtr &This, CScriptVarPtr *newThis/*=0*/ ) +{ + vector newArgs=boundedArguments; + newArgs.insert(newArgs.end(), Arguments.begin(), Arguments.end()); + return context->callFunction(execute, boundedFunction, newArgs, newThis ? This : boundedThis, newThis); +} + + +////////////////////////////////////////////////////////////////////////// +/// CScriptVarFunctionNative +////////////////////////////////////////////////////////////////////////// CScriptVarFunctionNative::~CScriptVarFunctionNative() {} bool CScriptVarFunctionNative::isNative() { return true; } -string CScriptVarFunctionNative::getString() {return "[ Function Native ]";} -////////////////////////////////////////////////////////////////////////// CScriptVarFunctionNativeCallback +////////////////////////////////////////////////////////////////////////// +/// CScriptVarFunctionNativeCallback +////////////////////////////////////////////////////////////////////////// CScriptVarFunctionNativeCallback::~CScriptVarFunctionNativeCallback() {} CScriptVarPtr CScriptVarFunctionNativeCallback::clone() { return new CScriptVarFunctionNativeCallback(*this); } void CScriptVarFunctionNativeCallback::callFunction(const CFunctionsScopePtr &c) { jsCallback(c, jsUserData); } -////////////////////////////////////////////////////////////////////////// CScriptVarScope +////////////////////////////////////////////////////////////////////////// +/// CScriptVarAccessor +////////////////////////////////////////////////////////////////////////// + +declare_dummy_t(Accessor); +CScriptVarAccessor::CScriptVarAccessor(CTinyJS *Context) : CScriptVarObject(Context, Context->objectPrototype) { } +CScriptVarAccessor::CScriptVarAccessor(CTinyJS *Context, JSCallback getterFnc, void *getterData, JSCallback setterFnc, void *setterData) + : CScriptVarObject(Context) +{ + if(getterFnc) + addChild(TINYJS_ACCESSOR_GET_VAR, ::newScriptVar(Context, getterFnc, getterData), 0); + if(setterFnc) + addChild(TINYJS_ACCESSOR_SET_VAR, ::newScriptVar(Context, setterFnc, setterData), 0); +} + +CScriptVarAccessor::CScriptVarAccessor( CTinyJS *Context, const CScriptVarFunctionPtr &getter, const CScriptVarFunctionPtr &setter) : CScriptVarObject(Context, Context->objectPrototype) { + if(getter) + addChild(TINYJS_ACCESSOR_GET_VAR, getter, 0); + if(setter) + addChild(TINYJS_ACCESSOR_SET_VAR, setter, 0); +} + +CScriptVarAccessor::~CScriptVarAccessor() {} +CScriptVarPtr CScriptVarAccessor::clone() { return new CScriptVarAccessor(*this); } +bool CScriptVarAccessor::isAccessor() { return true; } +bool CScriptVarAccessor::isPrimitive() { return false; } +string CScriptVarAccessor::getParsableString(const string &indentString, const string &indent, uint32_t uniqueID, bool &hasRecursion) { + return ""; +} +string CScriptVarAccessor::getVarType() { return "accessor"; } + +////////////////////////////////////////////////////////////////////////// +/// CScriptVarScope +////////////////////////////////////////////////////////////////////////// declare_dummy_t(Scope); CScriptVarScope::~CScriptVarScope() {} @@ -2708,18 +3778,20 @@ CScriptVarPtr CScriptVarScope::clone() { return CScriptVarPtr(); } bool CScriptVarScope::isObject() { return false; } CScriptVarPtr CScriptVarScope::scopeVar() { return this; } ///< to create var like: var a = ... CScriptVarPtr CScriptVarScope::scopeLet() { return this; } ///< to create var like: let a = ... -CScriptVarLink *CScriptVarScope::findInScopes(const string &childName) { +CScriptVarLinkWorkPtr CScriptVarScope::findInScopes(const string &childName) { return CScriptVar::findChild(childName); } CScriptVarScopePtr CScriptVarScope::getParent() { return CScriptVarScopePtr(); } ///< no Parent -////////////////////////////////////////////////////////////////////////// CScriptVarScopeFnc +////////////////////////////////////////////////////////////////////////// +/// CScriptVarScopeFnc +////////////////////////////////////////////////////////////////////////// declare_dummy_t(ScopeFnc); CScriptVarScopeFnc::~CScriptVarScopeFnc() {} -CScriptVarLink *CScriptVarScopeFnc::findInScopes(const string &childName) { - CScriptVarLink * ret = findChild(childName); +CScriptVarLinkWorkPtr CScriptVarScopeFnc::findInScopes(const string &childName) { + CScriptVarLinkWorkPtr ret = findChild(childName); if( !ret ) { if(closure) ret = CScriptVarScopePtr(closure)->findInScopes(childName); else ret = context->getRoot()->findChild(childName); @@ -2728,7 +3800,7 @@ CScriptVarLink *CScriptVarScopeFnc::findInScopes(const string &childName) { } void CScriptVarScopeFnc::setReturnVar(const CScriptVarPtr &var) { - addChildNoDup(TINYJS_RETURN_VAR, var); + addChildOrReplace(TINYJS_RETURN_VAR, var); } CScriptVarPtr CScriptVarScopeFnc::getParameter(const string &name) { @@ -2742,53 +3814,82 @@ CScriptVarPtr CScriptVarScopeFnc::getArgument(const string &name) { return findChildOrCreate(name); } CScriptVarPtr CScriptVarScopeFnc::getArgument(int Idx) { - CScriptVarLink *arguments = findChildOrCreate(TINYJS_ARGUMENTS_VAR); - if(arguments) arguments = (*arguments)->findChild(int2string(Idx)); - return arguments ? arguments : constScriptVar(Undefined); + CScriptVarLinkPtr arguments = findChildOrCreate(TINYJS_ARGUMENTS_VAR); + if(arguments) arguments = arguments->getVarPtr()->findChild(int2string(Idx)); + return arguments ? arguments->getVarPtr() : constScriptVar(Undefined); } int CScriptVarScopeFnc::getParameterLength() { return getArgumentsLength(); } int CScriptVarScopeFnc::getArgumentsLength() { - CScriptVarLink *arguments = findChild(TINYJS_ARGUMENTS_VAR); - if(arguments) arguments = (*arguments)->findChild("length"); - return arguments ? (*arguments)->getInt() : 0; + CScriptVarLinkPtr arguments = findChild(TINYJS_ARGUMENTS_VAR); + if(arguments) arguments = arguments->getVarPtr()->findChild("length"); + return arguments ? arguments.getter()->toNumber().toInt32() : 0; } +void CScriptVarScopeFnc::throwError( ERROR_TYPES ErrorType, const string &message ) { + throw newScriptVarError(context, ErrorType, message.c_str()); +} -////////////////////////////////////////////////////////////////////////// CScriptVarScopeLet +////////////////////////////////////////////////////////////////////////// +/// CScriptVarScopeLet +////////////////////////////////////////////////////////////////////////// declare_dummy_t(ScopeLet); CScriptVarScopeLet::CScriptVarScopeLet(const CScriptVarScopePtr &Parent) // constructor for LetScope - : CScriptVarScope(Parent->getContext()), parent(addChild(TINYJS_SCOPE_PARENT_VAR, Parent, 0)) {} + : CScriptVarScope(Parent->getContext()), parent(addChild(TINYJS_SCOPE_PARENT_VAR, Parent, 0)) + , letExpressionInitMode(false) {} CScriptVarScopeLet::~CScriptVarScopeLet() {} CScriptVarPtr CScriptVarScopeLet::scopeVar() { // to create var like: var a = ... return getParent()->scopeVar(); } CScriptVarScopePtr CScriptVarScopeLet::getParent() { return (CScriptVarPtr)parent; } -CScriptVarLink *CScriptVarScopeLet::findInScopes(const string &childName) { - CScriptVarLink *ret = findChild(childName); - if( !ret ) ret = getParent()->findInScopes(childName); +CScriptVarLinkWorkPtr CScriptVarScopeLet::findInScopes(const string &childName) { + CScriptVarLinkWorkPtr ret; + if(letExpressionInitMode) { + return getParent()->findInScopes(childName); + } else { + ret = findChild(childName); + if( !ret ) ret = getParent()->findInScopes(childName); + } return ret; } -////////////////////////////////////////////////////////////////////////// CScriptVarScopeWith + +////////////////////////////////////////////////////////////////////////// +/// CScriptVarScopeWith +////////////////////////////////////////////////////////////////////////// declare_dummy_t(ScopeWith); CScriptVarScopeWith::~CScriptVarScopeWith() {} CScriptVarPtr CScriptVarScopeWith::scopeLet() { // to create var like: let a = ... return getParent()->scopeLet(); } -CScriptVarLink *CScriptVarScopeWith::findInScopes(const string &childName) { +CScriptVarLinkWorkPtr CScriptVarScopeWith::findInScopes(const string &childName) { if(childName == "this") return with; - CScriptVarLink * ret = (*with)->findChild(childName); + CScriptVarLinkWorkPtr ret = with->getVarPtr()->findChild(childName); + if( !ret ) { + ret = with->getVarPtr()->findChildInPrototypeChain(childName); + if(ret) { + ret(ret->getVarPtr(), ret->getName()); // recreate ret + ret.setReferencedOwner(with->getVarPtr()); // fake referenced Owner + } + } if( !ret ) ret = getParent()->findInScopes(childName); return ret; } -// ----------------------------------------------------------------------------------- CSCRIPT + +////////////////////////////////////////////////////////////////////////// +/// CTinyJS +////////////////////////////////////////////////////////////////////////// + +extern "C" void _registerFunctions(CTinyJS *tinyJS); +extern "C" void _registerStringFunctions(CTinyJS *tinyJS); +extern "C" void _registerMathFunctions(CTinyJS *tinyJS); + bool CTinyJS::noexecute = false; CTinyJS::CTinyJS() { CScriptVarPtr var; @@ -2821,38 +3922,66 @@ CTinyJS::CTinyJS() { // Add built-in classes ////////////////////////////////////////////////////////////////////////// // Object - var = addNative("function Object()", this, &CTinyJS::native_Object); + var = addNative("function Object()", this, &CTinyJS::native_Object, 0, SCRIPTVARLINK_CONSTANT); objectPrototype = var->findChild(TINYJS_PROTOTYPE_CLASS); addNative("function Object.getPrototypeOf(obj)", this, &CTinyJS::native_Object_getPrototypeOf); - addNative("function Object.prototype.hasOwnProperty(prop)", this, &CTinyJS::native_Object_hasOwnProperty); - objectPrototype_valueOf = addNative("function Object.prototype.valueOf()", this, &CTinyJS::native_Object_valueOf); - objectPrototype_toString = addNative("function Object.prototype.toString(radix)", this, &CTinyJS::native_Object_toString); + addNative("function Object.preventExtensions(obj)", this, &CTinyJS::native_Object_setObjectSecure); + addNative("function Object.isExtensible(obj)", this, &CTinyJS::native_Object_isSecureObject); + addNative("function Object.seel(obj)", this, &CTinyJS::native_Object_setObjectSecure, (void*)1); + addNative("function Object.isSealed(obj)", this, &CTinyJS::native_Object_isSecureObject, (void*)1); + addNative("function Object.freeze(obj)", this, &CTinyJS::native_Object_setObjectSecure, (void*)2); + addNative("function Object.isFrozen(obj)", this, &CTinyJS::native_Object_isSecureObject, (void*)2); + addNative("function Object.keys(obj)", this, &CTinyJS::native_Object_keys); + addNative("function Object.getOwnPropertyNames(obj)", this, &CTinyJS::native_Object_keys, (void*)1); + addNative("function Object.getOwnPropertyDescriptor(obj,name)", this, &CTinyJS::native_Object_getOwnPropertyDescriptor); + addNative("function Object.defineProperty(obj,name,attributes)", this, &CTinyJS::native_Object_defineProperty); + addNative("function Object.defineProperties(obj,properties)", this, &CTinyJS::native_Object_defineProperties); + addNative("function Object.create(obj,properties)", this, &CTinyJS::native_Object_defineProperties, (void*)1); + + addNative("function Object.prototype.hasOwnProperty(prop)", this, &CTinyJS::native_Object_prototype_hasOwnProperty); + objectPrototype_valueOf = addNative("function Object.prototype.valueOf()", this, &CTinyJS::native_Object_prototype_valueOf); + objectPrototype_toString = addNative("function Object.prototype.toString(radix)", this, &CTinyJS::native_Object_prototype_toString); pseudo_refered.push_back(&objectPrototype); pseudo_refered.push_back(&objectPrototype_valueOf); pseudo_refered.push_back(&objectPrototype_toString); ////////////////////////////////////////////////////////////////////////// // Array - var = addNative("function Array()", this, &CTinyJS::native_Array); + var = addNative("function Array()", this, &CTinyJS::native_Array, 0, SCRIPTVARLINK_CONSTANT); arrayPrototype = var->findChild(TINYJS_PROTOTYPE_CLASS); arrayPrototype->addChild("valueOf", objectPrototype_valueOf); arrayPrototype->addChild("toString", objectPrototype_toString); pseudo_refered.push_back(&arrayPrototype); - + var = addNative("function Array.__constructor__()", this, &CTinyJS::native_Array, (void*)1, SCRIPTVARLINK_CONSTANT); + var->getFunctionData()->name = "Array"; ////////////////////////////////////////////////////////////////////////// // String - var = addNative("function String()", this, &CTinyJS::native_String); + var = addNative("function String()", this, &CTinyJS::native_String, 0, SCRIPTVARLINK_CONSTANT); stringPrototype = var->findChild(TINYJS_PROTOTYPE_CLASS); stringPrototype->addChild("valueOf", objectPrototype_valueOf); stringPrototype->addChild("toString", objectPrototype_toString); pseudo_refered.push_back(&stringPrototype); + var = addNative("function String.__constructor__()", this, &CTinyJS::native_String, (void*)1, SCRIPTVARLINK_CONSTANT); + var->getFunctionData()->name = "String"; + + ////////////////////////////////////////////////////////////////////////// + // RegExp +#ifndef NO_REGEXP + var = addNative("function RegExp()", this, &CTinyJS::native_RegExp, 0, SCRIPTVARLINK_CONSTANT); + regexpPrototype = var->findChild(TINYJS_PROTOTYPE_CLASS); + regexpPrototype->addChild("valueOf", objectPrototype_valueOf); + regexpPrototype->addChild("toString", objectPrototype_toString); + pseudo_refered.push_back(®expPrototype); +#endif /* NO_REGEXP */ ////////////////////////////////////////////////////////////////////////// // Number - var = addNative("function Number()", this, &CTinyJS::native_Number); - var->addChild("NaN", constNaN = newScriptVarNaN(this), SCRIPTVARLINK_ENUMERABLE); - var->addChild("POSITIVE_INFINITY", constInfinityPositive = newScriptVarInfinity(this, InfinityPositive), SCRIPTVARLINK_ENUMERABLE); - var->addChild("NEGATIVE_INFINITY", constInfinityNegative = newScriptVarInfinity(this, InfinityNegative), SCRIPTVARLINK_ENUMERABLE); + var = addNative("function Number()", this, &CTinyJS::native_Number, 0, SCRIPTVARLINK_CONSTANT); + var->addChild("NaN", constNaN = newScriptVarNumber(this, NaN), SCRIPTVARLINK_CONSTANT); + var->addChild("MAX_VALUE", constInfinityPositive = newScriptVarNumber(this, numeric_limits::max()), SCRIPTVARLINK_CONSTANT); + var->addChild("MIN_VALUE", constInfinityPositive = newScriptVarNumber(this, numeric_limits::min()), SCRIPTVARLINK_CONSTANT); + var->addChild("POSITIVE_INFINITY", constInfinityPositive = newScriptVarNumber(this, InfinityPositive), SCRIPTVARLINK_CONSTANT); + var->addChild("NEGATIVE_INFINITY", constInfinityNegative = newScriptVarNumber(this, InfinityNegative), SCRIPTVARLINK_CONSTANT); numberPrototype = var->findChild(TINYJS_PROTOTYPE_CLASS); numberPrototype->addChild("valueOf", objectPrototype_valueOf); numberPrototype->addChild("toString", objectPrototype_toString); @@ -2860,28 +3989,33 @@ CTinyJS::CTinyJS() { pseudo_refered.push_back(&constNaN); pseudo_refered.push_back(&constInfinityPositive); pseudo_refered.push_back(&constInfinityNegative); + var = addNative("function Number.__constructor__()", this, &CTinyJS::native_Number, (void*)1, SCRIPTVARLINK_CONSTANT); + var->getFunctionData()->name = "Number"; ////////////////////////////////////////////////////////////////////////// // Boolean - var = addNative("function Boolean()", this, &CTinyJS::native_Boolean); + var = addNative("function Boolean()", this, &CTinyJS::native_Boolean, 0, SCRIPTVARLINK_CONSTANT); booleanPrototype = var->findChild(TINYJS_PROTOTYPE_CLASS); booleanPrototype->addChild("valueOf", objectPrototype_valueOf); booleanPrototype->addChild("toString", objectPrototype_toString); pseudo_refered.push_back(&booleanPrototype); + var = addNative("function Boolean.__constructor__()", this, &CTinyJS::native_Boolean, (void*)1, SCRIPTVARLINK_CONSTANT); + var->getFunctionData()->name = "Boolean"; ////////////////////////////////////////////////////////////////////////// // Function - var = addNative("function Function(params, body)", this, &CTinyJS::native_Function); - var->addChildNoDup(TINYJS_PROTOTYPE_CLASS, functionPrototype); - addNative("function Function.prototype.call(objc)", this, &CTinyJS::native_Function_call); - addNative("function Function.prototype.apply(objc, args)", this, &CTinyJS::native_Function_apply); + var = addNative("function Function(params, body)", this, &CTinyJS::native_Function, 0, SCRIPTVARLINK_CONSTANT); + var->addChildOrReplace(TINYJS_PROTOTYPE_CLASS, functionPrototype); + addNative("function Function.prototype.call(objc)", this, &CTinyJS::native_Function_prototype_call); + addNative("function Function.prototype.apply(objc, args)", this, &CTinyJS::native_Function_prototype_apply); + addNative("function Function.prototype.bind(objc, args)", this, &CTinyJS::native_Function_prototype_bind); functionPrototype->addChild("valueOf", objectPrototype_valueOf); functionPrototype->addChild("toString", objectPrototype_toString); pseudo_refered.push_back(&functionPrototype); ////////////////////////////////////////////////////////////////////////// // Error - var = addNative("function Error(message, fileName, lineNumber, column)", this, &CTinyJS::native_Error); + var = addNative("function Error(message, fileName, lineNumber, column)", this, &CTinyJS::native_Error, 0, SCRIPTVARLINK_CONSTANT); errorPrototypes[Error] = var->findChild(TINYJS_PROTOTYPE_CLASS); errorPrototypes[Error]->addChild("message", newScriptVar("")); errorPrototypes[Error]->addChild("name", newScriptVar("Error")); @@ -2889,41 +4023,41 @@ CTinyJS::CTinyJS() { errorPrototypes[Error]->addChild("lineNumber", newScriptVar(-1)); // -1 means not viable errorPrototypes[Error]->addChild("column", newScriptVar(-1)); // -1 means not viable - var = addNative("function EvalError(message, fileName, lineNumber, column)", this, &CTinyJS::native_EvalError); + var = addNative("function EvalError(message, fileName, lineNumber, column)", this, &CTinyJS::native_EvalError, 0, SCRIPTVARLINK_CONSTANT); errorPrototypes[EvalError] = var->findChild(TINYJS_PROTOTYPE_CLASS); - errorPrototypes[EvalError]->addChildNoDup(TINYJS___PROTO___VAR, errorPrototypes[Error], SCRIPTVARLINK_WRITABLE); + errorPrototypes[EvalError]->addChildOrReplace(TINYJS___PROTO___VAR, errorPrototypes[Error], SCRIPTVARLINK_WRITABLE); errorPrototypes[EvalError]->addChild("name", newScriptVar("EvalError")); - var = addNative("function RangeError(message, fileName, lineNumber, column)", this, &CTinyJS::native_RangeError); + var = addNative("function RangeError(message, fileName, lineNumber, column)", this, &CTinyJS::native_RangeError, 0, SCRIPTVARLINK_CONSTANT); errorPrototypes[RangeError] = var->findChild(TINYJS_PROTOTYPE_CLASS); - errorPrototypes[RangeError]->addChildNoDup(TINYJS___PROTO___VAR, errorPrototypes[Error], SCRIPTVARLINK_WRITABLE); + errorPrototypes[RangeError]->addChildOrReplace(TINYJS___PROTO___VAR, errorPrototypes[Error], SCRIPTVARLINK_WRITABLE); errorPrototypes[RangeError]->addChild("name", newScriptVar("RangeError")); - var = addNative("function ReferenceError(message, fileName, lineNumber, column)", this, &CTinyJS::native_ReferenceError); + var = addNative("function ReferenceError(message, fileName, lineNumber, column)", this, &CTinyJS::native_ReferenceError, 0, SCRIPTVARLINK_CONSTANT); errorPrototypes[ReferenceError] = var->findChild(TINYJS_PROTOTYPE_CLASS); - errorPrototypes[ReferenceError]->addChildNoDup(TINYJS___PROTO___VAR, errorPrototypes[Error], SCRIPTVARLINK_WRITABLE); + errorPrototypes[ReferenceError]->addChildOrReplace(TINYJS___PROTO___VAR, errorPrototypes[Error], SCRIPTVARLINK_WRITABLE); errorPrototypes[ReferenceError]->addChild("name", newScriptVar("ReferenceError")); - var = addNative("function SyntaxError(message, fileName, lineNumber, column)", this, &CTinyJS::native_SyntaxError); + var = addNative("function SyntaxError(message, fileName, lineNumber, column)", this, &CTinyJS::native_SyntaxError, 0, SCRIPTVARLINK_CONSTANT); errorPrototypes[SyntaxError] = var->findChild(TINYJS_PROTOTYPE_CLASS); - errorPrototypes[SyntaxError]->addChildNoDup(TINYJS___PROTO___VAR, errorPrototypes[Error], SCRIPTVARLINK_WRITABLE); + errorPrototypes[SyntaxError]->addChildOrReplace(TINYJS___PROTO___VAR, errorPrototypes[Error], SCRIPTVARLINK_WRITABLE); errorPrototypes[SyntaxError]->addChild("name", newScriptVar("SyntaxError")); - var = addNative("function TypeError(message, fileName, lineNumber, column)", this, &CTinyJS::native_TypeError); + var = addNative("function TypeError(message, fileName, lineNumber, column)", this, &CTinyJS::native_TypeError, 0, SCRIPTVARLINK_CONSTANT); errorPrototypes[TypeError] = var->findChild(TINYJS_PROTOTYPE_CLASS); - errorPrototypes[TypeError]->addChildNoDup(TINYJS___PROTO___VAR, errorPrototypes[Error], SCRIPTVARLINK_WRITABLE); + errorPrototypes[TypeError]->addChildOrReplace(TINYJS___PROTO___VAR, errorPrototypes[Error], SCRIPTVARLINK_WRITABLE); errorPrototypes[TypeError]->addChild("name", newScriptVar("TypeError")); ////////////////////////////////////////////////////////////////////////// // add global built-in vars & constants - root->addChild("undefined", constUndefined = newScriptVarUndefined(this), SCRIPTVARLINK_ENUMERABLE); + root->addChild("undefined", constUndefined = newScriptVarUndefined(this), SCRIPTVARLINK_CONSTANT); pseudo_refered.push_back(&constUndefined); - root->addChild("NaN", constNaN, SCRIPTVARLINK_ENUMERABLE); - root->addChild("Infinity", constInfinityPositive, SCRIPTVARLINK_ENUMERABLE); + constNull = newScriptVarNull(this); pseudo_refered.push_back(&constNull); + root->addChild("NaN", constNaN, SCRIPTVARLINK_CONSTANT); + root->addChild("Infinity", constInfinityPositive, SCRIPTVARLINK_CONSTANT); + constNegativZero = newScriptVarNumber(this, NegativeZero); pseudo_refered.push_back(&constNegativZero); constFalse = newScriptVarBool(this, false); pseudo_refered.push_back(&constFalse); constTrue = newScriptVarBool(this, true); pseudo_refered.push_back(&constTrue); - constZero = newScriptVar(0); pseudo_refered.push_back(&constZero); - constOne = newScriptVar(1); pseudo_refered.push_back(&constOne); ////////////////////////////////////////////////////////////////////////// // add global functions addNative("function eval(jsCode)", this, &CTinyJS::native_eval); @@ -2935,6 +4069,9 @@ CTinyJS::CTinyJS() { addNative("function JSON.parse(text, reviver)", this, &CTinyJS::native_JSON_parse); + _registerFunctions(this); + _registerStringFunctions(this); + _registerMathFunctions(this); } CTinyJS::~CTinyJS() { @@ -2960,7 +4097,7 @@ CTinyJS::~CTinyJS() { /// throws an Error & Exception ////////////////////////////////////////////////////////////////////////// -void CTinyJS::throwError(bool &execute, ERROR_TYPES ErrorType, const std::string &message ) { +void CTinyJS::throwError(bool &execute, ERROR_TYPES ErrorType, const string &message ) { if(execute && (runtimeFlags & RUNTIME_CAN_THROW)) { exceptionVar = newScriptVarError(this, ErrorType, message.c_str(), t->currentFile.c_str(), t->currentLine(), t->currentColumn()); runtimeFlags |= RUNTIME_THROW; @@ -2969,21 +4106,21 @@ void CTinyJS::throwError(bool &execute, ERROR_TYPES ErrorType, const std::string } throw new CScriptException(ErrorType, message, t->currentFile, t->currentLine(), t->currentColumn()); } -void CTinyJS::throwException(ERROR_TYPES ErrorType, const std::string &message ) { +void CTinyJS::throwException(ERROR_TYPES ErrorType, const string &message ) { throw new CScriptException(ErrorType, message, t->currentFile, t->currentLine(), t->currentColumn()); } -void CTinyJS::throwError(bool &execute, ERROR_TYPES ErrorType, const std::string &message, CScriptTokenizer::ScriptTokenPosition &Pos ){ +void CTinyJS::throwError(bool &execute, ERROR_TYPES ErrorType, const string &message, CScriptTokenizer::ScriptTokenPosition &Pos ){ if(execute && (runtimeFlags & RUNTIME_CAN_THROW)) { exceptionVar = newScriptVarError(this, ErrorType, message.c_str(), t->currentFile.c_str(), Pos.currentLine(), Pos.currentColumn()); runtimeFlags |= RUNTIME_THROW; execute = false; return; } - throw new CScriptException(message, t->currentFile, Pos.currentLine(), Pos.currentColumn()); + throw new CScriptException(ErrorType, message, t->currentFile, Pos.currentLine(), Pos.currentColumn()); } -void CTinyJS::throwException(ERROR_TYPES ErrorType, const std::string &message, CScriptTokenizer::ScriptTokenPosition &Pos ){ - throw new CScriptException(message, t->currentFile, Pos.currentLine(), Pos.currentColumn()); +void CTinyJS::throwException(ERROR_TYPES ErrorType, const string &message, CScriptTokenizer::ScriptTokenPosition &Pos ){ + throw new CScriptException(ErrorType, message, t->currentFile, Pos.currentLine(), Pos.currentColumn()); } ////////////////////////////////////////////////////////////////////////// @@ -3004,7 +4141,7 @@ void CTinyJS::execute(const string &Code, const string &File, int Line, int Colu evaluateComplex(Code, File, Line, Column); } -CScriptVarLink CTinyJS::evaluateComplex(CScriptTokenizer &Tokenizer) { +CScriptVarLinkPtr CTinyJS::evaluateComplex(CScriptTokenizer &Tokenizer) { CScriptVarLinkPtr v; t = &Tokenizer; try { @@ -3032,25 +4169,25 @@ CScriptVarLink CTinyJS::evaluateComplex(CScriptTokenizer &Tokenizer) { } if (v) { - return CScriptVarLink(v->getVarPtr()); + return CScriptVarLinkPtr(v->getVarPtr()); } // return undefined... - return CScriptVarLink(constScriptVar(Undefined)); + return CScriptVarLinkPtr(constScriptVar(Undefined)); } -CScriptVarLink CTinyJS::evaluateComplex(const char *Code, const string &File, int Line, int Column) { +CScriptVarLinkPtr CTinyJS::evaluateComplex(const char *Code, const string &File, int Line, int Column) { CScriptTokenizer Tokenizer(Code, File, Line, Column); return evaluateComplex(Tokenizer); } -CScriptVarLink CTinyJS::evaluateComplex(const string &Code, const string &File, int Line, int Column) { +CScriptVarLinkPtr CTinyJS::evaluateComplex(const string &Code, const string &File, int Line, int Column) { CScriptTokenizer Tokenizer(Code.c_str(), File, Line, Column); return evaluateComplex(Tokenizer); } string CTinyJS::evaluate(CScriptTokenizer &Tokenizer) { - return evaluateComplex(Tokenizer).getVarPtr()->getString(); + return evaluateComplex(Tokenizer)->toString(); } string CTinyJS::evaluate(const char *Code, const string &File, int Line, int Column) { - return evaluateComplex(Code, File, Line, Column).getVarPtr()->getString(); + return evaluateComplex(Code, File, Line, Column)->toString(); } string CTinyJS::evaluate(const string &Code, const string &File, int Line, int Column) { return evaluate(Code.c_str(), File, Line, Column); @@ -3070,7 +4207,7 @@ CScriptVarFunctionNativePtr CTinyJS::addNative(const string &funcDesc, CScriptVa /* Check for dots, we might want to do something like function String.substring ... */ while (lex.tk == '.') { lex.match('.'); - CScriptVarLink *link = base->findChild(funcName); + CScriptVarLinkPtr link = base->findChild(funcName); // if it doesn't exist, make an object class if (!link) link = base->addChild(funcName, newScriptVar(Object)); base = link->getVarPtr(); @@ -3079,9 +4216,10 @@ CScriptVarFunctionNativePtr CTinyJS::addNative(const string &funcDesc, CScriptVa } auto_ptr pFunctionData(new CScriptTokenDataFnc); + pFunctionData->name = funcName; lex.match('('); while (lex.tk!=')') { - pFunctionData->arguments.push_back(lex.tkStr); + pFunctionData->arguments.push_back(CScriptToken(LEX_ID, lex.tkStr)); lex.match(LEX_ID); if (lex.tk!=')') lex.match(',',')'); } @@ -3094,52 +4232,77 @@ CScriptVarFunctionNativePtr CTinyJS::addNative(const string &funcDesc, CScriptVa } -CScriptVarLinkPtr CTinyJS::parseFunctionDefinition(CScriptToken &FncToken) { - CScriptTokenDataFnc &Fnc = FncToken.Fnc(); - string fncName = (FncToken.token == LEX_T_FUNCTION_OPERATOR) ? TINYJS_TEMP_NAME : Fnc.name; - CScriptVarLinkPtr funcVar(newScriptVar(&Fnc), fncName); +CScriptVarLinkWorkPtr CTinyJS::parseFunctionDefinition(const CScriptToken &FncToken) { + const CScriptTokenDataFnc &Fnc = FncToken.Fnc(); +// string fncName = (FncToken.token == LEX_T_FUNCTION_OPERATOR) ? TINYJS_TEMP_NAME : Fnc.name; + CScriptVarLinkWorkPtr funcVar(newScriptVar((CScriptTokenDataFnc*)&Fnc), Fnc.name); if(scope() != root) - (*funcVar)->addChild(TINYJS_FUNCTION_CLOSURE_VAR, scope(), 0); - (*funcVar)->addChild(TINYJS_PROTOTYPE_CLASS, newScriptVar(Object), SCRIPTVARLINK_WRITABLE); + funcVar->getVarPtr()->addChild(TINYJS_FUNCTION_CLOSURE_VAR, scope(), 0); + funcVar->getVarPtr()->addChild(TINYJS_PROTOTYPE_CLASS, newScriptVar(Object), SCRIPTVARLINK_WRITABLE); return funcVar; } -CScriptVarLinkPtr CTinyJS::parseFunctionsBodyFromString(const string &ArgumentList, const string &FncBody) { +CScriptVarLinkWorkPtr CTinyJS::parseFunctionsBodyFromString(const string &ArgumentList, const string &FncBody) { string Fnc = "function ("+ArgumentList+"){"+FncBody+"}"; CScriptTokenizer tokenizer(Fnc.c_str()); return parseFunctionDefinition(tokenizer.getToken()); } +CScriptVarPtr CTinyJS::callFunction(const CScriptVarFunctionPtr &Function, vector &Arguments, const CScriptVarPtr &This, CScriptVarPtr *newThis) { + bool execute=true; + CScriptVarPtr retVar = callFunction(execute, Function, Arguments, This, newThis); + if(!execute) throw exceptionVar; + return retVar; +} -CScriptVarPtr CTinyJS::callFunction(bool &execute, const CScriptVarFunctionPtr &Function, std::vector &Arguments, const CScriptVarPtr &This, CScriptVarPtr *newThis) { -//CScriptVarSmartLink CTinyJS::callFunction(CScriptVarSmartLink &Function, vector &Arguments, const CScriptVarPtr& This, bool &execute) { +CScriptVarPtr CTinyJS::callFunction(bool &execute, const CScriptVarFunctionPtr &Function, vector &Arguments, const CScriptVarPtr &This, CScriptVarPtr *newThis) { ASSERT(Function && Function->isFunction()); + if(Function->isBounded()) return CScriptVarFunctionBoundedPtr(Function)->callFunction(execute, Arguments, This, newThis); + + CScriptTokenDataFnc *Fnc = Function->getFunctionData(); CScriptVarScopeFncPtr functionRoot(::newScriptVar(this, ScopeFnc, CScriptVarPtr(Function->findChild(TINYJS_FUNCTION_CLOSURE_VAR)))); + if(Fnc->name.size()) functionRoot->addChild(Fnc->name, Function); functionRoot->addChild("this", This); CScriptVarPtr arguments = functionRoot->addChild(TINYJS_ARGUMENTS_VAR, newScriptVar(Object)); - CScriptTokenDataFnc *Fnc = Function->getFunctionData(); - if(Fnc->name.size()) functionRoot->addChild(Fnc->name, Function); int length_proto = Fnc->arguments.size(); int length_arguments = Arguments.size(); int length = max(length_proto, length_arguments); for(int arguments_idx = 0; arguments_idxaddChild(arguments_idx_str, Arguments[arguments_idx]); } else { value = constScriptVar(Undefined); } if(arguments_idx < length_proto) { - functionRoot->addChildNoDup(Fnc->arguments[arguments_idx], value); + CScriptToken &FncArguments = Fnc->arguments[arguments_idx]; + if(FncArguments.token == LEX_ID) + functionRoot->addChildOrReplace(FncArguments.String(), value); + else { + ASSERT(FncArguments.DestructuringVar().vars.size()>1); + vector Path(1, value); + for(DESTRUCTURING_VARS_it it=FncArguments.DestructuringVar().vars.begin()+1; it!=FncArguments.DestructuringVar().vars.end(); ++it) { + if(it->second == "}" || it->second == "]") + Path.pop_back(); + else { + if(it->second.empty()) continue; // skip empty entries + CScriptVarLinkWorkPtr var = Path.back()->findChild(it->first); + if(var) var = var.getter(execute); else var = constUndefined; + if(!execute) return constUndefined; + if(it->second == "{" || it->second == "[") { + Path.push_back(var); + } else + functionRoot->addChildOrReplace(it->second, var); + } + } + } } } arguments->addChild("length", newScriptVar(length_arguments)); - CScriptVarLinkPtr returnVar; + CScriptVarLinkWorkPtr returnVar; - int old_function_runtimeFlags = runtimeFlags; // save runtimeFlags - runtimeFlags &= ~RUNTIME_LOOP_MASK; // clear LOOP-Flags because we can't break or continue a loop from functions-body // execute function! // add the function's execute space to the symbol table so we can recurse CScopeControl ScopeControl(this); @@ -3147,7 +4310,6 @@ CScriptVarPtr CTinyJS::callFunction(bool &execute, const CScriptVarFunctionPtr & if (Function->isNative()) { try { CScriptVarFunctionNativePtr(Function)->callFunction(functionRoot); - runtimeFlags = old_function_runtimeFlags | (runtimeFlags & RUNTIME_THROW); // restore runtimeFlags if(runtimeFlags & RUNTIME_THROW) execute = false; } catch (CScriptVarPtr v) { @@ -3156,7 +4318,7 @@ CScriptVarPtr CTinyJS::callFunction(bool &execute, const CScriptVarFunctionPtr & execute = false; exceptionVar = v; } else - throw new CScriptException(SyntaxError, "uncaught exception: '"+v->getString()+"' in: native function '"+Function->getFunctionData()->name+"'"); + throw new CScriptException(SyntaxError, v->toString(execute)+"' in: native function '"+Function->getFunctionData()->name+"'"); } } else { /* we just want to execute the block, but something could @@ -3165,12 +4327,13 @@ CScriptVarPtr CTinyJS::callFunction(bool &execute, const CScriptVarFunctionPtr & string oldFile = t->currentFile; t->currentFile = Fnc->file; t->pushTokenScope(Fnc->body); - SET_RUNTIME_CANRETURN; - execute_block(execute); + if(Fnc->body.front().token == '{') + execute_block(execute, false); + else + functionRoot->addChildOrReplace(TINYJS_RETURN_VAR, execute_base(execute)); t->currentFile = oldFile; // because return will probably have called this, and set execute to false - runtimeFlags = old_function_runtimeFlags | (runtimeFlags & RUNTIME_THROW); // restore runtimeFlags if(!(runtimeFlags & RUNTIME_THROW)) { execute = true; } @@ -3184,73 +4347,32 @@ CScriptVarPtr CTinyJS::callFunction(bool &execute, const CScriptVarFunctionPtr & return constScriptVar(Undefined); } - -template -CScriptVarPtr DoMaths(CScriptVarPtr &a, CScriptVarPtr &b, int op) -{ - int dai = a->getInt();// use int when needed - int dbi = b->getInt(); - T da = (T)(a->isDouble()?a->getDouble():dai); - T db = (T)(b->isDouble()?b->getDouble():dbi); - switch (op) { - case '+': return a->newScriptVar(da+db); - case '-': return a->newScriptVar(da-db); - case '*': return a->newScriptVar(da*db); - case '/': - if(db==0) { - if(da==0) return a->constScriptVar(NaN); // 0/0 = NaN - else return a->constScriptVar(Infinity(da<0 ? -1 : 1)); // /0 = Infinity - } else return a->newScriptVar(da/db); - case '%': - if(db==0) return a->constScriptVar(NaN); // 0/0 = NaN - else return a->newScriptVar(dai%dbi); - case '&': return a->newScriptVar(dai&dbi); - case '|': return a->newScriptVar(dai|dbi); - case '^': return a->newScriptVar(dai^dbi); - case '~': return a->newScriptVar(~dai); - case LEX_LSHIFT: return a->newScriptVar(dai<newScriptVar(dai>>dbi); - case LEX_RSHIFTU: return a->newScriptVar((int)(((unsigned int)dai)>>dbi)); - case LEX_EQUAL: return a->constScriptVar(da==db); - case LEX_NEQUAL: return a->constScriptVar(da!=db); - case '<': return a->constScriptVar(daconstScriptVar(da<=db); - case '>': return a->constScriptVar(da>db); - case LEX_GEQUAL: return a->constScriptVar(da>=db); - default: throw new CScriptException("This operation not supported on the int datatype"); - } -} - -#define RETURN_NAN return constScriptVar(NaN) - CScriptVarPtr CTinyJS::mathsOp(bool &execute, const CScriptVarPtr &A, const CScriptVarPtr &B, int op) { if(!execute) return constUndefined; if (op == LEX_TYPEEQUAL || op == LEX_NTYPEEQUAL) { - // check type first, then call again to check data - if(A->isNaN() || B->isNaN()) return constFalse; - if( (typeid(*A.getVar()) == typeid(*B.getVar())) ^ (op != LEX_TYPEEQUAL) ) - return mathsOp(execute, A, B, op == LEX_TYPEEQUAL ? LEX_EQUAL : LEX_NEQUAL); - return constFalse; - } - if (!A->isPrimitive() && !B->isPrimitive()) { // Objects - /* Just check pointers */ + // check type first + if( (A->getVarType() == B->getVarType()) ^ (op == LEX_TYPEEQUAL)) return constFalse; + // check value second + return mathsOp(execute, A, B, op == LEX_TYPEEQUAL ? LEX_EQUAL : LEX_NEQUAL); + } + if (!A->isPrimitive() && !B->isPrimitive()) { // Objects both + // check pointers switch (op) { case LEX_EQUAL: return constScriptVar(A==B); case LEX_NEQUAL: return constScriptVar(A!=B); } } - CScriptVarPtr a = A->getPrimitivVar(execute); - CScriptVarPtr b = B->getPrimitivVar(execute); + CScriptVarPtr a = A->toPrimitive_hintNumber(execute); + CScriptVarPtr b = B->toPrimitive_hintNumber(execute); if(!execute) return constUndefined; // do maths... bool a_isString = a->isString(); bool b_isString = b->isString(); - // special for strings and string '+' // both a String or one a String and op='+' if( (a_isString && b_isString) || ((a_isString || b_isString) && op == '+')) { - string da = a->getString(); - string db = b->getString(); + string da = a->isNull() ? "" : a->toString(execute); + string db = b->isNull() ? "" : b->toString(execute); switch (op) { case '+': return newScriptVar(da+db); case LEX_EQUAL: return constScriptVar(da==db); @@ -3259,123 +4381,131 @@ CScriptVarPtr CTinyJS::mathsOp(bool &execute, const CScriptVarPtr &A, const CScr case LEX_LEQUAL: return constScriptVar(da<=db); case '>': return constScriptVar(da>db); case LEX_GEQUAL: return constScriptVar(da>=db); - default: RETURN_NAN; } } - // special for undefined and null + // special for undefined and null --> every true: undefined==undefined, undefined==null, null==undefined and null=null else if( (a->isUndefined() || a->isNull()) && (b->isUndefined() || b->isNull()) ) { switch (op) { - case LEX_NEQUAL: return constScriptVar( !( ( a->isUndefined() || a->isNull() ) && ( b->isUndefined() || b->isNull() ) ) ); - case LEX_EQUAL: return constScriptVar( ( a->isUndefined() || a->isNull() ) && ( b->isUndefined() || b->isNull() ) ); + case LEX_EQUAL: return constScriptVar(true); + case LEX_NEQUAL: case LEX_GEQUAL: case LEX_LEQUAL: case '<': case '>': return constScriptVar(false); - default: RETURN_NAN; } - } + } + CNumber da = a->toNumber(); + CNumber db = b->toNumber(); + switch (op) { + case '+': return a->newScriptVar(da+db); + case '-': return a->newScriptVar(da-db); + case '*': return a->newScriptVar(da*db); + case '/': return a->newScriptVar(da/db); + case '%': return a->newScriptVar(da%db); + case '&': return a->newScriptVar(da.toInt32()&db.toInt32()); + case '|': return a->newScriptVar(da.toInt32()|db.toInt32()); + case '^': return a->newScriptVar(da.toInt32()^db.toInt32()); + case '~': return a->newScriptVar(~da); + case LEX_LSHIFT: return a->newScriptVar(da<newScriptVar(da>>db); + case LEX_RSHIFTU: return a->newScriptVar(da.ushift(db)); + case LEX_EQUAL: return a->constScriptVar(da==db); + case LEX_NEQUAL: return a->constScriptVar(da!=db); + case '<': return a->constScriptVar(daconstScriptVar(da<=db); + case '>': return a->constScriptVar(da>db); + case LEX_GEQUAL: return a->constScriptVar(da>=db); + default: throw new CScriptException("This operation not supported on the int datatype"); + } +} - // gets only an Integer, a Double, in Infinity or a NaN - CScriptVarPtr a_l = a->getNumericVar(); - CScriptVarPtr b_l = b->getNumericVar(); - { - CScriptVarPtr a = a_l; - CScriptVarPtr b = b_l; - - if( a->isNaN() || b->isNaN() ) { - switch (op) { - case LEX_NEQUAL: return constScriptVar(true); - case LEX_EQUAL: - case LEX_GEQUAL: - case LEX_LEQUAL: - case '<': - case '>': return constScriptVar(false); - default: RETURN_NAN; - } - } - else if((a->isInfinity() || b->isInfinity())) { - int tmp = 0; - int a_i=a->isInfinity(), a_sig=a->getInt()>0?1:-1; - int b_i=b->isInfinity(), b_sig=a->getInt()>0?1:-1; - switch (op) { - case LEX_EQUAL: return constScriptVar(a_i == b_i); - case LEX_GEQUAL: - case '>': return constScriptVar(a_i >= b_i); - case LEX_LEQUAL: - case '<': return constScriptVar(a_i <= b_i); - case LEX_NEQUAL: return constScriptVar(a_i != b_i); - case '+': if(a_i && b_i && a_i != b_i) RETURN_NAN; - return constScriptVar(Infinity(b_i?b_i:a_i)); - case '-': if(a_i && a_i == b_i) RETURN_NAN; - return constScriptVar(Infinity(b_i?-b_i:a_i)); - case '*': tmp = a->getInt() * b->getInt(); - if(tmp == 0) RETURN_NAN; - return constScriptVar(Infinity(tmp)); - case '/': if(a_i && b_i) RETURN_NAN; - if(b_i) return newScriptVar(0); - return constScriptVar(Infinity(a_sig*b_sig)); - case '%': if(a_i) RETURN_NAN; - return constScriptVar(Infinity(a_sig)); - case '&': return newScriptVar( 0); - case '|': - case '^': if(a_i && b_i) return newScriptVar( 0); - return newScriptVar(a_i?b->getInt():a->getInt()); - case LEX_LSHIFT: - case LEX_RSHIFT: - case LEX_RSHIFTU: if(a_i) return newScriptVar(0); - return newScriptVar(a->getInt()); - default: throw new CScriptException("This operation not supported on the int datatype"); +void CTinyJS::execute_var_init( bool hideLetScope, bool &execute ) +{ + for(;;) { + if(t->tk == LEX_T_DESTRUCTURING_VAR) { + DESTRUCTURING_VARS_t &vars = t->getToken().DestructuringVar().vars; + t->match(LEX_T_DESTRUCTURING_VAR); + t->match('='); + if(hideLetScope) CScriptVarScopeLetPtr(scope())->setletExpressionInitMode(true); + vector Path(1, execute_assignment(execute)); + if(hideLetScope) CScriptVarScopeLetPtr(scope())->setletExpressionInitMode(false); + for(DESTRUCTURING_VARS_it it=vars.begin()+1; execute && it!=vars.end(); ++it) { + if(it->second == "}" || it->second == "]") + Path.pop_back(); + else { + if(it->second.empty()) continue; // skip empty entries + CScriptVarLinkWorkPtr var = Path.back()->findChild(it->first); + if(var) var = var.getter(execute); else var = constUndefined; + if(!execute) break; + if(it->second == "{" || it->second == "[") { + Path.push_back(var); + } else { + CScriptVarLinkWorkPtr v(findInScopes(it->second)); + ASSERT(v==true); + if(v) v.setter(execute, var); + } + } } } else { - if (!a->isDouble() && !b->isDouble()) { - // use ints - return DoMaths(a, b, op); - } else { - // use doubles - return DoMaths(a, b, op); + string name = t->tkStr(); + t->match(LEX_ID); + CScriptVarPtr var; + if (t->tk == '=') { + t->match('='); + CScriptVarLinkWorkPtr v(findInScopes(name)); + ASSERT(v==true); + if(hideLetScope) CScriptVarScopeLetPtr(scope())->setletExpressionInitMode(true); + if(v) v.setter(execute, execute_assignment(execute)); + if(hideLetScope) CScriptVarScopeLetPtr(scope())->setletExpressionInitMode(false); } } + if (t->tk == ',') + t->match(','); + else + break; } - - ASSERT(0); - return constUndefined; } - -/* -CScriptVarSmartLink CTinyJS::setValue( CScriptVarSmartLink Var ) -{ - if((*Var)->isAccessor()) { - +void CTinyJS::execute_destructuring(CScriptTokenDataObjectLiteral &Objc, const CScriptVarPtr &Val, bool &execute) { + for(vector::iterator it=Objc.elements.begin(); execute && it!=Objc.elements.end(); ++it) { + if(it->value.empty()) continue; + CScriptVarPtr rhs = Val->findChild(it->id).getter(execute); + if(it->value.front().token == LEX_T_OBJECT_LITERAL && it->value.front().Object().destructuring) { + execute_destructuring(it->value.front().Object(), rhs, execute); + } else { + t->pushTokenScope(it->value); + CScriptVarLinkWorkPtr lhs = execute_condition(execute); + if (!lhs->isOwned()) { + CScriptVarPtr Owner = lhs.getReferencedOwner(); + if(Owner) { + if(!lhs.getReferencedOwner()->isExtensible()) + continue; + lhs = Owner->addChildOrReplace(lhs->getName(), lhs); + } else + lhs = root->addChildOrReplace(lhs->getName(), lhs); + } + lhs.setter(execute, rhs); + } } } -CScriptVarSmartLink CTinyJS::getValue( CScriptVarSmartLink Var, bool execute ) -{ - if((*Var)->isAccessor()) { - CScriptVarLink *get = (*Var)->findChild("__accessor_get__"); - if(get) { - } else - return CScriptVarSmartLink(new CScriptVar()); - } else - return Var; -} -*/ -CScriptVarLinkPtr CTinyJS::execute_literals(bool &execute) { +CScriptVarLinkWorkPtr CTinyJS::execute_literals(bool &execute) { switch(t->tk) { case LEX_ID: if(execute) { - CScriptVarLinkPtr a; - a(execute, findInScopes(t->tkStr())); + CScriptVarLinkWorkPtr a(findInScopes(t->tkStr())); if (!a) { /* Variable doesn't exist! JavaScript says we should create it * (we won't add it here. This is done in the assignment operator)*/ - if(t->tkStr() == "this") { + if(t->tkStr() == "this") a = root; // fake this - } else { - a(constScriptVar(Undefined), t->tkStr()); - } - } else if(t->tkStr() == "this") + else + a = CScriptVarLinkPtr(constScriptVar(Undefined), t->tkStr()); + } +/* + prvention for assignment to this is now done by the tokenizer + else if(t->tkStr() == "this") a(a->getVarPtr()); // prevent assign to this +*/ t->match(LEX_ID); return a; } @@ -3384,6 +4514,7 @@ CScriptVarLinkPtr CTinyJS::execute_literals(bool &execute) { case LEX_INT: { CScriptVarPtr a = newScriptVar(t->getToken().Int()); + a->setExtensible(false); t->match(LEX_INT); return a; } @@ -3402,97 +4533,70 @@ CScriptVarLinkPtr CTinyJS::execute_literals(bool &execute) { return a; } break; - case '{': - if(execute) { - CScriptVarPtr contents(newScriptVar(Object)); - /* JSON-style object definition */ - t->match('{'); - while (t->tk != '}') { - // we only allow strings or IDs on the left hand side of an initialisation - if (t->tk==LEX_STR || t->tk==LEX_ID) { - string id = t->tkStr(); - t->match(t->tk); - t->match(':'); - CScriptVarLinkPtr a = execute_assignment(execute); - if (execute) { - contents->addChildNoDup(id, a); - } - } else if(t->tk==LEX_T_GET || t->tk==LEX_T_SET) { - CScriptTokenDataFnc &Fnc = t->getToken().Fnc(); - if((t->tk == LEX_T_GET && Fnc.arguments.size()==0) || (t->tk == LEX_T_SET && Fnc.arguments.size()==1)) { - CScriptVarLinkPtr funcVar = parseFunctionDefinition(t->getToken()); - CScriptVarLink *child = contents->findChild(Fnc.name); - if(child && !(*child)->isAccessor()) child = 0; - if(!child) child = contents->addChildNoDup(Fnc.name, newScriptVar(Accessor)); - child->getVarPtr()->addChildNoDup((t->tk==LEX_T_GET?TINYJS_ACCESSOR_GET_VAR:TINYJS_ACCESSOR_SET_VAR), funcVar->getVarPtr()); - } - t->match(t->tk); - } - else - t->match(LEX_ID); - // no need to clean here, as it will definitely be used - if (t->tk != ',') break; - t->match(','); - } - - t->match('}'); - return contents; - } else - t->skip(t->getToken().Int()); +#ifndef NO_REGEXP + case LEX_REGEXP: + { + string::size_type pos = t->getToken().String().find_last_of('/'); + string source = t->getToken().String().substr(1, pos-1); + string flags = t->getToken().String().substr(pos+1); + CScriptVarPtr a = newScriptVar(source, flags); + t->match(LEX_REGEXP); + return a; + } break; - case '[': +#endif /* NO_REGEXP */ + case LEX_T_OBJECT_LITERAL: if(execute) { - CScriptVarPtr contents(newScriptVar(Array)); - /* JSON-style array */ - t->match('['); - int idx = 0; - while (t->tk != ']') { - CScriptVarLinkPtr a = execute_assignment(execute); - if (execute) { - contents->addChild(int2string(idx), a); + CScriptTokenDataObjectLiteral &Objc = t->getToken().Object(); + t->match(LEX_T_OBJECT_LITERAL); + if(Objc.destructuring) { + t->match('='); + CScriptVarPtr a = execute_assignment(execute); + if(execute) execute_destructuring(Objc, a, execute); + return a; + } else { + CScriptVarPtr a = Objc.type==CScriptTokenDataObjectLiteral::OBJECT ? newScriptVar(Object) : newScriptVar(Array); + for(vector::iterator it=Objc.elements.begin(); execute && it!=Objc.elements.end(); ++it) { + if(it->value.empty()) continue; + CScriptToken &tk = it->value.front(); + if(tk.token==LEX_T_GET || tk.token==LEX_T_SET) { + CScriptTokenDataFnc &Fnc = tk.Fnc(); + if((tk.token == LEX_T_GET && Fnc.arguments.size()==0) || (tk.token == LEX_T_SET && Fnc.arguments.size()==1)) { + CScriptVarLinkWorkPtr funcVar = parseFunctionDefinition(tk); + CScriptVarLinkWorkPtr child = a->findChild(Fnc.name); + if(child && !child->getVarPtr()->isAccessor()) child.clear(); + if(!child) child = a->addChildOrReplace(Fnc.name, newScriptVar(Accessor)); + child->getVarPtr()->addChildOrReplace((tk.token==LEX_T_GET?TINYJS_ACCESSOR_GET_VAR:TINYJS_ACCESSOR_SET_VAR), funcVar->getVarPtr()); + } + } else { + t->pushTokenScope(it->value); + a->addChildOrReplace(it->id, execute_assignment(execute)); + while(0); + } } - // no need to clean here, as it will definitely be used - if (t->tk != ']') t->match(','); - idx++; + return a; } - t->match(']'); - // return new CScriptVarLink(contents.release()); - return contents; } else - t->skip(t->getToken().Int()); + t->match(LEX_T_OBJECT_LITERAL); break; case LEX_R_LET: // let as expression if(execute) { + CScopeControl ScopeControl(this); t->match(LEX_R_LET); t->match('('); - CScopeControl ScopeControl(this); + t->check(LEX_T_FORWARD); ScopeControl.addLetScope(); - for(;;) { - CScriptVarLinkPtr a; - string var = t->tkStr(); - t->match(LEX_ID); - a = scope()->scopeLet()->findChildOrCreate(var); - a->setDeletable(false); - // sort out initialiser - if (t->tk == '=') { - t->match('='); - a.replaceVar(execute, execute_assignment(execute)); - } - if (t->tk == ',') - t->match(','); - else - break; - } + execute_statement(execute); + execute_var_init(true, execute); t->match(')'); return execute_base(execute); } else { t->skip(t->getToken().Int()); - execute_base(execute); } break; case LEX_T_FUNCTION_OPERATOR: if(execute) { - CScriptVarLinkPtr a = parseFunctionDefinition(t->getToken()); + CScriptVarLinkWorkPtr a = parseFunctionDefinition(t->getToken()); t->match(LEX_T_FUNCTION_OPERATOR); return a; } @@ -3501,19 +4605,24 @@ CScriptVarLinkPtr CTinyJS::execute_literals(bool &execute) { case LEX_R_NEW: // new -> create a new object if (execute) { t->match(LEX_R_NEW); - CScriptVarLinkPtr parent = execute_literals(execute); - CScriptVarLinkPtr objClass = execute_member(parent, execute); + CScriptVarLinkWorkPtr parent = execute_literals(execute); + CScriptVarLinkWorkPtr objClass = execute_member(parent, execute).getter(execute); if (execute) { if(objClass->getVarPtr()->isFunction()) { CScriptVarPtr obj(newScriptVar(Object)); - CScriptVarLink *prototype = (*objClass)->findChild(TINYJS_PROTOTYPE_CLASS); - if(!prototype) prototype = (*objClass)->addChild(TINYJS_PROTOTYPE_CLASS, newScriptVar(Object), SCRIPTVARLINK_WRITABLE); - obj->addChildNoDup(TINYJS___PROTO___VAR, prototype, SCRIPTVARLINK_WRITABLE); + CScriptVarLinkPtr prototype = objClass->getVarPtr()->findChild(TINYJS_PROTOTYPE_CLASS); + if(!prototype || prototype->getVarPtr()->isUndefined() || prototype->getVarPtr()->isNull()) { + prototype = objClass->getVarPtr()->addChild(TINYJS_PROTOTYPE_CLASS, newScriptVar(Object), SCRIPTVARLINK_WRITABLE); + obj->addChildOrReplace(TINYJS___PROTO___VAR, prototype, SCRIPTVARLINK_WRITABLE); + } + CScriptVarLinkPtr constructor = objClass->getVarPtr()->findChild("__constructor__"); + if(constructor && constructor->getVarPtr()->isFunction()) + objClass = constructor; vector arguments; if (t->tk == '(') { t->match('('); while(t->tk!=')') { - CScriptVarPtr value = execute_assignment(execute); + CScriptVarPtr value = execute_assignment(execute).getter(execute); if (execute) arguments.push_back(value); if (t->tk!=')') t->match(',', ')'); @@ -3522,9 +4631,9 @@ CScriptVarLinkPtr CTinyJS::execute_literals(bool &execute) { } if(execute) { CScriptVarPtr returnVar = callFunction(execute, objClass->getVarPtr(), arguments, obj, &obj); - if(!returnVar->isPrimitive()) - return CScriptVarLinkPtr(returnVar); - return CScriptVarLinkPtr(obj); + if(returnVar->isObject()) + return CScriptVarLinkWorkPtr(returnVar); + return CScriptVarLinkWorkPtr(obj); } } else throwError(execute, TypeError, objClass->getName() + " is not a constructor"); @@ -3541,11 +4650,11 @@ CScriptVarLinkPtr CTinyJS::execute_literals(bool &execute) { return constScriptVar(false); case LEX_R_NULL: t->match(LEX_R_NULL); - return newScriptVar(Null); + return constScriptVar(Null); case '(': if(execute) { t->match('('); - CScriptVarLinkPtr a = execute_base(execute); + CScriptVarLinkWorkPtr a = execute_base(execute).getter(execute); t->match(')'); return a; } else @@ -3558,63 +4667,35 @@ CScriptVarLinkPtr CTinyJS::execute_literals(bool &execute) { return constScriptVar(Undefined); } -CScriptVarLinkPtr CTinyJS::execute_member(CScriptVarLinkPtr &parent, bool &execute) { - CScriptVarLinkPtr a; +CScriptVarLinkWorkPtr CTinyJS::execute_member(CScriptVarLinkWorkPtr &parent, bool &execute) { + CScriptVarLinkWorkPtr a; parent.swap(a); if(t->tk == '.' || t->tk == '[') { -// string path = a->name; -// CScriptVar *parent = 0; while(t->tk == '.' || t->tk == '[') { - - if(execute && ((*a)->isUndefined() || (*a)->isNull())) { - throwError(execute, TypeError, a->getName() + " is " + (*a)->getString()); + parent.swap(a); + a = parent.getter(execute); // a is now the "getted" var + if(execute && (a->getVarPtr()->isUndefined() || a->getVarPtr()->isNull())) { + throwError(execute, ReferenceError, a->getName() + " is " + a->toString(execute)); } string name; if(t->tk == '.') { t->match('.'); name = t->tkStr(); -// path += "."+name; t->match(LEX_ID); } else { if(execute) { t->match('['); - CScriptVarLinkPtr index = execute_expression(execute); - name = (*index)->getString(); -// path += "["+name+"]"; + name = execute_expression(execute)->toString(execute); t->match(']'); } else t->skip(t->getToken().Int()); } if (execute) { - bool in_prototype = false; - CScriptVarLink *child = (*a)->findChildWithPrototypeChain(name); -// if ( !child && (child = findInPrototypeChain(a, name)) ) - if ( child && child->getOwner() != a->getVarPtr().getVar() ) - in_prototype = true; - if (!child) { - /* if we haven't found this defined yet, use the built-in - 'length' properly */ - if ((*a)->isArray() && name == "length") { - int l = (*a)->getArrayLength(); - child = new CScriptVarLink(newScriptVar(l)); - } else if ((*a)->isString() && name == "length") { - int l = (*a)->getString().size(); - child = new CScriptVarLink(newScriptVar(l)); - } else { - //child = (*a)->addChild(name); - } - } - if(child) { - parent = a; - if(in_prototype) { - a(child->getVarPtr(), child->getName()); - a->setOwner(parent->getVarPtr().getVar()); // fake owner - but not set Owned -> for assignment stuff - } else - a = child; - } else { - CScriptVar *owner = a->getVarPtr().getVar(); + CScriptVarPtr aVar = a; + a = aVar->findChildWithPrototypeChain(name); + if(!a) { a(constScriptVar(Undefined), name); - a->setOwner(owner); // fake owner - but not set Owned -> for assignment stuff + a.setReferencedOwner(aVar); } } } @@ -3622,128 +4703,119 @@ CScriptVarLinkPtr CTinyJS::execute_member(CScriptVarLinkPtr &parent, bool &execu return a; } -CScriptVarLinkPtr CTinyJS::execute_function_call(bool &execute) { - CScriptVarLinkPtr parent = execute_literals(execute); - CScriptVarLinkPtr a = execute_member(parent, execute); +CScriptVarLinkWorkPtr CTinyJS::execute_function_call(bool &execute) { + CScriptVarLinkWorkPtr parent = execute_literals(execute); + CScriptVarLinkWorkPtr a = execute_member(parent, execute); while (t->tk == '(') { if (execute) { - if (!(*a)->isFunction()) { - string errorMsg = "Expecting '"; - errorMsg = errorMsg + a->getName() + "' to be a function"; - throw new CScriptException(errorMsg.c_str()); - } + if(a->getVarPtr()->isUndefined() || a->getVarPtr()->isNull()) + throwError(execute, ReferenceError, a->getName() + " is " + a->toString(execute)); + CScriptVarLinkWorkPtr fnc = a.getter(execute); + if (!fnc->getVarPtr()->isFunction()) + throwError(execute, TypeError, a->getName() + " is not a function"); t->match('('); // path += '('; // grab in all parameters vector arguments; while(t->tk!=')') { - CScriptVarLinkPtr value = execute_assignment(execute); + CScriptVarLinkWorkPtr value = execute_assignment(execute).getter(execute); // path += (*value)->getString(); if (execute) { arguments.push_back(value); } - if (t->tk!=')') { t->match(',',')'); /*path+=',';*/ } + if (t->tk!=')') { t->match(','); /*path+=',';*/ } } t->match(')'); //path+=')'; // setup a return variable - CScriptVarLinkPtr returnVar; + CScriptVarLinkWorkPtr returnVar; if(execute) { if (!parent) parent = findInScopes("this"); // if no parent use the root-scope CScriptVarPtr This(parent ? parent->getVarPtr() : (CScriptVarPtr )root); - a = callFunction(execute, **a, arguments, This); + a = callFunction(execute, a->getVarPtr(), arguments, This); } } else { // function, but not executing - just parse args and be done t->match('('); while (t->tk != ')') { - CScriptVarLinkPtr value = execute_base(execute); + CScriptVarLinkWorkPtr value = execute_base(execute); // if (t->tk!=')') t->match(','); } t->match(')'); } - a = execute_member(a, execute); + a = execute_member(parent = a, execute); } return a; } // R->L: Precedence 3 (in-/decrement) ++ -- // R<-L: Precedence 4 (unary) ! ~ + - typeof void delete -CScriptVarLinkPtr CTinyJS::execute_unary(bool &execute) { - CScriptVarLinkPtr a; +bool CTinyJS::execute_unary_rhs(bool &execute, CScriptVarLinkWorkPtr& a) { + t->match(t->tk); + a = execute_unary(execute).getter(execute); + if(execute) CheckRightHandVar(execute, a); + return execute; +}; +CScriptVarLinkWorkPtr CTinyJS::execute_unary(bool &execute) { + CScriptVarLinkWorkPtr a; switch(t->tk) { case '-': - t->match('-'); - a = execute_unary(execute); - if (execute) { - CheckRightHandVar(execute, a); - CScriptVarPtr zero = newScriptVar(0); - a = mathsOp(execute, zero, a, '-'); - } + if(execute_unary_rhs(execute, a)) + a(newScriptVar(-a->getVarPtr()->toNumber(execute))); break; case '+': - t->match('+'); - a = execute_unary(execute); - CheckRightHandVar(execute, a); + if(execute_unary_rhs(execute, a)) + a = newScriptVar(a->getVarPtr()->toNumber(execute)); break; case '!': - t->match('!'); // binary not - a = execute_unary(execute); - if (execute) { - CheckRightHandVar(execute, a); - a = mathsOp(execute, a, constZero, LEX_EQUAL); - } + if(execute_unary_rhs(execute, a)) + a = constScriptVar(!a->getVarPtr()->toBoolean()); break; case '~': - t->match('~'); // binary neg - a = execute_unary(execute); - if (execute) { - CheckRightHandVar(execute, a); - a = mathsOp(execute, a, constZero, '~'); - } + if(execute_unary_rhs(execute, a)) + a = newScriptVar(~a->getVarPtr()->toNumber(execute)); break; case LEX_R_TYPEOF: - t->match(LEX_R_TYPEOF); // void - a = execute_unary(execute); - if (execute) { - CheckRightHandVar(execute, a); - a(newScriptVar((*a)->getVarType())); - } + if(execute_unary_rhs(execute, a)) + a = newScriptVar(a->getVarPtr()->getVarType()); break; case LEX_R_VOID: - t->match(LEX_R_VOID); // void - a = execute_unary(execute); - if (execute) { - CheckRightHandVar(execute, a); - a(constScriptVar(Undefined)); - } + if(execute_unary_rhs(execute, a)) + a = constScriptVar(Undefined); break; case LEX_R_DELETE: t->match(LEX_R_DELETE); // delete - a = execute_unary(execute); + a = execute_unary(execute); // no getter - delete can remove the accessor if (execute) { // !!! no right-hand-check by delete - if(a->isOwned() && a->isDeletable()) { + if(a->isOwned() && a->isConfigurable()) { a->getOwner()->removeLink(a); // removes the link from owner - a(constScriptVar(true)); + a = constScriptVar(true); } else - a(constScriptVar(false)); + a = constScriptVar(false); } + break; case LEX_PLUSPLUS: case LEX_MINUSMINUS: { int op = t->tk; - t->match(t->tk); // pre increment/decrement + t->match(op); // pre increment/decrement + CScriptTokenizer::ScriptTokenPosition ErrorPos = t->getPos(); a = execute_function_call(execute); if (execute) { - if(a->isOwned() && a->isWritable()) - { - CScriptVarPtr res = mathsOp(execute, a, constOne, op==LEX_PLUSPLUS ? '+' : '-'); - // in-place add/subtract - a->replaceWith(res); - a = res; + if(a->getName().empty()) + throwError(execute, SyntaxError, string("invalid ")+(op==LEX_PLUSPLUS ? "increment" : "decrement")+" operand", ErrorPos); + else if(!a->isOwned() && !a.hasReferencedOwner() && !a->getName().empty()) + throwError(execute, ReferenceError, a->getName() + " is not defined", ErrorPos); + CScriptVarPtr res = newScriptVar(a.getter(execute)->getVarPtr()->toNumber(execute).add(op==LEX_PLUSPLUS ? 1 : -1)); + if(a->isWritable()) { + if(!a->isOwned() && a.hasReferencedOwner() && a.getReferencedOwner()->isExtensible()) + a.getReferencedOwner()->addChildOrReplace(a->getName(), res); + else + a.setter(execute, res); } + a = res; } } break; @@ -3754,33 +4826,38 @@ CScriptVarLinkPtr CTinyJS::execute_unary(bool &execute) { // post increment/decrement if (t->tk==LEX_PLUSPLUS || t->tk==LEX_MINUSMINUS) { int op = t->tk; - t->match(t->tk); + t->match(op); if (execute) { - if(a->isOwned() && a->isWritable()) - { -// TRACE("post-increment of %s and a is %sthe owner\n", a->name.c_str(), a->owned?"":"not "); - CScriptVarPtr res = a; - CScriptVarPtr new_a = mathsOp(execute, a, constOne, op==LEX_PLUSPLUS ? '+' : '-'); - a->replaceWith(new_a); - a = res; + if(a->getName().empty()) + throwError(execute, SyntaxError, string("invalid ")+(op==LEX_PLUSPLUS ? "increment" : "decrement")+" operand", t->getPrevPos()); + else if(!a->isOwned() && !a.hasReferencedOwner() && !a->getName().empty()) + throwError(execute, ReferenceError, a->getName() + " is not defined", t->getPrevPos()); + CNumber num = a.getter(execute)->getVarPtr()->toNumber(execute); + CScriptVarPtr res = newScriptVar(num.add(op==LEX_PLUSPLUS ? 1 : -1)); + if(a->isWritable()) { + if(!a->isOwned() && a.hasReferencedOwner() && a.getReferencedOwner()->isExtensible()) + a.getReferencedOwner()->addChildOrReplace(a->getName(), res); + else + a.setter(execute, res); } + a = newScriptVar(num); } } return a; } // L->R: Precedence 5 (term) * / % -CScriptVarLinkPtr CTinyJS::execute_term(bool &execute) { - CScriptVarLinkPtr a = execute_unary(execute); +CScriptVarLinkWorkPtr CTinyJS::execute_term(bool &execute) { + CScriptVarLinkWorkPtr a = execute_unary(execute); if (t->tk=='*' || t->tk=='/' || t->tk=='%') { CheckRightHandVar(execute, a); while (t->tk=='*' || t->tk=='/' || t->tk=='%') { int op = t->tk; t->match(t->tk); - CScriptVarLinkPtr b = execute_unary(execute); // L->R + CScriptVarLinkWorkPtr b = execute_unary(execute); // L->R if (execute) { CheckRightHandVar(execute, b); - a = mathsOp(execute, a, b, op); + a = mathsOp(execute, a.getter(execute), b.getter(execute), op); } } } @@ -3788,18 +4865,17 @@ CScriptVarLinkPtr CTinyJS::execute_term(bool &execute) { } // L->R: Precedence 6 (addition/subtraction) + - -CScriptVarLinkPtr CTinyJS::execute_expression(bool &execute) { - CScriptVarLinkPtr a = execute_term(execute); +CScriptVarLinkWorkPtr CTinyJS::execute_expression(bool &execute) { + CScriptVarLinkWorkPtr a = execute_term(execute); if (t->tk=='+' || t->tk=='-') { CheckRightHandVar(execute, a); while (t->tk=='+' || t->tk=='-') { int op = t->tk; t->match(t->tk); - CScriptVarLinkPtr b = execute_term(execute); // L->R + CScriptVarLinkWorkPtr b = execute_term(execute); // L->R if (execute) { CheckRightHandVar(execute, b); - // not in-place, so just replace - a = mathsOp(execute, a, b, op); + a = mathsOp(execute, a.getter(execute), b.getter(execute), op); } } } @@ -3807,19 +4883,19 @@ CScriptVarLinkPtr CTinyJS::execute_expression(bool &execute) { } // L->R: Precedence 7 (bitwise shift) << >> >>> -CScriptVarLinkPtr CTinyJS::execute_binary_shift(bool &execute) { - CScriptVarLinkPtr a = execute_expression(execute); +CScriptVarLinkWorkPtr CTinyJS::execute_binary_shift(bool &execute) { + CScriptVarLinkWorkPtr a = execute_expression(execute); if (t->tk==LEX_LSHIFT || t->tk==LEX_RSHIFT || t->tk==LEX_RSHIFTU) { CheckRightHandVar(execute, a); while (t->tk>=LEX_SHIFTS_BEGIN && t->tk<=LEX_SHIFTS_END) { int op = t->tk; t->match(t->tk); - CScriptVarLinkPtr b = execute_expression(execute); // L->R + CScriptVarLinkWorkPtr b = execute_expression(execute); // L->R if (execute) { CheckRightHandVar(execute, a); // not in-place, so just replace - a = mathsOp(execute, a, b, op); + a = mathsOp(execute, a.getter(execute), b.getter(execute), op); } } } @@ -3827,27 +4903,30 @@ CScriptVarLinkPtr CTinyJS::execute_binary_shift(bool &execute) { } // L->R: Precedence 8 (relational) < <= > <= in instanceof // L->R: Precedence 9 (equality) == != === !=== -CScriptVarLinkPtr CTinyJS::execute_relation(bool &execute, int set, int set_n) { - CScriptVarLinkPtr a = set_n ? execute_relation(execute, set_n, 0) : execute_binary_shift(execute); +CScriptVarLinkWorkPtr CTinyJS::execute_relation(bool &execute, int set, int set_n) { + CScriptVarLinkWorkPtr a = set_n ? execute_relation(execute, set_n, 0) : execute_binary_shift(execute); if ((set==LEX_EQUAL && t->tk>=LEX_RELATIONS_1_BEGIN && t->tk<=LEX_RELATIONS_1_END) || (set=='<' && (t->tk==LEX_LEQUAL || t->tk==LEX_GEQUAL || t->tk=='<' || t->tk=='>' || t->tk == LEX_R_IN || t->tk == LEX_R_INSTANCEOF))) { CheckRightHandVar(execute, a); + a = a.getter(execute); while ((set==LEX_EQUAL && t->tk>=LEX_RELATIONS_1_BEGIN && t->tk<=LEX_RELATIONS_1_END) || (set=='<' && (t->tk==LEX_LEQUAL || t->tk==LEX_GEQUAL || t->tk=='<' || t->tk=='>' || t->tk == LEX_R_IN || t->tk == LEX_R_INSTANCEOF))) { int op = t->tk; t->match(t->tk); - CScriptVarLinkPtr b = set_n ? execute_relation(execute, set_n, 0) : execute_binary_shift(execute); // L->R + CScriptVarLinkWorkPtr b = set_n ? execute_relation(execute, set_n, 0) : execute_binary_shift(execute); // L->R if (execute) { CheckRightHandVar(execute, b); + string nameOf_b = b->getName(); + b = b.getter(execute); if(op == LEX_R_IN) { - a(constScriptVar( (*b)->findChildWithPrototypeChain((*a)->getString())!= 0 )); + a(constScriptVar( (bool)b->getVarPtr()->findChildWithPrototypeChain(a->toString(execute)))); } else if(op == LEX_R_INSTANCEOF) { - CScriptVarLink *prototype = (*b)->findChild(TINYJS_PROTOTYPE_CLASS); + CScriptVarLinkPtr prototype = b->getVarPtr()->findChild(TINYJS_PROTOTYPE_CLASS); if(!prototype) - throwError(execute, TypeError, "invalid 'instanceof' operand "+b->getName()); + throwError(execute, TypeError, "invalid 'instanceof' operand "+nameOf_b); else { unsigned int uniqueID = getUniqueID(); - CScriptVarPtr object = (*a)->findChild(TINYJS___PROTO___VAR); + CScriptVarPtr object = a->getVarPtr()->findChild(TINYJS___PROTO___VAR); while( object && object!=prototype->getVarPtr() && object->getTempraryID() != uniqueID) { object->setTemporaryID(uniqueID); // prevents recursions object = object->findChild(TINYJS___PROTO___VAR); @@ -3865,54 +4944,44 @@ CScriptVarLinkPtr CTinyJS::execute_relation(bool &execute, int set, int set_n) { // L->R: Precedence 10 (bitwise-and) & // L->R: Precedence 11 (bitwise-xor) ^ // L->R: Precedence 12 (bitwise-or) | -CScriptVarLinkPtr CTinyJS::execute_binary_logic(bool &execute, int op, int op_n1, int op_n2) { - CScriptVarLinkPtr a = op_n1 ? execute_binary_logic(execute, op_n1, op_n2, 0) : execute_relation(execute); +CScriptVarLinkWorkPtr CTinyJS::execute_binary_logic(bool &execute, int op, int op_n1, int op_n2) { + CScriptVarLinkWorkPtr a = op_n1 ? execute_binary_logic(execute, op_n1, op_n2, 0) : execute_relation(execute); if (t->tk==op) { CheckRightHandVar(execute, a); + a = a.getter(execute); while (t->tk==op) { t->match(t->tk); - CScriptVarLinkPtr b = op_n1 ? execute_binary_logic(execute, op_n1, op_n2, 0) : execute_relation(execute); // L->R + CScriptVarLinkWorkPtr b = op_n1 ? execute_binary_logic(execute, op_n1, op_n2, 0) : execute_relation(execute); // L->R if (execute) { CheckRightHandVar(execute, b); - a = mathsOp(execute, a, b, op); + a = mathsOp(execute, a, b.getter(execute), op); } } } return a; } -// L->R: Precedence 13 ==> (logical-or) && +// L->R: Precedence 13 ==> (logical-and) && // L->R: Precedence 14 ==> (logical-or) || -CScriptVarLinkPtr CTinyJS::execute_logic(bool &execute, int op /*= LEX_OROR*/, int op_n /*= LEX_ANDAND*/) { - CScriptVarLinkPtr a = op_n ? execute_logic(execute, op_n, 0) : execute_binary_logic(execute); +CScriptVarLinkWorkPtr CTinyJS::execute_logic(bool &execute, int op /*= LEX_OROR*/, int op_n /*= LEX_ANDAND*/) { + CScriptVarLinkWorkPtr a = op_n ? execute_logic(execute, op_n, 0) : execute_binary_logic(execute); if (t->tk==op) { if(execute) { CheckRightHandVar(execute, a); - CScriptVarPtr _a = a; - CScriptVarLinkPtr b=a; - bool _a_bool = _a->getBool(); + a(a.getter(execute)); // rebuild a + CScriptVarLinkWorkPtr b; + bool result_bool = a->toBoolean(); bool shortCircuit = false; while (t->tk==op) { - int binary_op = t->tk; t->match(t->tk); - if (op==LEX_ANDAND) { - binary_op = '&'; - shortCircuit = !_a_bool; - } else { - binary_op = '|'; - shortCircuit = _a_bool; - } + shortCircuit = (op==LEX_ANDAND) ? !result_bool : result_bool; b = op_n ? execute_logic(shortCircuit ? noexecute : execute, op_n, 0) : execute_binary_logic(shortCircuit ? noexecute : execute); // L->R - CheckRightHandVar(execute, b); if (execute && !shortCircuit) { CheckRightHandVar(execute, b); - _a = mathsOp(execute, constScriptVar(_a_bool), constScriptVar((*b)->getBool()), binary_op); - _a_bool = _a->getBool(); + a(b.getter(execute)); // rebuild a + result_bool = a->toBoolean(); } } - if (_a_bool && ( (op==LEX_ANDAND && !shortCircuit) || (op==LEX_OROR) ) ) - return b; - else - return constFalse; + return a; } else op_n ? execute_logic(execute, op_n, 0) : execute_binary_logic(execute); // L->R } @@ -3920,20 +4989,18 @@ CScriptVarLinkPtr CTinyJS::execute_logic(bool &execute, int op /*= LEX_OROR*/, i } // L<-R: Precedence 15 (condition) ?: -CScriptVarLinkPtr CTinyJS::execute_condition(bool &execute) +CScriptVarLinkWorkPtr CTinyJS::execute_condition(bool &execute) { - CScriptVarLinkPtr a = execute_logic(execute); + CScriptVarLinkWorkPtr a = execute_logic(execute); if (t->tk=='?') { CheckRightHandVar(execute, a); t->match('?'); - bool cond = execute && (*a)->getBool(); - CScriptVarLinkPtr b; + bool cond = execute && a.getter(execute)->toBoolean(); + CScriptVarLinkWorkPtr b; a = execute_condition(cond ? execute : noexecute ); // L<-R -// CheckRightHandVar(execute, a); t->match(':'); b = execute_condition(cond ? noexecute : execute); // R-L -// CheckRightHandVar(execute, b); if(!cond) return b; } @@ -3941,69 +5008,52 @@ CScriptVarLinkPtr CTinyJS::execute_condition(bool &execute) } // L<-R: Precedence 16 (assignment) = += -= *= /= %= <<= >>= >>>= &= |= ^= +// now we can return CScriptVarLinkPtr execute_assignment returns always no setters/getters +// force life of the Owner is no more needed CScriptVarLinkPtr CTinyJS::execute_assignment(bool &execute) { - CScriptVarLinkPtr lhs = execute_condition(execute); + return execute_assignment(execute_condition(execute), execute); +} +CScriptVarLinkPtr CTinyJS::execute_assignment(CScriptVarLinkWorkPtr lhs, bool &execute) { if (t->tk=='=' || (t->tk>=LEX_ASSIGNMENTS_BEGIN && t->tk<=LEX_ASSIGNMENTS_END) ) { int op = t->tk; CScriptTokenizer::ScriptTokenPosition leftHandPos = t->getPos(); t->match(t->tk); - CScriptVarLinkPtr rhs = execute_assignment(execute); // L<-R + CScriptVarLinkWorkPtr rhs = execute_assignment(execute).getter(execute); // L<-R if (execute) { - bool lhs_is_accessor = lhs.getLink() != lhs.getRealLink() && lhs.getRealLink() && (*lhs.getRealLink())->isAccessor(); - if (!lhs->isOwned() && lhs->getName().empty() && !lhs_is_accessor) { - throw new CScriptException("invalid assignment left-hand side", t->currentFile, leftHandPos.currentLine(), leftHandPos.currentColumn()); - } else if (op != '=' && !lhs->isOwned() && !lhs_is_accessor) { + if (!lhs->isOwned() && !lhs.hasReferencedOwner() && lhs->getName().empty()) { + throw new CScriptException(ReferenceError, "invalid assignment left-hand side (at runtime)", t->currentFile, leftHandPos.currentLine(), leftHandPos.currentColumn()); + } else if (op != '=' && !lhs->isOwned()) { throwError(execute, ReferenceError, lhs->getName() + " is not defined"); } else if(lhs->isWritable()) { if (op=='=') { - if (!lhs->isOwned() && !lhs_is_accessor) { - CScriptVarLink *realLhs; - if(lhs->isOwner()) - realLhs = lhs->getOwner()->addChildNoDup(lhs->getName(), lhs); - else - realLhs = root->addChildNoDup(lhs->getName(), lhs); - lhs = realLhs; + if (!lhs->isOwned()) { + CScriptVarPtr fakedOwner = lhs.getReferencedOwner(); + if(fakedOwner) { + if(!fakedOwner->isExtensible()) + return rhs->getVarPtr(); + lhs = fakedOwner->addChildOrReplace(lhs->getName(), lhs); + } else + lhs = root->addChildOrReplace(lhs->getName(), lhs); } - lhs.replaceVar(execute, rhs); + lhs.setter(execute, rhs); return rhs->getVarPtr(); } else { CScriptVarPtr result; static int assignments[] = {'+', '-', '*', '/', '%', LEX_LSHIFT, LEX_RSHIFT, LEX_RSHIFTU, '&', '|', '^'}; result = mathsOp(execute, lhs, rhs, assignments[op-LEX_PLUSEQUAL]); -/* - if (op==LEX_PLUSEQUAL) - result = mathsOp(execute, lhs, rhs, '+'); - else if (op==LEX_MINUSEQUAL) - result = mathsOp(execute, lhs, rhs, '-'); - else if (op==LEX_ASTERISKEQUAL) - result = mathsOp(execute, lhs, rhs, '*'); - else if (op==LEX_SLASHEQUAL) - result = mathsOp(execute, lhs, rhs, '/'); - else if (op==LEX_PERCENTEQUAL) - result = mathsOp(execute, lhs, rhs, '%'); - else if (op==LEX_LSHIFTEQUAL) - result = mathsOp(execute, lhs, rhs, LEX_LSHIFT); - else if (op==LEX_RSHIFTEQUAL) - result = mathsOp(execute, lhs, rhs, LEX_RSHIFT); - else if (op==LEX_RSHIFTUEQUAL) - result = mathsOp(execute, lhs, rhs, LEX_RSHIFTU); - else if (op==LEX_ANDEQUAL) - result = mathsOp(execute, lhs, rhs, '&'); - else if (op==LEX_OREQUAL) - result = mathsOp(execute, lhs, rhs, '|'); - else if (op==LEX_XOREQUAL) - result = mathsOp(execute, lhs, rhs, '^'); -*/ - lhs->replaceWith(result); + lhs.setter(execute, result); return result; } + } else { + // lhs is not writable we ignore lhs & use rhs + return rhs->getVarPtr(); } } } else CheckRightHandVar(execute, lhs); - return lhs; + return lhs.getter(execute); } // L->R: Precedence 17 (comma) , CScriptVarLinkPtr CTinyJS::execute_base(bool &execute) { @@ -4018,11 +5068,11 @@ CScriptVarLinkPtr CTinyJS::execute_base(bool &execute) { } return a; } -void CTinyJS::execute_block(bool &execute) { +void CTinyJS::execute_block(bool &execute, bool createLetScope /*=true*/) { if(execute) { t->match('{'); CScopeControl ScopeControl(this); - if(scope()->scopeLet() != scope()) // add a LetScope only if needed + if(createLetScope && t->tk==LEX_T_FORWARD && t->getToken().Forwarder().lets.size()) // add a LetScope only if needed ScopeControl.addLetScope(); while (t->tk && t->tk!='}') execute_statement(execute); @@ -4045,38 +5095,47 @@ CScriptVarLinkPtr CTinyJS::execute_statement(bool &execute) { ret = execute_base(execute); t->match(';'); break; + case LEX_T_FORWARD: + { + CScriptVarPtr in_scope = scope()->scopeLet(); + STRING_SET_t &vars = t->getToken().Forwarder().lets; + for(int i=0; i<2; ++i) { + for(STRING_SET_it it=vars.begin(); it!=vars.end(); ++it) { + CScriptVarLinkPtr a = in_scope->findChild(*it); + if(!a) in_scope->addChild(*it, constScriptVar(Undefined), SCRIPTVARLINK_VARDEFAULT); + } + in_scope = scope()->scopeVar(); + vars = t->getToken().Forwarder().vars; + } + CScriptTokenDataForwards::FNC_SET_t &functions = t->getToken().Forwarder().functions; + for(CScriptTokenDataForwards::FNC_SET_it it=functions.begin(); it!=functions.end(); ++it) { + CScriptVarLinkWorkPtr funcVar = parseFunctionDefinition(*it); + in_scope->addChildOrReplace(funcVar->getName(), funcVar, SCRIPTVARLINK_VARDEFAULT); + } + t->match(LEX_T_FORWARD); + } + break; case LEX_R_VAR: case LEX_R_LET: if(execute) { + CScopeControl ScopeControl(this); bool let = t->tk==LEX_R_LET, let_ext=false; t->match(t->tk); - CScopeControl ScopeControl(this); if(let && t->tk=='(') { let_ext = true; t->match('('); + t->check(LEX_T_FORWARD); ScopeControl.addLetScope(); + execute_statement(execute); } - CScriptVarPtr in_scope = let ? scope()->scopeLet() : scope()->scopeVar(); - for(;;) { - CScriptVarLinkPtr a; - string var = t->tkStr(); - t->match(LEX_ID); - a = in_scope->findChildOrCreate(var); - a->setDeletable(false); - // sort out initialiser - if (t->tk == '=') { - t->match('='); - a.replaceVar(execute, execute_assignment(execute)); - } - if (t->tk == ',') - t->match(','); - else - break; - } + execute_var_init(let_ext, execute); if(let_ext) { t->match(')'); - execute_statement(execute); + if(t->tk == '{') + execute_block(execute, false); + else + execute_statement(execute); } else t->match(';'); } else @@ -4098,7 +5157,7 @@ CScriptVarLinkPtr CTinyJS::execute_statement(bool &execute) { if(execute) { t->match(LEX_R_IF); t->match('('); - bool cond = (*execute_base(execute))->getBool(); + bool cond = execute_base(execute)->toBoolean(); t->match(')'); if(cond && execute) { t->match(LEX_T_SKIP); @@ -4149,7 +5208,7 @@ CScriptVarLinkPtr CTinyJS::execute_statement(bool &execute) { } t->match(LEX_R_WHILE); t->match('('); - loopCond = (*execute_base(execute))->getBool(); + loopCond = execute_base(execute)->toBoolean(); t->match(')'); t->match(';'); } @@ -4167,7 +5226,7 @@ CScriptVarLinkPtr CTinyJS::execute_statement(bool &execute) { bool loopCond; t->match('('); CScriptTokenizer::ScriptTokenPosition condStart = t->getPos(); - loopCond = (*execute_base(execute))->getBool(); + loopCond = execute_base(execute)->toBoolean(); t->match(')'); if(loopCond && execute) { t->match(LEX_T_SKIP); @@ -4192,7 +5251,7 @@ CScriptVarLinkPtr CTinyJS::execute_statement(bool &execute) { } if(execute) { t->setPos(condStart); - loopCond = (*execute_base(execute))->getBool(); + loopCond = execute_base(execute)->toBoolean(); } } t->setPos(loopEnd); @@ -4214,8 +5273,8 @@ CScriptVarLinkPtr CTinyJS::execute_statement(bool &execute) { t->match(LEX_T_LOOP_LABEL); } t->match('('); - CScriptVarLinkPtr for_var; - CScriptVarLinkPtr for_in_var; + CScriptVarLinkWorkPtr for_var; + CScriptVarLinkWorkPtr for_in_var; CScopeControl ScopeControl(this); ScopeControl.addLetScope(); @@ -4226,35 +5285,36 @@ CScriptVarLinkPtr CTinyJS::execute_statement(bool &execute) { t->match(LEX_ID); for_var = scope()->scopeLet()->findChildOrCreate(var); } - else + else { + if(t->tk == LEX_R_VAR) t->match(LEX_R_VAR); // skip var for_var = execute_function_call(execute); + } - t->match(LEX_R_IN); + if(!for_each) for_each = t->tk==LEX_T_OF; + t->match(LEX_R_IN, LEX_T_OF); for_in_var = execute_function_call(execute); CheckRightHandVar(execute, for_in_var); t->match(')'); STRING_SET_t keys; - (*for_in_var)->keys(keys, true, getUniqueID()); + for_in_var->getVarPtr()->keys(keys, true, 0); if( keys.size() ) { if(!for_var->isOwned()) { - CScriptVarLink *real_for_var; - if(for_var->isOwner()) - real_for_var = for_var->getOwner()->addChildNoDup(for_var->getName(), for_var); + if(for_var.hasReferencedOwner()) + for_var = for_var.getReferencedOwner()->addChildOrReplace(for_var->getName(), for_var); else - real_for_var = root->addChildNoDup(for_var->getName(), for_var); - for_var = real_for_var; + for_var = root->addChildOrReplace(for_var->getName(), for_var); } CScriptTokenizer::ScriptTokenPosition loopStart = t->getPos(); for(STRING_SET_it it = keys.begin(); execute && it != keys.end(); ++it) { - CScriptVarLink *link = for_var.getLink(); + CScriptVarLinkPtr link = for_var; if(link) { if (for_each) - link->replaceWith((*for_in_var)->findChildWithPrototypeChain(*it)); + link->setVarPtr(for_in_var->getVarPtr()->findChild(*it)); else - link->replaceWith(newScriptVar(*it)); + link->setVarPtr(newScriptVar(*it)); } else ASSERT(0); t->setPos(loopStart); execute_statement(execute); @@ -4287,15 +5347,18 @@ CScriptVarLinkPtr CTinyJS::execute_statement(bool &execute) { } t->match('('); CScopeControl ScopeControl(this); - if(t->tk == LEX_R_LET) + if(t->tk == LEX_T_FORWARD) { ScopeControl.addLetScope(); + execute_statement(execute); // forwarder + } + //if(t->tk == LEX_R_LET) execute_statement(execute); // initialisation CScriptTokenizer::ScriptTokenPosition conditionStart = t->getPos(); bool cond_empty = true; bool loopCond = execute; // Empty Condition -->always true if(t->tk != ';') { cond_empty = false; - loopCond = execute && (*execute_base(execute))->getBool(); + loopCond = execute && execute_base(execute)->toBoolean(); } t->match(';'); CScriptTokenizer::ScriptTokenPosition iterStart = t->getPos(); @@ -4334,7 +5397,7 @@ CScriptVarLinkPtr CTinyJS::execute_statement(bool &execute) { } if(!cond_empty) { t->setPos(conditionStart); - loopCond = (*execute_base(execute))->getBool(); + loopCond = execute_base(execute)->toBoolean(); } } } @@ -4365,22 +5428,18 @@ CScriptVarLinkPtr CTinyJS::execute_statement(bool &execute) { if (t->tk != ';') result = execute_base(execute); t->match(';'); - if(result) scope()->scopeVar()->addChildNoDup(TINYJS_RETURN_VAR, result); + if(result) scope()->scopeVar()->addChildOrReplace(TINYJS_RETURN_VAR, result); execute = false; } else t->skip(t->getToken().Int()); break; case LEX_R_FUNCTION: if(execute) { - CScriptTokenDataFnc &Fnc = t->getToken().Fnc(); - if(!Fnc.name.length()) - throw new CScriptException("Functions defined at statement-level are meant to have a name."); - else { - CScriptVarLinkPtr funcVar = parseFunctionDefinition(t->getToken()); - scope()->scopeVar()->addChildNoDup(funcVar->getName(), funcVar)->setDeletable(false); - } + CScriptVarLinkWorkPtr funcVar = parseFunctionDefinition(t->getToken()); + scope()->scopeVar()->addChildOrReplace(funcVar->getName(), funcVar, SCRIPTVARLINK_VARDEFAULT); } - t->match(LEX_R_FUNCTION); + case LEX_R_FUNCTION_PLACEHOLDER: + t->match(t->tk); break; case LEX_R_TRY: if(execute) { @@ -4422,14 +5481,14 @@ CScriptVarLinkPtr CTinyJS::execute_statement(bool &execute) { bool condition = true; if(t->tk == LEX_R_IF) { t->match(LEX_R_IF); - condition = (*execute_base(execute))->getPrimitivVar(execute)->getBool(); + condition = execute_base(execute)->toBoolean(); } t->match(')'); if(execute && condition) { isThrow = false; - execute_block(execute); + execute_block(execute, false); } else - execute_block(noexecute); + execute_block(noexecute, false); } else t->skip(t->getToken().Int()); } @@ -4462,7 +5521,7 @@ CScriptVarLinkPtr CTinyJS::execute_statement(bool &execute) { exceptionVar = a; } else - throw new CScriptException("uncaught exception: '"+a->getString()+"'", t->currentFile, tokenPos.currentLine(), tokenPos.currentColumn()); + throw new CScriptException("uncaught exception: '"+a->toString(execute)+"'", t->currentFile, tokenPos.currentLine(), tokenPos.currentColumn()); } } else t->skip(t->getToken().Int()); @@ -4474,80 +5533,74 @@ CScriptVarLinkPtr CTinyJS::execute_statement(bool &execute) { CScriptVarPtr SwitchValue = execute_base(execute); t->match(')'); if(execute) { - // save runtimeFlags - int old_switch_runtimeFlags = runtimeFlags & RUNTIME_BREAK_MASK; - // set runtimeFlags - runtimeFlags = (runtimeFlags & ~RUNTIME_BREAK_MASK) | RUNTIME_CAN_BREAK; - bool found = false ,execute = false; t->match('{'); CScopeControl ScopeControl(this); - ScopeControl.addLetScope(); - if(t->tk == LEX_R_CASE || t->tk == LEX_R_DEFAULT || t->tk == '}') { - CScriptTokenizer::ScriptTokenPosition defaultStart = t->getPos(); - bool hasDefault = false; - while (t->tk) { - switch(t->tk) { - case LEX_R_CASE: - if(found) { - t->skip(t->getToken().Int()); - if(execute) t->match(':'); - else t->skip(t->getToken().Int()); - } else { - t->match(LEX_R_CASE); - execute = true; - CScriptVarLinkPtr CaseValue = execute_base(execute); - if(execute) { - CaseValue = mathsOp(execute, CaseValue, SwitchValue, LEX_EQUAL); - found = execute = (*CaseValue)->getBool(); - if(found) t->match(':'); - else t->skip(t->getToken().Int()); - } else { - found = true; - t->skip(t->getToken().Int()); - } - } - break; - case LEX_R_DEFAULT: + if(t->tk == LEX_T_FORWARD) { + ScopeControl.addLetScope(); // add let-scope only if needed + execute_statement(execute); // execute forwarder + } + CScriptTokenizer::ScriptTokenPosition defaultStart = t->getPos(); + bool hasDefault = false, found = false; + while (t->tk) { + switch(t->tk) { + case LEX_R_CASE: + if(!execute) + t->skip(t->getToken().Int()); // skip up to'}' + else if(found) { // execute && found + t->match(LEX_R_CASE); + t->skip(t->getToken().Int()); // skip up to ':' + t->match(':'); // skip ':' and execute all after ':' + } else { // execute && !found + t->match(LEX_R_CASE); + t->match(LEX_T_SKIP); // skip 'L_T_SKIP' + CScriptVarLinkPtr CaseValue = execute_base(execute); + CaseValue = mathsOp(execute, CaseValue, SwitchValue, LEX_EQUAL); + if(execute) { + found = CaseValue->toBoolean(); + if(found) t->match(':'); // skip ':' and execute all after ':' + else t->skip(t->getToken().Int()); // skip up to next 'case'/'default' or '}' + } else + t->skip(t->getToken().Int()); // skip up to next 'case'/'default' or '}' + } + break; + case LEX_R_DEFAULT: + if(!execute) + t->skip(t->getToken().Int()); // skip up to'}' NOTE: no extra 'L_T_SKIP' for skipping tp ':' + else { t->match(LEX_R_DEFAULT); - if(found) { - if(execute) t->match(':'); - else t->skip(t->getToken().Int()); - } else { - hasDefault = true; - defaultStart = t->getPos(); - t->skip(t->getToken().Int()); + if(found) + t->match(':'); // skip ':' and execute all after ':' + else { + hasDefault = true; // in fist pass: skip default-area + defaultStart = t->getPos(); // remember pos of default + t->skip(t->getToken().Int()); // skip up to next 'case' or '}' } - break; - case '}': - if(!found && hasDefault) { - found = execute = true; - t->setPos(defaultStart); - t->match(':'); - } else - goto end_while; // goto isn't fine but C supports no "break lable;" - break; - default: - execute_statement(execute); - break; } + break; + case '}': + if(execute && !found && hasDefault) { // if not found & have default -> execute default + found = true; + t->setPos(defaultStart); + t->match(':'); + } else + goto end_while; // goto isn't fine but C supports no "break lable;" + break; + default: + execute_statement(found ? execute : noexecute); + break; } + } end_while: - t->match('}'); - if(!found || (runtimeFlags & RUNTIME_BREAK) ) - execute = true; - // restore runtimeFlags - runtimeFlags = (runtimeFlags & ~RUNTIME_BREAK_MASK) | old_switch_runtimeFlags; - } else - throw new CScriptException("invalid switch statement"); + t->match('}'); + if(!execute && (runtimeFlags & RUNTIME_BREAK) ) { + execute = true; + runtimeFlags &= ~RUNTIME_BREAK; + } } else - execute_block(execute); + t->skip(t->getToken().Int()); } else t->skip(t->getToken().Int()); break; - case LEX_T_DUMMY_LABEL: - t->match(LEX_T_DUMMY_LABEL); // ignore dummy Label - t->match(':'); - break; case LEX_T_LABEL: { string Label = t->tkStr(); @@ -4580,7 +5633,7 @@ CScriptVarLinkPtr CTinyJS::execute_statement(bool &execute) { /// Finds a child, looking recursively up the scopes -CScriptVarLink *CTinyJS::findInScopes(const string &childName) { +CScriptVarLinkPtr CTinyJS::findInScopes(const string &childName) { return scope()->findInScopes(childName); } @@ -4589,32 +5642,141 @@ CScriptVarLink *CTinyJS::findInScopes(const string &childName) { ////////////////////////////////////////////////////////////////////////// void CTinyJS::native_Object(const CFunctionsScopePtr &c, void *data) { - CScriptVarPtr objc = c->getArgument(0); - if(objc->isUndefined() || objc->isNull()) - c->setReturnVar(newScriptVar(Object)); - else if(objc->isPrimitive()) { - c->setReturnVar(newScriptVar(ObjectWrap, objc)); - } else - c->setReturnVar(objc); -} -void CTinyJS::native_Object_hasOwnProperty(const CFunctionsScopePtr &c, void *data) { - c->setReturnVar(c->constScriptVar(c->getArgument("this")->findChild(c->getArgument("prop")->getString()) != 0)); + c->setReturnVar(c->getArgument(0)->toObject()); } void CTinyJS::native_Object_getPrototypeOf(const CFunctionsScopePtr &c, void *data) { if(c->getArgumentsLength()>=1) { - c->setReturnVar(c->getArgument(0)->findChild(TINYJS___PROTO___VAR)); + CScriptVarPtr obj = c->getArgument(0); + if(obj->isObject()) { + c->setReturnVar(obj->findChild(TINYJS___PROTO___VAR)); + return; + } } - // TODO throw error + c->throwError(TypeError, "argument is not an object"); } -void CTinyJS::native_Object_valueOf(const CFunctionsScopePtr &c, void *data) { - bool execute = true; - c->setReturnVar(c->getArgument("this")->_valueOf(execute)); + +void CTinyJS::native_Object_setObjectSecure(const CFunctionsScopePtr &c, void *data) { + CScriptVarPtr obj = c->getArgument(0); + if(!obj->isObject()) c->throwError(TypeError, "argument is not an object"); + if(data==(void*)2) + obj->freeze(); + else if(data==(void*)1) + obj->seal(); + else + obj->preventExtensions(); + c->setReturnVar(obj); +} + +void CTinyJS::native_Object_isSecureObject(const CFunctionsScopePtr &c, void *data) { + CScriptVarPtr obj = c->getArgument(0); + if(!obj->isObject()) c->throwError(TypeError, "argument is not an object"); + bool ret; + if(data==(void*)2) + ret = obj->isFrozen(); + else if(data==(void*)1) + ret = obj->isSealed(); + else + ret = obj->isExtensible(); + c->setReturnVar(constScriptVar(ret)); +} + +void CTinyJS::native_Object_keys(const CFunctionsScopePtr &c, void *data) { + CScriptVarPtr obj = c->getArgument(0); + if(!obj->isObject()) c->throwError(TypeError, "argument is not an object"); + CScriptVarPtr returnVar = c->newScriptVar(Array); + c->setReturnVar(returnVar); + + STRING_SET_t keys; + obj->keys(keys, data==0); + + uint32_t length = 0; + CScriptVarStringPtr isStringObj = obj->getRawPrimitive(); + if(isStringObj) + length = isStringObj->stringLength(); + else + length = obj->getArrayLength(); + for(uint32_t i=0; isetArrayIndex(idx++, newScriptVar(*it)); +} + +void CTinyJS::native_Object_getOwnPropertyDescriptor(const CFunctionsScopePtr &c, void *data) { + CScriptVarPtr obj = c->getArgument(0); + if(!obj->isObject()) c->throwError(TypeError, "argument is not an object"); + c->setReturnVar(obj->getOwnPropertyDescriptor(c->getArgument(1)->toString())); +} + +void CTinyJS::native_Object_defineProperty(const CFunctionsScopePtr &c, void *data) { + CScriptVarPtr obj = c->getArgument(0); + if(!obj->isObject()) c->throwError(TypeError, "argument is not an object"); + string name = c->getArgument(1)->toString(); + CScriptVarPtr attributes = c->getArgument(2); + if(!attributes->isObject()) c->throwError(TypeError, "attributes is not an object"); + const char *err = obj->defineProperty(name, attributes); + if(err) c->throwError(TypeError, err); + c->setReturnVar(obj); +} + +void CTinyJS::native_Object_defineProperties(const CFunctionsScopePtr &c, void *data) { + bool ObjectCreate = data!=0; + CScriptVarPtr obj = c->getArgument(0); + if(ObjectCreate) { + if(!obj->isObject() && !obj->isNull()) c->throwError(TypeError, "argument is not an object or null"); + obj = newScriptVar(Object, obj); + } else + if(!obj->isObject()) c->throwError(TypeError, "argument is not an object"); + c->setReturnVar(obj); + if(c->getArrayLength()<2) { + if(ObjectCreate) return; + c->throwError(TypeError, "Object.defineProperties requires 2 arguments"); + } + + CScriptVarPtr properties = c->getArgument(1); + + STRING_SET_t names; + properties->keys(names, true); + + for(STRING_SET_it it=names.begin(); it!=names.end(); ++it) { + CScriptVarPtr attributes = properties->findChildWithStringChars(*it).getter(); + if(!attributes->isObject()) c->throwError(TypeError, "descriptor for "+*it+" is not an object"); + const char *err = obj->defineProperty(*it, attributes); + if(err) c->throwError(TypeError, err); + } +} + + +////////////////////////////////////////////////////////////////////////// +/// Object.prototype +////////////////////////////////////////////////////////////////////////// + +void CTinyJS::native_Object_prototype_hasOwnProperty(const CFunctionsScopePtr &c, void *data) { + CScriptVarPtr This = c->getArgument("this"); + string PropStr = c->getArgument("prop")->toString(); + CScriptVarLinkPtr Prop = This->findChild(PropStr); + bool res = Prop && !Prop->getVarPtr()->isUndefined(); + if(!res) { + CScriptVarStringPtr This_asString = This->getRawPrimitive(); + if(This_asString) { + uint32_t Idx = isArrayIndex(PropStr); + res = Idx!=uint32_t(-1) && IdxstringLength(); + } + } + c->setReturnVar(c->constScriptVar(res)); +} +void CTinyJS::native_Object_prototype_valueOf(const CFunctionsScopePtr &c, void *data) { + c->setReturnVar(c->getArgument("this")->valueOf_CallBack()); } -void CTinyJS::native_Object_toString(const CFunctionsScopePtr &c, void *data) { +void CTinyJS::native_Object_prototype_toString(const CFunctionsScopePtr &c, void *data) { bool execute = true; int radix = 10; - if(c->getArgumentsLength()>=1) radix = c->getArgument("radix")->getInt(); - c->setReturnVar(c->getArgument("this")->_toString(execute, radix)); + if(c->getArgumentsLength()>=1) radix = c->getArgument("radix")->toNumber().toInt32(); + c->setReturnVar(c->getArgument("this")->toString_CallBack(execute, radix)); + if(!execute) { + // TODO + } } ////////////////////////////////////////////////////////////////////////// @@ -4622,13 +5784,18 @@ void CTinyJS::native_Object_toString(const CFunctionsScopePtr &c, void *data) { ////////////////////////////////////////////////////////////////////////// void CTinyJS::native_Array(const CFunctionsScopePtr &c, void *data) { -// CScriptVar *returnVar = new CScriptVarArray(c->getContext()); CScriptVarPtr returnVar = c->newScriptVar(Array); c->setReturnVar(returnVar); int length = c->getArgumentsLength(); - if(length == 1 && c->getArgument(0)->isNumber()) - returnVar->setArrayIndex(c->getArgument(0)->getInt(), constScriptVar(Undefined)); - else for(int i=0; igetArgument(0); + if(data!=0 && length == 1 && Argument_0_Var->isNumber()) { + CNumber Argument_0 = Argument_0_Var->toNumber(); + uint32_t new_size = Argument_0.toUInt32(); + if(Argument_0.isFinite() && Argument_0 == new_size) + returnVar->setArrayIndex(new_size-1, constScriptVar(Undefined)); + else + c->throwError(RangeError, "invalid array length"); + } else for(int i=0; isetArrayIndex(i, c->getArgument(i)); } @@ -4641,11 +5808,38 @@ void CTinyJS::native_String(const CFunctionsScopePtr &c, void *data) { if(c->getArgumentsLength()==0) arg = newScriptVar(""); else - arg = newScriptVar(c->getArgument(0)->getString()); - CScriptVarLink *This = c->findChild("this"); - This->replaceWith(newScriptVar(ObjectWrap, arg)); - c->setReturnVar(arg); + arg = newScriptVar(c->getArgument(0)->toString()); + if(data) + c->setReturnVar(arg->toObject()); + else + c->setReturnVar(arg); +} + + +////////////////////////////////////////////////////////////////////////// +/// RegExp +////////////////////////////////////////////////////////////////////////// +#ifndef NO_REGEXP + +void CTinyJS::native_RegExp(const CFunctionsScopePtr &c, void *data) { + int arglen = c->getArgumentsLength(); + string RegExp, Flags; + if(arglen>=1) { + RegExp = c->getArgument(0)->toString(); + try { regex(RegExp, regex_constants::ECMAScript); } catch(regex_error e) { + c->throwError(SyntaxError, string(e.what())+" - "+CScriptVarRegExp::ErrorStr(e.code())); + } + if(arglen>=2) { + Flags = c->getArgument(1)->toString(); + string::size_type pos = Flags.find_first_not_of("gimy"); + if(pos != string::npos) { + c->throwError(SyntaxError, string("invalid regular expression flag ")+Flags[pos]); + } + } + } + c->setReturnVar(newScriptVar(RegExp, Flags)); } +#endif /* NO_REGEXP */ ////////////////////////////////////////////////////////////////////////// /// Number @@ -4656,12 +5850,14 @@ void CTinyJS::native_Number(const CFunctionsScopePtr &c, void *data) { if(c->getArgumentsLength()==0) arg = newScriptVar(0); else - arg = c->getArgument(0)->getNumericVar(); - CScriptVarLink *This = c->findChild("this"); - This->replaceWith(newScriptVar(ObjectWrap, arg)); - c->setReturnVar(arg); + arg = newScriptVar(c->getArgument(0)->toNumber()); + if(data) + c->setReturnVar(arg->toObject()); + else + c->setReturnVar(arg); } + ////////////////////////////////////////////////////////////////////////// /// Boolean ////////////////////////////////////////////////////////////////////////// @@ -4671,10 +5867,11 @@ void CTinyJS::native_Boolean(const CFunctionsScopePtr &c, void *data) { if(c->getArgumentsLength()==0) arg = constScriptVar(false); else - arg = constScriptVar(c->getArgument(0)->getBool()); - CScriptVarLink *This = c->findChild("this"); - This->replaceWith(newScriptVar(ObjectWrap, arg)); - c->setReturnVar(arg); + arg = constScriptVar(c->getArgument(0)->toBoolean()); + if(data) + c->setReturnVar(arg->toObject()); + else + c->setReturnVar(arg); } ////////////////////////////////////////////////////////////////////////// @@ -4685,44 +5882,62 @@ void CTinyJS::native_Function(const CFunctionsScopePtr &c, void *data) { int length = c->getArgumentsLength(); string params, body; if(length>=1) - body = c->getArgument(length-1)->getString(); + body = c->getArgument(length-1)->toString(); if(length>=2) { - params = c->getArgument(0)->getString(); + params = c->getArgument(0)->toString(); for(int i=1; igetArgument(i)->getString()); + params.append(c->getArgument(i)->toString()); } } c->setReturnVar(parseFunctionsBodyFromString(params,body)); } -void CTinyJS::native_Function_call(const CFunctionsScopePtr &c, void *data) { +void CTinyJS::native_Function_prototype_call(const CFunctionsScopePtr &c, void *data) { int length = c->getArgumentsLength(); CScriptVarPtr Fnc = c->getArgument("this"); + if(!Fnc->isFunction()) c->throwError(TypeError, "Function.prototype.call called on incompatible Object"); CScriptVarPtr This = c->getArgument(0); - vector Params; + vector Args; for(int i=1; igetArgument(i)); - bool execute = true; - callFunction(execute, Fnc, Params, This); + Args.push_back(c->getArgument(i)); + c->setReturnVar(callFunction(Fnc, Args, This)); } -void CTinyJS::native_Function_apply(const CFunctionsScopePtr &c, void *data) { +void CTinyJS::native_Function_prototype_apply(const CFunctionsScopePtr &c, void *data) { + int length=0; CScriptVarPtr Fnc = c->getArgument("this"); - CScriptVarPtr This = c->getArgument(0); + if(!Fnc->isFunction()) c->throwError(TypeError, "Function.prototype.apply called on incompatible Object"); + // Argument_0 + CScriptVarPtr This = c->getArgument(0)->toObject(); + if(This->isNull() || This->isUndefined()) This=root; + // Argument_1 CScriptVarPtr Array = c->getArgument(1); - CScriptVarLink *Length = Array->findChild("length"); - int length = Length ? (*Length)->getInt() : 0; - vector Params; + if(!Array->isNull() && !Array->isUndefined()) { + CScriptVarLinkWorkPtr Length = Array->findChild("length"); + if(!Length) c->throwError(TypeError, "second argument to Function.prototype.apply must be an array or an array like object"); + length = Length.getter()->toNumber().toInt32(); + } + vector Args; for(int i=0; ifindChild(int2string(i)); - if(value) Params.push_back(value); - else Params.push_back(constScriptVar(Undefined)); + CScriptVarLinkPtr value = Array->findChild(int2string(i)); + if(value) Args.push_back(value); + else Args.push_back(constScriptVar(Undefined)); } - bool execute = true; - callFunction(execute, Fnc, Params, This); + c->setReturnVar(callFunction(Fnc, Args, This)); +} +void CTinyJS::native_Function_prototype_bind(const CFunctionsScopePtr &c, void *data) { + int length = c->getArgumentsLength(); + CScriptVarPtr Fnc = c->getArgument("this"); + if(!Fnc->isFunction()) c->throwError(TypeError, "Function.prototype.bind called on incompatible Object"); + CScriptVarPtr This = c->getArgument(0); + if(This->isUndefined() || This->isNull()) This = root; + vector Args; + for(int i=1; igetArgument(i)); + c->setReturnVar(newScriptVarFunctionBounded(Fnc, This, Args)); } + ////////////////////////////////////////////////////////////////////////// /// Error ////////////////////////////////////////////////////////////////////////// @@ -4731,10 +5946,10 @@ static CScriptVarPtr _newError(CTinyJS *context, ERROR_TYPES type, const CFuncti int i = c->getArgumentsLength(); string message, fileName; int line=-1, column=-1; - if(i>0) message = c->getArgument(0)->getString(); - if(i>1) fileName = c->getArgument(1)->getString(); - if(i>2) line = c->getArgument(2)->getInt(); - if(i>3) column = c->getArgument(3)->getInt(); + if(i>0) message = c->getArgument(0)->toString(); + if(i>1) fileName = c->getArgument(1)->toString(); + if(i>2) line = c->getArgument(2)->toNumber().toInt32(); + if(i>3) column = c->getArgument(3)->toNumber().toInt32(); return ::newScriptVarError(context, type, message.c_str(), fileName.c_str(), line, column); } void CTinyJS::native_Error(const CFunctionsScopePtr &c, void *data) { c->setReturnVar(_newError(this, Error,c)); } @@ -4749,12 +5964,11 @@ void CTinyJS::native_TypeError(const CFunctionsScopePtr &c, void *data){ c->setR ////////////////////////////////////////////////////////////////////////// void CTinyJS::native_eval(const CFunctionsScopePtr &c, void *data) { - string Code = c->getArgument("jsCode")->getString(); + string Code = c->getArgument("jsCode")->toString(); CScriptVarScopePtr scEvalScope = scopes.back(); // save scope scopes.pop_back(); // go back to the callers scope - CScriptVarLinkPtr returnVar; + CScriptVarLinkWorkPtr returnVar; CScriptTokenizer *oldTokenizer = t; t=0; - runtimeFlags &= ~RUNTIME_CAN_RETURN; // we can't return a function from eval-code try { CScriptTokenizer Tokenizer(Code.c_str(), "eval"); t = &Tokenizer; @@ -4784,41 +5998,31 @@ void CTinyJS::native_eval(const CFunctionsScopePtr &c, void *data) { } void CTinyJS::native_isNAN(const CFunctionsScopePtr &c, void *data) { - c->setReturnVar(constScriptVar(c->getArgument("objc")->getNumericVar()->isNaN())); + c->setReturnVar(constScriptVar(c->getArgument("objc")->toNumber().isNaN())); } void CTinyJS::native_isFinite(const CFunctionsScopePtr &c, void *data) { - CScriptVarPtr objc = c->getArgument("objc")->getNumericVar(); - c->setReturnVar(constScriptVar(!(objc->isInfinity() || objc->isNaN()))); + c->setReturnVar(constScriptVar(c->getArgument("objc")->toNumber().isFinite())); } void CTinyJS::native_parseInt(const CFunctionsScopePtr &c, void *) { - string str = c->getArgument("string")->getString(); - int radix = c->getArgument("radix")->getInt(); - char *endp = 0; - int val = strtol(str.c_str(),&endp,radix!=0 ? radix : 0); - if(endp == str.c_str()) - c->setReturnVar(c->constScriptVar(NaN)); - else - c->setReturnVar(c->newScriptVar(val)); + CNumber result; + result.parseInt(c->getArgument("string")->toString(), c->getArgument("radix")->toNumber().toInt32()); + c->setReturnVar(c->newScriptVar(result)); } void CTinyJS::native_parseFloat(const CFunctionsScopePtr &c, void *) { - string str = c->getArgument("string")->getString(); - char *endp = 0; - double val = strtod(str.c_str(),&endp); - if(endp == str.c_str()) - c->setReturnVar(c->constScriptVar(NaN)); - else - c->setReturnVar(c->newScriptVar(val)); + CNumber result; + result.parseFloat(c->getArgument("string")->toString()); + c->setReturnVar(c->newScriptVar(result)); } void CTinyJS::native_JSON_parse(const CFunctionsScopePtr &c, void *data) { - string Code = "�" + c->getArgument("text")->getString(); + string Code = "�" + c->getArgument("text")->toString(); // "�" is a spezal-token - it's for the tokenizer and means the code begins not in Statement-level - CScriptVarLinkPtr returnVar; + CScriptVarLinkWorkPtr returnVar; CScriptTokenizer *oldTokenizer = t; t=0; try { CScriptTokenizer Tokenizer(Code.c_str(), "JSON.parse", 0, -1); @@ -4867,4 +6071,3 @@ void CTinyJS::ClearUnreferedVars(const CScriptVarPtr &extra/*=CScriptVarPtr()*/) } } - diff --git a/TinyJS.h b/TinyJS.h index b7e59e8..8e2bac4 100755 --- a/TinyJS.h +++ b/TinyJS.h @@ -14,7 +14,7 @@ * * Authored / Changed By Armin Diedering * - * Copyright (C) 2010 ardisoft + * Copyright (C) 2010-2012 ardisoft * * * Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -43,19 +43,31 @@ #include #include #include -#include "pool_allocator.h" #include -#include +#include +#include -#ifndef ASSERT -# define ASSERT(X) assert(X) +#include "config.h" + +#if __cplusplus >= 201103L +# define MEMBER_DELETE =delete +#else +# define MEMBER_DELETE #endif -#ifndef __GNUC__ -# define __attribute__(x) +#ifdef NO_POOL_ALLOCATOR + template + class fixed_size_object {}; +#else +# include "pool_allocator.h" #endif #ifdef _MSC_VER +# if defined(_DEBUG) && defined(_DEBUG_NEW) +# define _AFXDLL +# include // MFC-Kern- und -Standardkomponenten +# define new DEBUG_NEW +# endif # define DEPRECATED(_Text) __declspec(deprecated(_Text)) #elif defined(__GNUC__) # define DEPRECATED(_Text) __attribute__ ((deprecated)) @@ -63,6 +75,10 @@ # define DEPRECATED(_Text) #endif +#ifndef ASSERT +# define ASSERT(X) assert(X) +#endif + #undef TRACE #ifndef TRACE #define TRACE printf @@ -125,9 +141,9 @@ enum LEX_TYPES { LEX_T_FOR_EACH_IN, #define LEX_TOKEN_LOOP_END LEX_T_FOR_EACH_IN LEX_R_IN, + LEX_T_OF, LEX_R_BREAK, LEX_R_CONTINUE, - LEX_R_FUNCTION, LEX_R_RETURN, LEX_R_VAR, LEX_R_LET, @@ -150,84 +166,50 @@ enum LEX_TYPES { // special token // LEX_T_FILE, +#define LEX_TOKEN_FUNCTION_BEGIN LEX_R_FUNCTION_PLACEHOLDER + LEX_R_FUNCTION_PLACEHOLDER, + LEX_R_FUNCTION, LEX_T_FUNCTION_OPERATOR, + LEX_T_FUNCTION_SHORT, + LEX_T_FUNCTION_OPERATOR_SHORT, LEX_T_GET, LEX_T_SET, +#define LEX_TOKEN_FUNCTION_END LEX_T_SET LEX_T_SKIP, + LEX_T_FORWARD, + LEX_T_DESTRUCTURING_VAR, + LEX_T_OBJECT_LITERAL, }; #define LEX_TOKEN_DATA_STRING(tk) ((LEX_TOKEN_STRING_BEGIN<= tk && tk <= LEX_TOKEN_STRING_END)) #define LEX_TOKEN_DATA_FLOAT(tk) (tk==LEX_FLOAT) -#define LEX_TOKEN_DATA_FUNCTION(tk) (tk==LEX_R_FUNCTION || tk==LEX_T_FUNCTION_OPERATOR || tk==LEX_T_SET || tk==LEX_T_GET) -#define LEX_TOKEN_DATA_SIMPLE(tk) (!LEX_TOKEN_DATA_STRING(tk) && !LEX_TOKEN_DATA_FLOAT(tk) && !LEX_TOKEN_DATA_FUNCTION(tk)) -/* -enum SCRIPTVAR_FLAGS { - SCRIPTVAR_UNDEFINED = 0, - SCRIPTVAR_FUNCTION = 1<<0, - SCRIPTVAR_OBJECT = 1<<1, - SCRIPTVAR_ARRAY = 1<<2, - SCRIPTVAR_DOUBLE = 1<<3, // floating point double - SCRIPTVAR_INTEGER = 1<<4, // integer number - SCRIPTVAR_BOOLEAN = 1<<5, // boolean - SCRIPTVAR_STRING = 1<<6, // string - SCRIPTVAR_NULL = 1<<7, // it seems null is its own data type - SCRIPTVAR_INFINITY = 1<<8, // it seems infinity is its own data type - SCRIPTVAR_NAN = 1<<9, // it seems NaN is its own data type - SCRIPTVAR_ACCESSOR = 1<<10, // it seems an Object with get() and set() - - SCRIPTVAR_END_OF_TYPES = 1<<15, // it seems NaN is its own data type - - SCRIPTVAR_NATIVE_FNC = 1<<16, // to specify this is a native function - SCRIPTVAR_NATIVE_MFNC = 1<<17, // to specify this is a native function from class->memberFunc - - SCRIPTVAR_NUMBERMASK = SCRIPTVAR_DOUBLE | - SCRIPTVAR_INTEGER, - SCRIPTVAR_NUMERICMASK = SCRIPTVAR_NUMBERMASK | - SCRIPTVAR_NULL | - SCRIPTVAR_BOOLEAN, - - SCRIPTVAR_VARTYPEMASK = SCRIPTVAR_END_OF_TYPES - 1, - - SCRIPTVAR_NATIVE = SCRIPTVAR_NATIVE_FNC | - SCRIPTVAR_NATIVE_MFNC, -}; -*/ +#define LEX_TOKEN_DATA_FUNCTION(tk) (LEX_TOKEN_FUNCTION_BEGIN <= tk && tk <= LEX_TOKEN_FUNCTION_END) +#define LEX_TOKEN_DATA_OBJECT_LITERAL(tk) (tk==LEX_T_OBJECT_LITERAL) +#define LEX_TOKEN_DATA_DESTRUCTURING_VAR(tk) (tk==LEX_T_DESTRUCTURING_VAR) +#define LEX_TOKEN_DATA_FORWARDER(tk) (tk==LEX_T_FORWARD) + +#define LEX_TOKEN_DATA_SIMPLE(tk) (!LEX_TOKEN_DATA_STRING(tk) && !LEX_TOKEN_DATA_FLOAT(tk) && !LEX_TOKEN_DATA_FUNCTION(tk) && !LEX_TOKEN_DATA_OBJECT_LITERAL(tk) && !LEX_TOKEN_DATA_DESTRUCTURING_VAR(tk) && !LEX_TOKEN_DATA_FORWARDER(tk)) + enum SCRIPTVARLINK_FLAGS { - SCRIPTVARLINK_OWNED = 1<<0, - SCRIPTVARLINK_WRITABLE = 1<<1, - SCRIPTVARLINK_DELETABLE = 1<<2, - SCRIPTVARLINK_ENUMERABLE = 1<<3, - SCRIPTVARLINK_HIDDEN = 1<<4, - SCRIPTVARLINK_DEFAULT = SCRIPTVARLINK_WRITABLE | SCRIPTVARLINK_DELETABLE | SCRIPTVARLINK_ENUMERABLE, - SCRIPTVARLINK_NATIVEDEFAULT = SCRIPTVARLINK_WRITABLE, + SCRIPTVARLINK_WRITABLE = 1<<0, + SCRIPTVARLINK_CONFIGURABLE = 1<<1, + SCRIPTVARLINK_ENUMERABLE = 1<<2, + SCRIPTVARLINK_DEFAULT = SCRIPTVARLINK_WRITABLE | SCRIPTVARLINK_CONFIGURABLE | SCRIPTVARLINK_ENUMERABLE, + SCRIPTVARLINK_VARDEFAULT = SCRIPTVARLINK_WRITABLE | SCRIPTVARLINK_ENUMERABLE, + SCRIPTVARLINK_BUILDINDEFAULT = SCRIPTVARLINK_WRITABLE | SCRIPTVARLINK_CONFIGURABLE, + SCRIPTVARLINK_READONLY = SCRIPTVARLINK_CONFIGURABLE, + SCRIPTVARLINK_READONLY_ENUM = SCRIPTVARLINK_CONFIGURABLE | SCRIPTVARLINK_ENUMERABLE, + SCRIPTVARLINK_CONSTANT = 0, }; enum RUNTIME_FLAGS { - RUNTIME_CAN_RETURN = 1<<0, - - RUNTIME_CAN_BREAK = 1<<1, - RUNTIME_BREAK = 1<<2, - RUNTIME_BREAK_MASK = RUNTIME_CAN_BREAK | RUNTIME_BREAK, - - RUNTIME_CAN_CONTINUE = 1<<3, - RUNTIME_CONTINUE = 1<<4, - RUNTIME_LOOP_MASK = RUNTIME_BREAK_MASK | RUNTIME_CAN_CONTINUE | RUNTIME_CONTINUE, - RUNTIME_LOOP_CAN_MASK = RUNTIME_CAN_BREAK | RUNTIME_CAN_CONTINUE, - RUNTIME_LOOP_STATE_MASK = RUNTIME_BREAK | RUNTIME_CONTINUE, - - RUNTIME_NEW = 1<<5, - - RUNTIME_CAN_THROW = 1<<6, - RUNTIME_THROW = 1<<7, + RUNTIME_BREAK = 1<<0, + RUNTIME_CONTINUE = 1<<1, + RUNTIME_CAN_THROW = 1<<2, + RUNTIME_THROW = 1<<3, RUNTIME_THROW_MASK = RUNTIME_CAN_THROW | RUNTIME_THROW, - }; -#define SAVE_RUNTIME_RETURN int old_return_runtimeFlags = runtimeFlags & RUNTIME_CAN_RETURN -#define RESTORE_RUNTIME_RETURN runtimeFlags = (runtimeFlags & ~RUNTIME_CAN_RETURN) | old_pass_runtimeFlags -#define SET_RUNTIME_CANRETURN runtimeFlags |= RUNTIME_CAN_RETURN -#define IS_RUNTIME_CANRETURN ((runtimeFlags & RUNTIME_CAN_RETURN) == RUNTIME_CAN_RETURN) - enum ERROR_TYPES { Error = 0, EvalError, @@ -266,10 +248,12 @@ typedef STRING_SET_t::iterator STRING_SET_it; /// convert the given string into a quoted string suitable for javascript std::string getJSString(const std::string &str); /// convert the given int into a string -std::string int2string(int intData); +std::string int2string(int32_t intData); +std::string int2string(uint32_t intData); /// convert the given double into a string std::string float2string(const double &floatData); + ////////////////////////////////////////////////////////////////////////// /// CScriptException ////////////////////////////////////////////////////////////////////////// @@ -282,39 +266,42 @@ class CScriptException { int lineNumber; int column; CScriptException(const std::string &Message, const std::string &File, int Line=-1, int Column=-1) : - errorType(Error), message(Message), fileName(File), lineNumber(Line+1), column(Column+1){} + errorType(Error), message(Message), fileName(File), lineNumber(Line), column(Column){} CScriptException(ERROR_TYPES ErrorType, const std::string &Message, const std::string &File, int Line=-1, int Column=-1) : - errorType(ErrorType), message(Message), fileName(File), lineNumber(Line+1), column(Column+1){} + errorType(ErrorType), message(Message), fileName(File), lineNumber(Line), column(Column){} CScriptException(const std::string &Message, const char *File="", int Line=-1, int Column=-1) : - errorType(Error), message(Message), fileName(File), lineNumber(Line+1), column(Column+1){} + errorType(Error), message(Message), fileName(File), lineNumber(Line), column(Column){} CScriptException(ERROR_TYPES ErrorType, const std::string &Message, const char *File="", int Line=-1, int Column=-1) : - errorType(ErrorType), message(Message), fileName(File), lineNumber(Line+1), column(Column+1){} + errorType(ErrorType), message(Message), fileName(File), lineNumber(Line), column(Column){} std::string toString(); }; + ////////////////////////////////////////////////////////////////////////// -/// CSCRIPTLEX +/// CScriptLex ////////////////////////////////////////////////////////////////////////// class CScriptLex { public: CScriptLex(const char *Code, const std::string &File="", int Line=0, int Column=0); - + struct POS; int tk; ///< The type of the token that we have int last_tk; ///< The type of the last token that we have - const char *tokenStart; std::string tkStr; ///< Data contained in the token we have here void check(int expected_tk, int alternate_tk=-1); ///< Lexical check wotsit void match(int expected_tk, int alternate_tk=-1); ///< Lexical match wotsit - static std::string getTokenStr(int token); ///< Get the string representation of the given token - void reset(const char *toPos, int line, const char *LineStart); ///< Reset this lex so we can start again + void reset(const POS &toPos); ///< Reset this lex so we can start again std::string currentFile; - int currentLine; - const char *currentLineStart; - int currentColumn() { return tokenStart-currentLineStart; } + struct POS { + const char *tokenStart; + int currentLine; + const char *currentLineStart; + } pos; + int currentLine() { return pos.currentLine; } + int currentColumn() { return pos.tokenStart-pos.currentLineStart; } bool lineBreakBeforeToken; private: const char *data; @@ -325,18 +312,26 @@ class CScriptLex void getNextToken(); ///< Get the text token from our text string }; -// ----------------------------------------------------------------------------------- CSCRIPTTOKEN + +////////////////////////////////////////////////////////////////////////// +/// CScriptTokenData +////////////////////////////////////////////////////////////////////////// + class CScriptToken; typedef std::vector TOKEN_VECT; typedef std::vector::iterator TOKEN_VECT_it; +typedef std::vector::const_iterator TOKEN_VECT_cit; class CScriptTokenData { -public: +protected: + CScriptTokenData() : refs(0){} virtual ~CScriptTokenData() {} +private: +// CScriptTokenData(const CScriptTokenData &noCopy); +// CScriptTokenData &operator=(const CScriptTokenData &noCopy); +public: void ref() { refs++; } void unref() { if(--refs == 0) delete this; } -protected: - CScriptTokenData() : refs(0){} private: int refs; }; @@ -346,16 +341,69 @@ class CScriptTokenDataString : public fixed_size_object, std::string tokenStr; private: }; + class CScriptTokenDataFnc : public fixed_size_object, public CScriptTokenData { public: std::string file; int line; std::string name; - STRING_VECTOR_t arguments; + TOKEN_VECT arguments; TOKEN_VECT body; + std::string getArgumentsString(); +}; + +class CScriptTokenDataForwards : public fixed_size_object, public CScriptTokenData { +public: + CScriptTokenDataForwards() {} + STRING_SET_t vars; + STRING_SET_t lets; + STRING_SET_t vars_in_letscope; + class compare_fnc_token_by_name { + public: + bool operator()(const CScriptToken& lhs, const CScriptToken& rhs) const; + }; + typedef std::set FNC_SET_t; + typedef FNC_SET_t::iterator FNC_SET_it; + FNC_SET_t functions; + bool checkRedefinition(const std::string &Str, bool checkVars); + void addVars( STRING_VECTOR_t &Vars ); + std::string addVarsInLetscope(STRING_VECTOR_t &Vars); + std::string addLets(STRING_VECTOR_t &Lets); +private: +}; + +typedef std::pair DESTRUCTURING_VAR_t; +typedef std::vector DESTRUCTURING_VARS_t; +typedef DESTRUCTURING_VARS_t::iterator DESTRUCTURING_VARS_it; +class CScriptTokenDataDestructuringVar : public fixed_size_object, public CScriptTokenData { +public: + DESTRUCTURING_VARS_t vars; + void getVarNames(STRING_VECTOR_t Name); + std::string getParsableString(); +private: +}; + +class CScriptTokenDataObjectLiteral : public fixed_size_object, public CScriptTokenData { +public: + enum {ARRAY, OBJECT} type; + int flags; + struct ELEMENT { + std::string id; + TOKEN_VECT value; + }; + bool destructuring; + bool structuring; + std::vector elements; + void setMode(bool Destructuring); + std::string getParsableString(); private: }; + +////////////////////////////////////////////////////////////////////////// +/// CScriptToken +////////////////////////////////////////////////////////////////////////// + class CScriptTokenizer; /* a Token needs 8 Byte @@ -370,7 +418,7 @@ class CScriptTokenizer; class CScriptToken : public fixed_size_object { public: - CScriptToken() : line(0), column(0), token(0) {} + CScriptToken() : line(0), column(0), token(0), intData(0) {} CScriptToken(CScriptLex *l, int Match=-1, int Alternate=-1); CScriptToken(uint16_t Tk, int IntData=0); CScriptToken(uint16_t Tk, const std::string &TkStr); @@ -379,30 +427,37 @@ class CScriptToken : public fixed_size_object ~CScriptToken() { clear(); } int &Int() { ASSERT(LEX_TOKEN_DATA_SIMPLE(token)); return intData; } - std::string &String() { ASSERT(LEX_TOKEN_DATA_STRING(token)); return stringData->tokenStr; } + std::string &String() { ASSERT(LEX_TOKEN_DATA_STRING(token)); return dynamic_cast(tokenData)->tokenStr; } double &Float() { ASSERT(LEX_TOKEN_DATA_FLOAT(token)); return *floatData; } - CScriptTokenDataFnc &Fnc() { ASSERT(LEX_TOKEN_DATA_FUNCTION(token)); return *fncData; } + CScriptTokenDataFnc &Fnc() { ASSERT(LEX_TOKEN_DATA_FUNCTION(token)); return *dynamic_cast(tokenData); } + const CScriptTokenDataFnc &Fnc() const { ASSERT(LEX_TOKEN_DATA_FUNCTION(token)); return *dynamic_cast(tokenData); } + CScriptTokenDataObjectLiteral &Object() { ASSERT(LEX_TOKEN_DATA_OBJECT_LITERAL(token)); return *dynamic_cast(tokenData); } + CScriptTokenDataDestructuringVar &DestructuringVar() { ASSERT(LEX_TOKEN_DATA_DESTRUCTURING_VAR(token)); return *dynamic_cast(tokenData); } + CScriptTokenDataForwards &Forwarder() { ASSERT(LEX_TOKEN_DATA_FORWARDER(token)); return *dynamic_cast(tokenData); } uint16_t line; uint16_t column; uint16_t token; - void print(std::string &indent); - std::string getParsableString(const std::string Indent=""); - std::string getParsableString(std::string &indentString, int &newln, const std::string &indent); ///< newln ==> 1=inject indentString, 2=inject \n, 3=last='{' + + static std::string getParsableString(TOKEN_VECT &Tokens, const std::string &IndentString="", const std::string &Indent=""); + static std::string getParsableString(TOKEN_VECT_it Begin, TOKEN_VECT_it End, const std::string &IndentString="", const std::string &Indent=""); + static std::string getTokenStr( int token, bool *need_space=0 ); + static const char *isReservedWord(int Token); + static int isReservedWord(const std::string &Str); private: void clear(); union { - int intData; - double *floatData; - CScriptTokenDataString *stringData; - CScriptTokenDataFnc *fncData; + int intData; + double *floatData; + CScriptTokenData *tokenData; }; }; -// ----------------------------------------------------------------------------------- CSCRIPTTOKENIZER -/* - the tokenizer converts the code in an Vector with Tokens -*/ + +////////////////////////////////////////////////////////////////////////// +/// CScriptTokenizer - converts the code in a vector with tokens +////////////////////////////////////////////////////////////////////////// + class CScriptTokenizer { public: @@ -437,7 +492,7 @@ class CScriptTokenizer std::string currentFile; int currentLine() { return getPos().currentLine();} int currentColumn() { return getPos().currentColumn();} - std::string &tkStr() { return getToken().String(); } + const std::string &tkStr() { static std::string empty; return LEX_TOKEN_DATA_STRING(getToken().token)?getToken().String():empty; } private: void tokenizeCatch(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); void tokenizeTry(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); @@ -447,21 +502,33 @@ class CScriptTokenizer void tokenizeDo(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); void tokenizeIf(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); void tokenizeFor(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); + void _tokenizeDeconstructionVarObject(DESTRUCTURING_VARS_t &Vars, STRING_VECTOR_t &VarNames); + void _tokenizeDeconstructionVarArray(DESTRUCTURING_VARS_t &Vars, STRING_VECTOR_t &VarNames); + void _tokenizeDestructionVar(DESTRUCTURING_VARS_t &Vars, const std::string &Path, STRING_VECTOR_t &VarNames); + CScriptToken tokenizeDestructionVar(STRING_VECTOR_t &VarNames); void tokenizeFunction(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); void tokenizeLet(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); void tokenizeVar(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); - - void tokenizeLiteral(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); - void tokenizeMember(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); - void tokenizeFunctionCall(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); - void tokenizeSubExpression(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); - void tokenizeCondition(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); + void _tokenizeLiteralObject(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags, int &State); + void _tokenizeLiteralArray(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags, int &State); + void tokenizeObjectLiteral(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags, int &State); + + void tokenizeLiteral(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags, int &State); + void tokenizeMember(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags, int &State); + void tokenizeFunctionCall(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags, int &State); + void tokenizeSubExpression(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags, int &State); + void tokenizeCondition(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags, int &State); void tokenizeAssignment(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); + void tokenizeAssignment(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags, int &State); void tokenizeExpression(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); void tokenizeBlock(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); void tokenizeStatement(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); int pushToken(TOKEN_VECT &Tokens, int Match=-1, int Alternate=-1); + int pushToken(TOKEN_VECT &Tokens, const CScriptToken &Token); + CScriptTokenDataForwards &pushForwarder(TOKEN_VECT &Tokens, std::vector &BlockStart); + void removeEmptyForwarder(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks); + CScriptTokenDataForwards &__getForwarder(TOKEN_VECT &Tokens, int Pos, std::vector &BlockStart, std::vector &Marks); void throwTokenNotExpected(); CScriptLex *l; TOKEN_VECT tokens; @@ -474,11 +541,16 @@ class CScriptTokenizer /// forward-declaration ////////////////////////////////////////////////////////////////////////// +class CNumber; class CScriptVar; class CScriptVarPtr; template class CScriptVarPointer; class CScriptVarLink; class CScriptVarLinkPtr; +class CScriptVarLinkWorkPtr; + +class CScriptVarPrimitive; +typedef CScriptVarPointer CScriptVarPrimitivePtr; class CScriptVarScopeFnc; typedef CScriptVarPointer CFunctionsScopePtr; @@ -487,12 +559,11 @@ typedef void (*JSCallback)(const CFunctionsScopePtr &var, void *userdata); class CTinyJS; - ////////////////////////////////////////////////////////////////////////// /// CScriptVar ////////////////////////////////////////////////////////////////////////// -typedef std::vector SCRIPTVAR_CHILDS_t; +typedef std::vector SCRIPTVAR_CHILDS_t; typedef SCRIPTVAR_CHILDS_t::iterator SCRIPTVAR_CHILDS_it; typedef SCRIPTVAR_CHILDS_t::const_iterator SCRIPTVAR_CHILDS_cit; @@ -501,70 +572,126 @@ class CScriptVar : public fixed_size_object { CScriptVar(CTinyJS *Context, const CScriptVarPtr &Prototype); ///< Create CScriptVar(const CScriptVar &Copy); ///< Copy protected -> use clone for public private: - CScriptVar & operator=(const CScriptVar &Copy); ///< private -> no assignment-Copy + CScriptVar & operator=(const CScriptVar &Copy) MEMBER_DELETE; ///< private -> no assignment-Copy public: - virtual ~CScriptVar(void); + virtual ~CScriptVar(); virtual CScriptVarPtr clone()=0; /// Type - virtual bool isObject(); ///< is CScriptVarObject - virtual bool isError(); ///< is CScriptVarObject - virtual bool isArray(); ///< is CScriptVarArray - virtual bool isAccessor(); ///< is CScriptVarAccessor - virtual bool isNull(); ///< is CScriptVarNull - virtual bool isUndefined();///< is CScriptVarUndefined - virtual bool isNaN(); ///< is CScriptVarNaN - virtual bool isString(); ///< is CScriptVarString - virtual bool isInt(); ///< is CScriptVarInteger - virtual bool isBool(); ///< is CScriptVarBool - virtual int isInfinity(); //< is CScriptVarInfinity ///< +1==POSITIVE_INFINITY, -1==NEGATIVE_INFINITY, 0==is not an InfinityVar - virtual bool isDouble(); ///< is CScriptVarDouble - - virtual bool isNumber(); ///< is CScriptVarInteger / CScriptVarDouble - virtual bool isNumeric(); ///< is CScriptVarNull + virtual bool isObject(); ///< is an Object + virtual bool isArray(); ///< is an Array + virtual bool isError(); ///< is an ErrorObject + virtual bool isRegExp(); ///< is a RegExpObject + virtual bool isAccessor(); ///< is an Accessor + virtual bool isNull(); ///< is Null + virtual bool isUndefined();///< is Undefined + virtual bool isNaN(); ///< is NaN + virtual bool isString(); ///< is String + virtual bool isInt(); ///< is Integer + virtual bool isBool(); ///< is Bool + virtual int isInfinity(); ///< is Infinity ///< +1==POSITIVE_INFINITY, -1==NEGATIVE_INFINITY, 0==is not an InfinityVar + virtual bool isDouble(); ///< is Double + + virtual bool isRealNumber(); ///< is isInt | isDouble + virtual bool isNumber(); ///< is isNaN | isInt | isDouble | isInfinity virtual bool isPrimitive();///< isNull | isUndefined | isNaN | isString | isInt | isDouble | isInfinity virtual bool isFunction(); ///< is CScriptVarFunction / CScriptVarFunctionNativeCallback / CScriptVarFunctionNativeClass virtual bool isNative(); ///< is CScriptVarFunctionNativeCallback / CScriptVarFunctionNativeClass + virtual bool isBounded(); ///< is CScriptVarFunctionBounded bool isBasic() { return Childs.empty(); } ///< Is this *not* an array/object/etc + ////////////////////////////////////////////////////////////////////////// /// Value - virtual int getInt(); ///< return 0 - virtual bool getBool(); ///< return false - virtual double getDouble(); ///< return 0.0 - virtual CScriptTokenDataFnc *getFunctionData(); ///< { return 0; } - virtual std::string getString(); ///< return "" + ////////////////////////////////////////////////////////////////////////// - virtual std::string getParsableString(const std::string &indentString, const std::string &indent); ///< get Data as a parsable javascript string + virtual CScriptVarPrimitivePtr getRawPrimitive()=0; ///< is Var==Primitive -> return this isObject return Value + CScriptVarPrimitivePtr toPrimitive(); ///< by default call getDefaultValue_hintNumber by a Date-object calls getDefaultValue_hintString + virtual CScriptVarPrimitivePtr toPrimitive(bool &execute); ///< if the var an ObjectType gets the valueOf; if valueOf of an ObjectType gets toString / otherwise gets the Var itself + CScriptVarPrimitivePtr toPrimitive_hintString(int32_t radix=0); ///< if the var an ObjectType gets the valueOf; if valueOf of an ObjectType gets toString / otherwise gets the Var itself + CScriptVarPrimitivePtr toPrimitive_hintString(bool &execute, int32_t radix=0); ///< if the var an ObjectType gets the valueOf; if valueOf of an ObjectType gets toString / otherwise gets the Var itself + CScriptVarPrimitivePtr toPrimitive_hintNumber(); ///< if the var an ObjectType gets the valueOf; if valueOf of an ObjectType gets toString / otherwise gets the Var itself + CScriptVarPrimitivePtr toPrimitive_hintNumber(bool &execute); ///< if the var an ObjectType gets the valueOf; if valueOf of an ObjectType gets toString / otherwise gets the Var itself + + CScriptVarPtr callJS_toString(bool &execute, int radix=0); + virtual CScriptVarPtr toString_CallBack(bool &execute, int radix=0); + CScriptVarPtr callJS_valueOf(bool &execute); + virtual CScriptVarPtr valueOf_CallBack(); + + CNumber toNumber(); + CNumber toNumber(bool &execute); + virtual bool toBoolean(); + std::string toString(int32_t radix=0); ///< shortcut for this->toPrimitive_hintString()->toCString(); + std::string toString(bool &execute, int32_t radix=0); ///< shortcut for this->toPrimitive_hintString(execute)->toCString(); +#define WARN_DEPRECATED +#ifdef WARN_DEPRECATED + int DEPRECATED("getInt() is deprecated use toNumber().toInt32 instead") getInt(); + bool DEPRECATED("getBool() is deprecated use toBoolean() instead") getBool(); + double DEPRECATED("getDouble() is deprecated use toNumber().toDouble() instead") getDouble(); + std::string DEPRECATED("getString() is deprecated use toString() instead") getString(); +#else + int getInt(); + bool getBool(); + double getDouble(); + std::string getString(); +#endif + virtual CScriptTokenDataFnc *getFunctionData(); ///< { return 0; } + + virtual CScriptVarPtr toObject()=0; + +// virtual std::string getParsableString(const std::string &indentString, const std::string &indent, bool &hasRecursion); ///< get Data as a parsable javascript string +#define getParsableStringRecursionsCheck() do{ \ + if(uniqueID && uniqueID==temporaryID) { hasRecursion=true; return "recursion"; } \ + temporaryID = uniqueID; \ + } while(0) + virtual std::string getParsableString(const std::string &indentString, const std::string &indent, uint32_t uniqueID, bool &hasRecursion); ///< get Data as a parsable javascript string virtual std::string getVarType()=0; - CScriptVarPtr getPrimitivVar(); ///< if the var an ObjectType gets the valueOf; if valueOf of an ObjectType gets toString / otherwise gets the Var itself - CScriptVarPtr getPrimitivVar(bool execute); ///< if the var an ObjectType gets the valueOf; if valueOf of an ObjectType gets toString / otherwise gets the Var itself - virtual CScriptVarPtr getNumericVar(); ///< returns an Integer, a Double, an Infinity or a NaN - CScriptVarPtr valueOf(bool execute); - virtual CScriptVarPtr _valueOf(bool execute); - CScriptVarPtr toString(bool execute, int radix=0); - virtual CScriptVarPtr _toString(bool execute, int radix=0); + +#ifdef WARN_DEPRECATED + CScriptVarPtr DEPRECATED("getNumericVar() is deprecated use toNumber() instead") getNumericVar(); ///< returns an Integer, a Double, an Infinity or a NaN +#else + CScriptVarPtr getNumericVar(); ///< returns an Integer, a Double, an Infinity or a NaN +#endif + + ////////////////////////////////////////////////////////////////////////// + /// Childs + ////////////////////////////////////////////////////////////////////////// + + + CScriptVarPtr getOwnPropertyDescriptor(const std::string &Name); + const char *defineProperty(const std::string &Name, CScriptVarPtr Attributes); + + /// flags + void setExtensible(bool On=true) { extensible=On; } + void preventExtensions() { extensible=false; } + bool isExtensible() const { return extensible; } + void seal(); + bool isSealed() const; + void freeze(); + bool isFrozen() const; + /// find - CScriptVarLink *findChild(const std::string &childName); ///< Tries to find a child with the given name, may return 0 - CScriptVarLink *findChildInPrototypeChain(const std::string &childName); - CScriptVarLink *findChildWithPrototypeChain(const std::string &childName); - CScriptVarLink *findChildByPath(const std::string &path); ///< Tries to find a child with the given path (separated by dots) - CScriptVarLink *findChildOrCreate(const std::string &childName/*, int varFlags=SCRIPTVAR_UNDEFINED*/); ///< Tries to find a child with the given name, or will create it with the given flags - CScriptVarLink *findChildOrCreateByPath(const std::string &path); ///< Tries to find a child with the given path (separated by dots) + CScriptVarLinkPtr findChild(const std::string &childName); ///< Tries to find a child with the given name, may return 0 + CScriptVarLinkWorkPtr findChildWithStringChars(const std::string &childName); + CScriptVarLinkPtr findChildInPrototypeChain(const std::string &childName); + CScriptVarLinkWorkPtr findChildWithPrototypeChain(const std::string &childName); + CScriptVarLinkPtr findChildByPath(const std::string &path); ///< Tries to find a child with the given path (separated by dots) + CScriptVarLinkPtr findChildOrCreate(const std::string &childName/*, int varFlags=SCRIPTVAR_UNDEFINED*/); ///< Tries to find a child with the given name, or will create it with the given flags + CScriptVarLinkPtr findChildOrCreateByPath(const std::string &path); ///< Tries to find a child with the given path (separated by dots) void keys(STRING_SET_t &Keys, bool OnlyEnumerable=true, uint32_t ID=0); /// add & remove - CScriptVarLink *addChild(const std::string &childName, const CScriptVarPtr &child, int linkFlags = SCRIPTVARLINK_DEFAULT); - CScriptVarLink *addChildNoDup(const std::string &childName, const CScriptVarPtr &child, int linkFlags = SCRIPTVARLINK_DEFAULT); ///< add a child overwriting any with the same name + CScriptVarLinkPtr addChild(const std::string &childName, const CScriptVarPtr &child, int linkFlags = SCRIPTVARLINK_DEFAULT); + CScriptVarLinkPtr DEPRECATED("addChildNoDup is deprecated use addChildOrReplace instead!") addChildNoDup(const std::string &childName, const CScriptVarPtr &child, int linkFlags = SCRIPTVARLINK_DEFAULT); + CScriptVarLinkPtr addChildOrReplace(const std::string &childName, const CScriptVarPtr &child, int linkFlags = SCRIPTVARLINK_DEFAULT); ///< add a child overwriting any with the same name bool removeLink(CScriptVarLinkPtr &link); ///< Remove a specific link (this is faster than finding via a child) - bool removeLink(CScriptVarLink *&link); ///< Remove a specific link (this is faster than finding via a child) void removeAllChildren(); /// ARRAY - CScriptVarPtr getArrayIndex(int idx); ///< The the value at an array index - void setArrayIndex(int idx, const CScriptVarPtr &value); ///< Set the value at an array index - int getArrayLength(); ///< If this is an array, return the number of items in it (else 0) + CScriptVarPtr getArrayIndex(uint32_t idx); ///< The the value at an array index + void setArrayIndex(uint32_t idx, const CScriptVarPtr &value); ///< Set the value at an array index + uint32_t getArrayLength(); ///< If this is an array, return the number of items in it (else 0) ////////////////////////////////////////////////////////////////////////// int getChildren() { return Childs.size(); } ///< Get the number of children @@ -589,12 +716,15 @@ class CScriptVar : public fixed_size_object { template T *get(){ T *ret = dynamic_cast(this); ASSERT(ret!=0); return ret; } + //CScriptVarPtr newScriptVar(const CNumber &t); // { return ::newScriptVar(context, t); } template CScriptVarPtr newScriptVar(T t); // { return ::newScriptVar(context, t); } + template CScriptVarPtr newScriptVar(T1 t1, T2 t2); // { return ::newScriptVar(context, t); } template const CScriptVarPtr &constScriptVar(T t); // { return ::newScriptVar(context, t); } void setTemporaryID(uint32_t ID) { temporaryID = ID; } virtual void setTemporaryID_recursive(uint32_t ID); uint32_t getTempraryID() { return temporaryID; } protected: + bool extensible; CTinyJS *context; int refs; ///< The number of references held to this - used for garbage collection CScriptVar *prev; @@ -605,17 +735,7 @@ class CScriptVar : public fixed_size_object { friend class CScriptVarPtr; }; -////////////////////////////////////////////////////////////////////////// -/// CScriptVarPrimitive -////////////////////////////////////////////////////////////////////////// - -class CScriptVarPrimitive : public CScriptVar { -protected: - CScriptVarPrimitive(CTinyJS *Context, const CScriptVarPtr &Prototype) : CScriptVar(Context, Prototype) {} - CScriptVarPrimitive(const CScriptVarPrimitive &Copy) : CScriptVar(Copy) {} -public: -}; ////////////////////////////////////////////////////////////////////////// /// CScriptVarPtr ////////////////////////////////////////////////////////////////////////// @@ -626,22 +746,8 @@ class CScriptVarPtr { CScriptVarPtr() : var(0) {} ///< 0-Pointer CScriptVarPtr(CScriptVar *Var) : var(Var) { if(var) var->ref(); } // creates a new CScriptVar (from new); - - CScriptVarPtr(const CScriptVarLink *Link); -// CScriptVarPtr(bool &execute, const CScriptVarLink *Link); - CScriptVarPtr(const CScriptVarLinkPtr &Link); - - // from CScriptVarLinkPtr -// CScriptVarPtr(bool &execute, const CScriptVarLinkPtr &Link);// : var(0) { *this = Link.get(execute); } -// CScriptVarPtr(const CScriptVarLinkPtr &Link);// : var(0) { *this = Link.get(); } -// CScriptVarPtr & operator =(const CScriptVarLinkPtr &Link); // { *this = 10; } -public: - // CScriptVarPtr & operator =(const CScriptVarLinkPtr &Link, bool &execute) : { - // *this = Link.get(execute)); - // } - // copy - CScriptVarPtr(const CScriptVarPtr &Copy) : var(0) { *this = Copy; } + CScriptVarPtr(const CScriptVarPtr &Copy) : var(Copy.var) { if(var) var->ref(); } CScriptVarPtr& operator=(const CScriptVarPtr &Copy) { if(var != Copy.var) { if(var) var->unref(); @@ -652,17 +758,16 @@ class CScriptVarPtr { // deconstruct ~CScriptVarPtr() { if(var) var->unref(); } + // if operator bool() const { return var!=0; } bool operator ==(const CScriptVarPtr &Other) const { return var == Other.var; } bool operator !=(const CScriptVarPtr &Other) const { return var != Other.var; } + // access CScriptVar * operator ->() const { return var; } CScriptVar *getVar() const { return var; } - /// temporary - // CScriptVarPtr(CScriptVarLink *Var); - void clear() { if(var) var->unref(); var=0; } protected: CScriptVar *var; @@ -676,320 +781,206 @@ template class CScriptVarPointer : public CScriptVarPtr { public: CScriptVarPointer() {} - CScriptVarPointer(const CScriptVarPtr &Copy) : CScriptVarPtr(Copy) { if(var) { var = dynamic_cast(var); } } -// CScriptVarPointer(const CScriptVarPointer &Copy) : CScriptVarPtr(Copy) { if(var) { var = dynamic_cast(var); } } + CScriptVarPointer(CScriptVar *Var) : CScriptVarPtr(dynamic_cast(Var)) {} + CScriptVarPointer(const CScriptVarPtr &Copy) : CScriptVarPtr(dynamic_cast(Copy.getVar())) {} C * operator ->() const { C *Var = dynamic_cast(var); ASSERT(var && Var); return Var; } }; + ////////////////////////////////////////////////////////////////////////// /// CScriptVarLink ////////////////////////////////////////////////////////////////////////// - - class CScriptVarLink : public fixed_size_object { -public: +private: // prevent gloabal creating CScriptVarLink(const CScriptVarPtr &var, const std::string &name = TINYJS_TEMP_NAME, int flags = SCRIPTVARLINK_DEFAULT); - CScriptVarLink(const CScriptVarLink &link); ///< Copy constructor +private: // prevent Copy + CScriptVarLink(const CScriptVarLink &link) MEMBER_DELETE; ///< Copy constructor +public: ~CScriptVarLink(); const std::string &getName() const { return name; } int getFlags() { return flags; } - const CScriptVarPtr &getVarPtr() const { return var; } - CScriptVarPtr &setVarPtr(const CScriptVarPtr &Var) { return var = Var; } - - void replaceWith(const CScriptVarPtr &newVar); ///< simple Replace the Variable pointed to + const CScriptVarPtr &setVarPtr(const CScriptVarPtr &Var) { return var = Var; } ///< simple Replace the Variable pointed to - bool isOwned() const { return (flags & SCRIPTVARLINK_OWNED) != 0; } - void setOwned(bool On) { On ? (flags |= SCRIPTVARLINK_OWNED) : (flags &= ~SCRIPTVARLINK_OWNED); } - bool isOwner() const { return owner!=0; } + bool isOwned() const { return owner!=0; } bool isWritable() const { return (flags & SCRIPTVARLINK_WRITABLE) != 0; } - void setWritable(bool On) { On ? (flags |= SCRIPTVARLINK_WRITABLE) : (flags &= ~SCRIPTVARLINK_DELETABLE); } - bool isDeletable() const { return (flags & SCRIPTVARLINK_DELETABLE) != 0; } - void setDeletable(bool On) { On ? (flags |= SCRIPTVARLINK_DELETABLE) : (flags &= ~SCRIPTVARLINK_DELETABLE); } + void setWritable(bool On) { On ? (flags |= SCRIPTVARLINK_WRITABLE) : (flags &= ~SCRIPTVARLINK_WRITABLE); } + bool isConfigurable() const { return (flags & SCRIPTVARLINK_CONFIGURABLE) != 0; } + void setConfigurable(bool On) { On ? (flags |= SCRIPTVARLINK_CONFIGURABLE) : (flags &= ~SCRIPTVARLINK_CONFIGURABLE); } bool isEnumerable() const { return (flags & SCRIPTVARLINK_ENUMERABLE) != 0; } void setEnumerable(bool On) { On ? (flags |= SCRIPTVARLINK_ENUMERABLE) : (flags &= ~SCRIPTVARLINK_ENUMERABLE); } - bool isHidden() const { return (flags & SCRIPTVARLINK_HIDDEN) != 0; } - void setHidden(bool On) { On ? (flags |= SCRIPTVARLINK_HIDDEN) : (flags &= ~SCRIPTVARLINK_HIDDEN); } - - const CScriptVarPtr &operator ->() const { return var; } - const CScriptVarPtr &operator *() const { return var; } - // TODO remove this (make's saver) CScriptVar *getOwner() { return owner; }; void setOwner(CScriptVar *Owner) { owner = Owner; } -protected: + + /// forward to ScriptVar + + CScriptVarPrimitivePtr toPrimitive() { ///< by default call getDefaultValue_hintNumber by a Date-object calls getDefaultValue_hintString + return var->toPrimitive(); } + CScriptVarPrimitivePtr toPrimitive(bool &execute) { ///< if the var an ObjectType gets the valueOf; if valueOf of an ObjectType gets toString / otherwise gets the Var itself + return var->toPrimitive(execute); } + CScriptVarPrimitivePtr toPrimitive_hintString(int32_t radix=0) { ///< if the var an ObjectType gets the valueOf; if valueOf of an ObjectType gets toString / otherwise gets the Var itself + return var->toPrimitive_hintString(radix); } + CScriptVarPrimitivePtr toPrimitive_hintString(bool &execute, int32_t radix=0) { ///< if the var an ObjectType gets the valueOf; if valueOf of an ObjectType gets toString / otherwise gets the Var itself + return var->toPrimitive_hintString(execute, radix); } + CScriptVarPrimitivePtr toPrimitive_hintNumber() { ///< if the var an ObjectType gets the valueOf; if valueOf of an ObjectType gets toString / otherwise gets the Var itself + return var->toPrimitive_hintNumber(); } + CScriptVarPrimitivePtr toPrimitive_hintNumber(bool &execute) { ///< if the var an ObjectType gets the valueOf; if valueOf of an ObjectType gets toString / otherwise gets the Var itself + return var->toPrimitive_hintNumber(execute); } + + CNumber toNumber(); // { return var->toNumber(); } + CNumber toNumber(bool &execute); // { return var->toNumber(execute); } + bool toBoolean() { return var->toBoolean(); } + std::string toString(int32_t radix=0) { ///< shortcut for this->toPrimitive_hintString()->toCString(); + return var->toString(radix); } + std::string toString(bool &execute, int32_t radix=0) { ///< shortcut for this->toPrimitive_hintString(execute)->toCString(); + return var->toString(execute, radix); } + CScriptVarPtr toObject() { return var->toObject(); }; + +private: std::string name; CScriptVar *owner; // pointer to the owner CScriptVar uint32_t flags; CScriptVarPtr var; +#ifdef _DEBUG char dummy[24]; - friend class CScriptVarLinkPtr; -}; - -// to create a CScriptVarPtr always call getValue to call getter if's needed -inline CScriptVarPtr::CScriptVarPtr(const CScriptVarLink *Link) : var(0) { if(Link) { var = Link->getVarPtr()->ref(); } } - - -////////////////////////////////////////////////////////////////////////// -/// CScriptVarLinkTmp / CScriptVarLinkTmpPtr -////////////////////////////////////////////////////////////////////////// -class CScriptVarLinkTmp : public CScriptVarLink{ +#endif + CScriptVarLink *ref(); + void unref(); private: - CScriptVarLinkTmp(const CScriptVarPtr &var, const std::string &name = TINYJS_TEMP_NAME, int flags = SCRIPTVARLINK_DEFAULT) : CScriptVarLink(var, name, flags) { - refs=1; - } int refs; - CScriptVarLinkTmp *ref() { refs++; return this;} - void unref() { if(--refs == 0) delete this; } - friend class CScriptVarLinkTmpPtr; -}; -class CScriptVarLinkTmpPtr{ -public: - CScriptVarLinkTmpPtr() : link(0) {} - CScriptVarLinkTmpPtr(const CScriptVarPtr &var, const std::string &name = TINYJS_TEMP_NAME, int flags = SCRIPTVARLINK_DEFAULT) { - link = new CScriptVarLinkTmp(var, name, flags); - } - CScriptVarLinkTmpPtr(const CScriptVarLinkTmpPtr &Copy) : link(Copy.link) { if(link) link->ref(); } - CScriptVarLinkTmpPtr &operator=(const CScriptVarLinkTmpPtr &Copy) { - if(link!=Copy.link) { if(link) link->unref(); link=Copy.link; if(link) link->ref(); } return *this; } - ~CScriptVarLinkTmpPtr() { if(link) link->unref(); } - operator bool() const { return link != 0; } - int refs() { return link ? link->refs : 0; } - CScriptVarLink *operator->() const { return link; } - operator CScriptVarLink *() const { return link; } - -private: - CScriptVarLinkTmp *link; + friend class CScriptVarLinkPtr; }; - - ////////////////////////////////////////////////////////////////////////// -/// CScriptVarSmartLink +/// CScriptVarLinkPtr ////////////////////////////////////////////////////////////////////////// -class CScriptVarLinkPtr : public fixed_size_object -{ -public: - // constructors - CScriptVarLinkPtr() : link(0) {} - CScriptVarLinkPtr(CScriptVarLink *Link) : link(0) { *this = Link; } - CScriptVarLinkPtr (CScriptVarPtr Var, const std::string &Name = TINYJS_TEMP_NAME, int Flags = SCRIPTVARLINK_DEFAULT) : link(0),tmp_link(Var, Name, Flags) { } - - // copy - CScriptVarLinkPtr (const CScriptVarLinkPtr &Link) : link(0) { *this = Link; } - CScriptVarLinkPtr &operator = (const CScriptVarLinkPtr &Link) { link = Link.link; tmp_link = Link.tmp_link; return *this; } - - // assign - CScriptVarLinkPtr &operator = (CScriptVarLink *Link) { return (*this)(Link); } - CScriptVarLinkPtr &operator = (const CScriptVarPtr &Var) { link = 0; set_tmp_link(Var); return *this; } - CScriptVarLinkPtr &operator()(CScriptVarLink *Link); - CScriptVarLinkPtr &operator()(bool &execute, CScriptVarLink *Link); - CScriptVarLinkPtr &operator()(const CScriptVarPtr &Var, const std::string &Name = TINYJS_TEMP_NAME, int Flags = SCRIPTVARLINK_DEFAULT) { link = 0; set_tmp_link(Var, Name, Flags); return *this; } - - -// void setLink(CScriptVarLink *Link); -// void setLink(bool &execute, CScriptVarLink *Link); - -// void setVarPtr(const CScriptVarPtr &Var, const std::string &Name = TINYJS_TEMP_NAME, int Flags = SCRIPTVARLINK_DEFAULT) { link = 0; set_tmp_link(Var, Name, Flags); } - // operator - CScriptVarLink *operator ->() { return getLink(); } - const CScriptVarLink *operator ->() const { return getLink(); } - CScriptVarLink &operator *() { return *getLink(); } - - void replaceVar(bool &execute, const CScriptVarPtr &Var); - - operator bool() const { return getLink()!=0; } - // operator CScriptVarLink *() { return link; } - CScriptVarLink *getLink() { return (tmp_link && tmp_link->var)?tmp_link:link; }; - CScriptVarLink *getRealLink() { return link; }; - const CScriptVarLink *getLink() const { return (tmp_link && tmp_link->var)?tmp_link:link; }; - void clear() { link=0; clear_tmp_link(); } - void swap(CScriptVarLinkPtr &Link); -private: - void set_tmp_link(const CScriptVarPtr &Var, const std::string &Name = TINYJS_TEMP_NAME, int Flags = SCRIPTVARLINK_DEFAULT); - void clear_tmp_link(); - - CScriptVarLink *link; - CScriptVarLinkTmpPtr tmp_link; -}; -inline CScriptVarPtr::CScriptVarPtr(const CScriptVarLinkPtr &Link) : var(0) { if(Link) { var = Link->getVarPtr()->ref(); } } - -////////////////////////////////////////////////////////////////////////// -#define define_dummy_t(t1) struct t1##_t{}; extern t1##_t t1 -#define declare_dummy_t(t1) t1##_t t1 -#define define_newScriptVar_Fnc(t1, ...) CScriptVarPtr newScriptVar(__VA_ARGS__) -#define define_newScriptVar_NamedFnc(t1, ...) CScriptVarPtr newScriptVar##t1(__VA_ARGS__) -#define define_ScriptVarPtr_Type(t1) class CScriptVar##t1; typedef CScriptVarPointer CScriptVar##t1##Ptr +class CScriptVarLinkPtr { +public: + // construct + CScriptVarLinkPtr() : link(0) {} ///< 0-Pointer + CScriptVarLinkPtr(const CScriptVarPtr &var, const std::string &name = TINYJS_TEMP_NAME, int flags = SCRIPTVARLINK_DEFAULT) { link=(new CScriptVarLink(var, name, flags))->ref(); } + CScriptVarLinkPtr(CScriptVarLink *Link) : link(Link) { if(link) link->ref(); } // creates a new CScriptVarLink (from new); -#define define_DEPRECATED_newScriptVar_Fnc(t1, ...) CScriptVarPtr DEPRECATED("newScriptVar("#__VA_ARGS__") is deprecated use constScriptVar("#__VA_ARGS__") instead") newScriptVar(__VA_ARGS__) -////////////////////////////////////////////////////////////////////////// -/// CScriptVarObject -////////////////////////////////////////////////////////////////////////// + // reconstruct + CScriptVarLinkPtr &operator()(const CScriptVarPtr &var, const std::string &name = TINYJS_TEMP_NAME, int flags = SCRIPTVARLINK_DEFAULT); + CScriptVarLinkPtr &operator=(const CScriptVarPtr &var) { return operator()(var); } + // deconstruct + ~CScriptVarLinkPtr() { if(link) link->unref(); } -define_dummy_t(Object); -define_ScriptVarPtr_Type(Object); + // copy + CScriptVarLinkPtr(const CScriptVarLinkPtr &Copy) : link(Copy.link) { if(link) link->ref(); } + CScriptVarLinkPtr &operator=(const CScriptVarLinkPtr &Copy) { + if(link != Copy.link) { + if(link) link->unref(); + link = Copy.link; if(link) link->ref(); + } + return *this; + } -class CScriptVarObject : public CScriptVar { -protected: - CScriptVarObject(CTinyJS *Context); - CScriptVarObject(CTinyJS *Context, const CScriptVarPtr &Prototype) : CScriptVar(Context, Prototype) {} - CScriptVarObject(const CScriptVarObject &Copy) : CScriptVar(Copy) {} ///< Copy protected -> use clone for public -public: - virtual ~CScriptVarObject(); - virtual CScriptVarPtr clone(); + // getter & setter + CScriptVarLinkWorkPtr getter(); + CScriptVarLinkWorkPtr getter(bool &execute); + CScriptVarLinkWorkPtr setter(const CScriptVarPtr &Var); + CScriptVarLinkWorkPtr setter(bool &execute, const CScriptVarPtr &Var); - virtual bool isObject(); // { return true; } - virtual bool isPrimitive(); // { return false; } + // if + operator bool() const { return link!=0; } - virtual int getInt(); - virtual bool getBool(); - virtual double getDouble(); - virtual std::string getString(); // { return "[ Object ]"; }; - virtual std::string getParsableString(const std::string &indentString, const std::string &indent); - virtual std::string getVarType(); // { return "object"; } + // for sorting in child-list + bool operator <(const std::string &rhs) const; + bool operator ==(const CScriptVarLinkPtr &rhs) const { return link==rhs.link; } + // access to CScriptVarLink + CScriptVarLink *operator ->() const { return link; } - virtual CScriptVarPtr _toString(bool execute, int radix=0); -private: - friend define_newScriptVar_Fnc(Object, CTinyJS *Context, Object_t); - friend define_newScriptVar_Fnc(Object, CTinyJS *Context, Object_t, const CScriptVarPtr &); -}; -inline define_newScriptVar_Fnc(Object, CTinyJS *Context, Object_t) { return new CScriptVarObject(Context); } -inline define_newScriptVar_Fnc(Object, CTinyJS *Context, Object_t, const CScriptVarPtr &Prototype) { return new CScriptVarObject(Context, Prototype); } + operator const CScriptVarPtr &() const { static CScriptVarPtr NullPtr; return link?link->getVarPtr():NullPtr; } + void clear() { if(link) link->unref(); link=0; } +protected: + CScriptVarLink *link; +}; -////////////////////////////////////////////////////////////////////////// -/// CScriptVarObjectWrap -////////////////////////////////////////////////////////////////////////// -define_dummy_t(ObjectWrap); -define_ScriptVarPtr_Type(ObjectWrap); +////////////////////////////////////////////////////////////////////////// +/// CScriptVarLinkWorkPtr +////////////////////////////////////////////////////////////////////////// -class CScriptVarObjectWrap : public CScriptVarObject { -protected: - CScriptVarObjectWrap(CTinyJS *Context, const CScriptVarPtr &Value);// : CScriptVarObject(Context), value(Value) {} - CScriptVarObjectWrap(const CScriptVarObjectWrap &Copy) : CScriptVarObject(Copy), value(Copy.value) {} ///< Copy protected -> use clone for public +class CScriptVarLinkWorkPtr : public CScriptVarLinkPtr { public: - virtual ~CScriptVarObjectWrap(); - virtual CScriptVarPtr clone(); - - virtual std::string getParsableString(const std::string &indentString, const std::string &indent); ///< get Data as a parsable javascript string + // construct + CScriptVarLinkWorkPtr() {} + CScriptVarLinkWorkPtr(const CScriptVarPtr &var, const std::string &name = TINYJS_TEMP_NAME, int flags = SCRIPTVARLINK_DEFAULT) : CScriptVarLinkPtr(var, name, flags) {} + CScriptVarLinkWorkPtr(CScriptVarLink *Link) : CScriptVarLinkPtr(Link) { if(link) referencedOwner = link->getOwner(); } // creates a new CScriptVarLink (from new); + CScriptVarLinkWorkPtr(const CScriptVarLinkPtr &Copy) : CScriptVarLinkPtr(Copy) { if(link) referencedOwner = link->getOwner(); } - virtual CScriptVarPtr _valueOf(bool execute); - virtual CScriptVarPtr _toString(bool execute, int radix=0); - virtual void setTemporaryID_recursive(uint32_t ID); -private: - CScriptVarPtr value; - friend define_newScriptVar_Fnc(ObjectWrap, CTinyJS *Context, ObjectWrap_t, const CScriptVarPtr); -}; -inline define_newScriptVar_Fnc(ObjectWrap, CTinyJS *Context, ObjectWrap_t, const CScriptVarPtr Value) { return new CScriptVarObjectWrap(Context, Value); } + // reconstruct + CScriptVarLinkWorkPtr &operator()(const CScriptVarPtr &var, const std::string &name = TINYJS_TEMP_NAME, int flags = SCRIPTVARLINK_DEFAULT) {CScriptVarLinkPtr::operator()(var, name, flags); referencedOwner.clear(); return *this; } -////////////////////////////////////////////////////////////////////////// -/// CScriptVarError -////////////////////////////////////////////////////////////////////////// + // copy + CScriptVarLinkWorkPtr(const CScriptVarLinkWorkPtr &Copy) : CScriptVarLinkPtr(Copy), referencedOwner(Copy.referencedOwner) {} + CScriptVarLinkWorkPtr &operator=(const CScriptVarLinkWorkPtr &Copy) { CScriptVarLinkPtr::operator=(Copy); referencedOwner = Copy.referencedOwner; return *this; } -define_ScriptVarPtr_Type(Error); + // getter & setter + CScriptVarLinkWorkPtr getter(); + CScriptVarLinkWorkPtr getter(bool &execute); + CScriptVarLinkWorkPtr setter(const CScriptVarPtr &Var); + CScriptVarLinkWorkPtr setter(bool &execute, const CScriptVarPtr &Var); -class CScriptVarError : public CScriptVarObject { -protected: - CScriptVarError(CTinyJS *Context, ERROR_TYPES type, const char *message, const char *file, int line, int column);// : CScriptVarObject(Context), value(Value) {} - CScriptVarError(const CScriptVarError &Copy) : CScriptVarObject(Copy) {} ///< Copy protected -> use clone for public -public: - virtual ~CScriptVarError(); - virtual CScriptVarPtr clone(); - virtual bool isError(); // { return true; } -// virtual std::string getParsableString(const std::string &indentString, const std::string &indent); ///< get Data as a parsable javascript string + void swap(CScriptVarLinkWorkPtr &Link) { + CScriptVarPtr _referencedOwner = referencedOwner; referencedOwner = Link.referencedOwner; Link.referencedOwner = _referencedOwner; + CScriptVarLink *_link=link; link=Link.link; Link.link=_link; + } - virtual CScriptVarPtr _toString(bool execute, int radix=0); + void clear() { CScriptVarLinkPtr::clear(); referencedOwner.clear(); } + void setReferencedOwner(const CScriptVarPtr &Owner) { referencedOwner = Owner; } + const CScriptVarPtr &getReferencedOwner() const { return referencedOwner; } + bool hasReferencedOwner() const { return referencedOwner; } private: - friend define_newScriptVar_NamedFnc(Error, CTinyJS *Context, ERROR_TYPES type, const char *message, const char *file, int line, int column); + CScriptVarPtr referencedOwner; }; -inline define_newScriptVar_NamedFnc(Error, CTinyJS *Context, ERROR_TYPES type, const char *message=0, const char *file=0, int line=-1, int column=-1) { return new CScriptVarError(Context, type, message, file, line, column); } -////////////////////////////////////////////////////////////////////////// -/// CScriptVarAccessor ////////////////////////////////////////////////////////////////////////// +#define define_dummy_t(t1) struct t1##_t{}; extern t1##_t t1 +#define declare_dummy_t(t1) t1##_t t1 +#define define_newScriptVar_Fnc(t1, ...) CScriptVarPtr newScriptVar(__VA_ARGS__) +#define define_newScriptVar_NamedFnc(t1, ...) CScriptVarPtr newScriptVar##t1(__VA_ARGS__) +#define define_ScriptVarPtr_Type(t1) class CScriptVar##t1; typedef CScriptVarPointer CScriptVar##t1##Ptr -define_dummy_t(Accessor); -define_ScriptVarPtr_Type(Accessor); - -class CScriptVarAccessor : public CScriptVar { -protected: - CScriptVarAccessor(CTinyJS *Context); - CScriptVarAccessor(const CScriptVarAccessor &Copy) : CScriptVar(Copy) {} ///< Copy protected -> use clone for public -public: - virtual ~CScriptVarAccessor(); - virtual CScriptVarPtr clone(); - virtual bool isAccessor(); // { return true; } - virtual bool isPrimitive(); // { return false; } - - virtual std::string getString(); // { return "[ Object ]"; }; - virtual std::string getParsableString(const std::string &indentString, const std::string &indent); - virtual std::string getVarType(); // { return "object"; } - - CScriptVarPtr getValue(); - - friend define_newScriptVar_Fnc(Accessor, CTinyJS *Context, Accessor_t); - -}; -inline define_newScriptVar_Fnc(Accessor, CTinyJS *Context, Accessor_t) { return new CScriptVarAccessor(Context); } +#define define_DEPRECATED_newScriptVar_Fnc(t1, ...) CScriptVarPtr DEPRECATED("newScriptVar("#__VA_ARGS__") is deprecated use constScriptVar("#__VA_ARGS__") instead") newScriptVar(__VA_ARGS__) -////////////////////////////////////////////////////////////////////////// -/// CScriptVarArray +////////////////////////////////////////////////////////////////////////// +/// CScriptVarPrimitive ////////////////////////////////////////////////////////////////////////// -define_dummy_t(Array); -define_ScriptVarPtr_Type(Array); -class CScriptVarArray : public CScriptVar { +define_ScriptVarPtr_Type(Primitive); +class CScriptVarPrimitive : public CScriptVar { protected: - CScriptVarArray(CTinyJS *Context); - CScriptVarArray(const CScriptVarArray &Copy) : CScriptVar(Copy) {} ///< Copy protected -> use clone for public + CScriptVarPrimitive(CTinyJS *Context, const CScriptVarPtr &Prototype) : CScriptVar(Context, Prototype) { setExtensible(false); } + CScriptVarPrimitive(const CScriptVarPrimitive &Copy) : CScriptVar(Copy) { } ///< Copy protected -> use clone for public public: - virtual ~CScriptVarArray(); - virtual CScriptVarPtr clone(); - virtual bool isArray(); // { return true; } - virtual bool isPrimitive(); // { return false; } - - virtual std::string getString(); - virtual std::string getVarType(); // { return "object"; } - virtual std::string getParsableString(const std::string &indentString, const std::string &indent); - friend define_newScriptVar_Fnc(Array, CTinyJS *Context, Array_t); -private: - void native_Length(const CFunctionsScopePtr &c, void *data); -}; -inline define_newScriptVar_Fnc(Array, CTinyJS *Context, Array_t) { return new CScriptVarArray(Context); } + virtual ~CScriptVarPrimitive(); + virtual bool isPrimitive(); ///< return true; -////////////////////////////////////////////////////////////////////////// -/// CScriptVarNull -////////////////////////////////////////////////////////////////////////// + virtual CScriptVarPrimitivePtr getRawPrimitive(); + virtual bool toBoolean(); /// false by default + virtual CNumber toNumber_Callback()=0; + virtual std::string toCString(int radix=0)=0; -define_dummy_t(Null); -define_ScriptVarPtr_Type(Null); -class CScriptVarNull : public CScriptVar { + virtual CScriptVarPtr toObject(); + virtual CScriptVarPtr toString_CallBack(bool &execute, int radix=0); protected: - CScriptVarNull(CTinyJS *Context); - CScriptVarNull(const CScriptVarNull &Copy) : CScriptVar(Copy) {} ///< Copy protected -> use clone for public -public: - virtual ~CScriptVarNull(); - virtual CScriptVarPtr clone(); - virtual bool isNull(); // { return true; } - virtual bool isNumeric(); // { return true; } - virtual std::string getString(); // { return "null"; }; - virtual std::string getVarType(); // { return "null"; } - virtual CScriptVarPtr getNumericVar(); ///< returns an Integer, a Double, an Infinity or a NaN - friend define_newScriptVar_Fnc(Null, CTinyJS *Context, Null_t); }; -inline define_newScriptVar_Fnc(Null, CTinyJS *Context, Null_t) { return new CScriptVarNull(Context); } ////////////////////////////////////////////////////////////////////////// @@ -998,16 +989,19 @@ inline define_newScriptVar_Fnc(Null, CTinyJS *Context, Null_t) { return new CScr define_dummy_t(Undefined); define_ScriptVarPtr_Type(Undefined); -class CScriptVarUndefined : public CScriptVar { +class CScriptVarUndefined : public CScriptVarPrimitive { protected: CScriptVarUndefined(CTinyJS *Context); - CScriptVarUndefined(const CScriptVarUndefined &Copy) : CScriptVar(Copy) {} ///< Copy protected -> use clone for public + CScriptVarUndefined(const CScriptVarUndefined &Copy) : CScriptVarPrimitive(Copy) {} ///< Copy protected -> use clone for public public: virtual ~CScriptVarUndefined(); virtual CScriptVarPtr clone(); + virtual bool isUndefined(); // { return true; } - virtual bool isNumeric(); // { return true; } - virtual std::string getString(); // { return "undefined"; }; + + virtual CNumber toNumber_Callback(); // { return NaN; } + virtual std::string toCString(int radix=0);// { return "undefined"; } + virtual std::string getVarType(); // { return "undefined"; } friend define_DEPRECATED_newScriptVar_Fnc(Undefined, CTinyJS *, Undefined_t); friend define_newScriptVar_NamedFnc(Undefined, CTinyJS *Context); @@ -1017,27 +1011,31 @@ inline define_newScriptVar_NamedFnc(Undefined, CTinyJS *Context) { return new CS ////////////////////////////////////////////////////////////////////////// -/// CScriptVarNaN +/// CScriptVarNull ////////////////////////////////////////////////////////////////////////// -define_dummy_t(NaN); -define_ScriptVarPtr_Type(NaN); -class CScriptVarNaN : public CScriptVar { +define_dummy_t(Null); +define_ScriptVarPtr_Type(Null); +class CScriptVarNull : public CScriptVarPrimitive { protected: - CScriptVarNaN(CTinyJS *Context); - CScriptVarNaN(const CScriptVarNaN &Copy) : CScriptVar(Copy) {} ///< Copy protected -> use clone for public + CScriptVarNull(CTinyJS *Context); + CScriptVarNull(const CScriptVarNull &Copy) : CScriptVarPrimitive(Copy) {} ///< Copy protected -> use clone for public public: - virtual ~CScriptVarNaN(); + virtual ~CScriptVarNull(); virtual CScriptVarPtr clone(); - virtual bool isNaN();// { return true; } - virtual bool isNumeric(); // { return true; } - virtual std::string getString(); // { return "NaN"; }; - virtual std::string getVarType(); // { return "number"; } - friend define_DEPRECATED_newScriptVar_Fnc(NaN, CTinyJS *, NaN_t); - friend define_newScriptVar_NamedFnc(NaN, CTinyJS *Context); + + virtual bool isNull(); // { return true; } + + virtual CNumber toNumber_Callback(); // { return 0; } + virtual std::string toCString(int radix=0);// { return "null"; } + + virtual std::string getVarType(); // { return "null"; } + + friend define_DEPRECATED_newScriptVar_Fnc(Null, CTinyJS *Context, Null_t); + friend define_newScriptVar_NamedFnc(Null, CTinyJS *Context); }; -inline define_DEPRECATED_newScriptVar_Fnc(NaN, CTinyJS *Context, NaN_t) { return new CScriptVarNaN(Context); } -inline define_newScriptVar_NamedFnc(NaN, CTinyJS *Context) { return new CScriptVarNaN(Context); } +inline define_DEPRECATED_newScriptVar_Fnc(Null, CTinyJS *Context, Null_t) { return new CScriptVarNull(Context); } +inline define_newScriptVar_NamedFnc(Null, CTinyJS *Context) { return new CScriptVarNull(Context); } ////////////////////////////////////////////////////////////////////////// @@ -1045,26 +1043,30 @@ inline define_newScriptVar_NamedFnc(NaN, CTinyJS *Context) { return new CScriptV ////////////////////////////////////////////////////////////////////////// define_ScriptVarPtr_Type(String); -class CScriptVarString : public CScriptVar { +class CScriptVarString : public CScriptVarPrimitive { protected: CScriptVarString(CTinyJS *Context, const std::string &Data); - CScriptVarString(const CScriptVarString &Copy) : CScriptVar(Copy), data(Copy.data) {} ///< Copy protected -> use clone for public + CScriptVarString(const CScriptVarString &Copy) : CScriptVarPrimitive(Copy), data(Copy.data) {} ///< Copy protected -> use clone for public public: virtual ~CScriptVarString(); virtual CScriptVarPtr clone(); virtual bool isString(); // { return true; } - virtual int getInt(); // {return strtol(data.c_str(),0,0); } - virtual bool getBool(); // {return data.length()!=0;} - virtual double getDouble(); // {return strtod(data.c_str(),0);} - virtual std::string getString(); // { return data; } - virtual std::string getParsableString(); // { return getJSString(data); } + + virtual bool toBoolean(); + virtual CNumber toNumber_Callback(); + virtual std::string toCString(int radix=0); + + virtual std::string getParsableString(const std::string &indentString, const std::string &indent, uint32_t uniqueID, bool &hasRecursion); // { return getJSString(data); } virtual std::string getVarType(); // { return "string"; } - virtual CScriptVarPtr getNumericVar(); ///< returns an Integer, a Double, an Infinity or a NaN + + virtual CScriptVarPtr toObject(); + virtual CScriptVarPtr toString_CallBack(bool &execute, int radix=0); + + uint32_t stringLength() { return data.size(); } + int getChar(uint32_t Idx); protected: std::string data; private: - void native_Length(const CFunctionsScopePtr &c, void *data); - friend define_newScriptVar_Fnc(String, CTinyJS *Context, const std::string &); friend define_newScriptVar_Fnc(String, CTinyJS *Context, const char *); friend define_newScriptVar_Fnc(String, CTinyJS *Context, char *); @@ -1074,50 +1076,180 @@ inline define_newScriptVar_Fnc(String, CTinyJS *Context, const char *Obj) { retu inline define_newScriptVar_Fnc(String, CTinyJS *Context, char *Obj) { return new CScriptVarString(Context, Obj); } -////////////////////////////////////////////////////////////////////////// -/// CScriptVarIntegerBase ////////////////////////////////////////////////////////////////////////// +/// CNumber +////////////////////////////////////////////////////////////////////////// +define_dummy_t(NegativeZero); +define_dummy_t(NaN); +class Infinity{public:Infinity(int Sig=1):sig(Sig){} int Sig(){return sig;} private:int sig; } ; +extern Infinity InfinityPositive; +extern Infinity InfinityNegative; -class CScriptVarIntegerBase : public CScriptVar { -protected: - CScriptVarIntegerBase(CTinyJS *Context, const CScriptVarPtr &Prototype, int Data); - CScriptVarIntegerBase(const CScriptVarIntegerBase &Copy) : CScriptVar(Copy), data(Copy.data) {} ///< Copy protected -> use clone for public +class CNumber { +private: + enum NType { + tnNULL, tInt32, tDouble, tNaN, tInfinity + }; + CNumber(NType Type, int32_t InfinitySign=0) : type(Type) { Int32 = InfinitySign; } public: - virtual ~CScriptVarIntegerBase(); - virtual bool isNumeric(); // { return true; } - virtual int getInt(); // {return data; } - virtual bool getBool(); // {return data!=0;} - virtual double getDouble(); // {return data;} - virtual std::string getString(); // {return int2string(data);} - virtual std::string getVarType(); // { return "number"; } - virtual CScriptVarPtr getNumericVar(); ///< returns an Integer, a Double, an Infinity or a NaN -protected: - int data; -}; + CNumber(const CNumber &Copy) { *this=Copy; } + + CNumber(int32_t Value=0) : type(tInt32) { Int32=Value; } +#if 1 + templateCNumber(T Value) { *this = Value; } +#else + CNumber(negativeZero_t Value) { *this = Value; } + CNumber(NaN_t Value) { *this = Value; } + CNumber(Infinity Value) { *this = Value; } + CNumber(uint32_t Value) { *this = Value; } + CNumber(double Value) { *this = Value; } + CNumber(unsigned char Value) { *this = Value; } + CNumber(const char *Value) { *this = Value; } + CNumber(const std::string &Value) { *this = Value; } +#endif + CNumber &operator=(NegativeZero_t) { type=tnNULL; Int32=0; return *this; } + CNumber &operator=(NaN_t) { type=tNaN; Int32=0; return *this; } + CNumber &operator=(Infinity v) { type=tInfinity; Int32=v.Sig(); return *this; } + CNumber &operator=(int32_t Value) { type=tInt32; Int32=Value; return *this; } + CNumber &operator=(uint32_t Value) { + if(Value<=(uint32_t)std::numeric_limits::max()) + type=tInt32, Int32=int32_t(Value); + else + type=tDouble, Double=Value; + return *this; + } + CNumber &operator=(double Value); + CNumber &operator=(unsigned char Value) { type=tInt32; Int32=Value; return *this; } + CNumber &operator=(const char *Value); + CNumber &operator=(const std::string &Value) { return operator=(Value.c_str());} + + int32_t parseInt(const char *str, int32_t radix=0, const char **endptr=0); + void parseInt(const std::string &str, int32_t radix=0) { parseInt(str.c_str(), radix); } + void parseFloat(const char *str, const char **endptr=0); + void parseFloat(const std::string &str) { parseFloat(str.c_str()); } + + CNumber add(const CNumber &Value) const; + CNumber operator-() const; + CNumber operator~() const { if(type==tNaN) return *this; else return ~toInt32(); } + bool operator!() const { return isZero(); } + CNumber multi(const CNumber &Value) const; + CNumber div(const CNumber &Value) const; + CNumber modulo(const CNumber &Value) const; + + CNumber round() const; + CNumber floor() const; + CNumber ceil() const; + CNumber abs() const; + + CNumber shift(const CNumber &Value, bool right) const; + CNumber ushift(const CNumber &Value, bool right=true) const; + + CNumber binary(const CNumber &Value, char Mode) const; + + + int less(const CNumber &Value) const; + bool equal(const CNumber &Value) const; + + + bool isInt32() const { return type == tInt32; } + bool isDouble() const { return type == tDouble; } + + bool isNaN() const { return type == tNaN; } + int isInfinity() const { return type == tInfinity ? Int32 : 0; } + bool isFinite() const { return type == tInt32 || type == tDouble || type == tnNULL; } + bool isNegativeZero() const { return type==tnNULL; } + bool isZero() const; ///< is 0, -0 + bool isInteger() const; + int sign() const; + + int32_t toInt32() const { return cast(); } + uint32_t toUInt32() const { return cast(); } + double toDouble() const; + bool toBoolean() const { return !isZero() && type!=tNaN; } + std::string toString(uint32_t Radix=10) const; +private: + template T cast() const { + switch(type) { + case tInt32: + return T(Int32); + case tDouble: + return T(Double); + default: + return T(0); + } + } + NType type; + union { + int32_t Int32; + double Double; + }; +}; +inline CNumber operator+(const CNumber &lhs, const CNumber &rhs) { return lhs.add(rhs); } +inline CNumber &operator+=(CNumber &lhs, const CNumber &rhs) { return lhs=lhs.add(rhs); } +inline CNumber operator-(const CNumber &lhs, const CNumber &rhs) { return lhs.add(-rhs); } +inline CNumber &operator-=(CNumber &lhs, const CNumber &rhs) { return lhs=lhs.add(-rhs); } +inline CNumber operator*(const CNumber &lhs, const CNumber &rhs) { return lhs.multi(rhs); } +inline CNumber &operator*=(CNumber &lhs, const CNumber &rhs) { return lhs=lhs.multi(rhs); } +inline CNumber operator/(const CNumber &lhs, const CNumber &rhs) { return lhs.div(rhs); } +inline CNumber &operator/=(CNumber &lhs, const CNumber &rhs) { return lhs=lhs.div(rhs); } +inline CNumber operator%(const CNumber &lhs, const CNumber &rhs) { return lhs.modulo(rhs); } +inline CNumber &operator%=(CNumber &lhs, const CNumber &rhs) { return lhs=lhs.modulo(rhs); } +inline CNumber operator>>(const CNumber &lhs, const CNumber &rhs) { return lhs.shift(rhs, true); } +inline CNumber &operator>>=(CNumber &lhs, const CNumber &rhs) { return lhs=lhs.shift(rhs, true); } +inline CNumber operator<<(const CNumber &lhs, const CNumber &rhs) { return lhs.shift(rhs, false); } +inline CNumber &operator<<=(CNumber &lhs, const CNumber &rhs) { return lhs=lhs.shift(rhs, false); } + +inline bool operator==(const CNumber &lhs, const CNumber &rhs) { return lhs.equal(rhs); } +inline bool operator!=(const CNumber &lhs, const CNumber &rhs) { return !lhs.equal(rhs); } +inline bool operator<(const CNumber &lhs, const CNumber &rhs) { return lhs.less(rhs)>0; } +inline bool operator<=(const CNumber &lhs, const CNumber &rhs) { return rhs.less(lhs)<0; } +inline bool operator>(const CNumber &lhs, const CNumber &rhs) { return rhs.less(lhs)>0; } +inline bool operator>=(const CNumber &lhs, const CNumber &rhs) { return lhs.less(rhs)<0; } + +inline CNumber round(const CNumber &lhs) { return lhs.round(); } +inline CNumber floor(const CNumber &lhs) { return lhs.floor(); } +inline CNumber ceil(const CNumber &lhs) { return lhs.ceil(); } +inline CNumber abs(const CNumber &lhs) { return lhs.abs(); } ////////////////////////////////////////////////////////////////////////// -/// CScriptVarInteger +/// CScriptVarNumber ////////////////////////////////////////////////////////////////////////// -define_dummy_t(Zero); -define_dummy_t(One); -define_ScriptVarPtr_Type(Integer); -class CScriptVarInteger : public CScriptVarIntegerBase { +define_ScriptVarPtr_Type(Number); +class CScriptVarNumber : public CScriptVarPrimitive { protected: - CScriptVarInteger(CTinyJS *Context, int Data); - CScriptVarInteger(const CScriptVarInteger &Copy) : CScriptVarIntegerBase(Copy) {} ///< Copy protected -> use clone for public + CScriptVarNumber(CTinyJS *Context, const CNumber &Data); + CScriptVarNumber(const CScriptVarNumber &Copy) : CScriptVarPrimitive(Copy), data(Copy.data) {} ///< Copy protected -> use clone for public public: - virtual ~CScriptVarInteger(); + virtual ~CScriptVarNumber(); virtual CScriptVarPtr clone(); virtual bool isNumber(); // { return true; } virtual bool isInt(); // { return true; } - virtual CScriptVarPtr _toString(bool execute, int radix=0); - friend define_newScriptVar_Fnc(Integer, CTinyJS *Context, int); - friend define_newScriptVar_Fnc(Integer, CTinyJS *Context, char); + virtual bool isDouble(); // { return true; } + virtual bool isRealNumber(); // { return true; } + virtual int isInfinity(); // { return data; } + virtual bool isNaN();// { return true; } + + virtual bool toBoolean(); + virtual CNumber toNumber_Callback(); + virtual std::string toCString(int radix=0); + + virtual std::string getVarType(); // { return "number"; } + + virtual CScriptVarPtr toObject(); +private: + CNumber data; + friend define_newScriptVar_Fnc(Number, CTinyJS *Context, const CNumber &); + friend define_newScriptVar_NamedFnc(Number, CTinyJS *Context, const CNumber &); }; -inline define_newScriptVar_Fnc(Integer, CTinyJS *Context, int Obj) { return new CScriptVarInteger(Context, Obj); } -inline define_newScriptVar_Fnc(Integer, CTinyJS *Context, char Obj) { return new CScriptVarInteger(Context, Obj); } +define_newScriptVar_Fnc(Number, CTinyJS *Context, const CNumber &Obj); +inline define_newScriptVar_NamedFnc(Number, CTinyJS *Context, const CNumber &Obj) { return new CScriptVarNumber(Context, Obj); } +inline define_newScriptVar_Fnc(Number, CTinyJS *Context, unsigned int Obj) { return newScriptVarNumber(Context, CNumber(Obj)); } +inline define_newScriptVar_Fnc(Number, CTinyJS *Context, int Obj) { return newScriptVarNumber(Context, CNumber(Obj)); } +inline define_newScriptVar_Fnc(Number, CTinyJS *Context, double Obj) { return newScriptVarNumber(Context, CNumber(Obj)); } +inline define_DEPRECATED_newScriptVar_Fnc(NaN, CTinyJS *Context, NaN_t) { return newScriptVarNumber(Context, CNumber(NaN)); } +inline define_DEPRECATED_newScriptVar_Fnc(Infinity, CTinyJS *Context, Infinity Obj) { return newScriptVarNumber(Context, CNumber(Obj)); } ////////////////////////////////////////////////////////////////////////// @@ -1125,17 +1257,25 @@ inline define_newScriptVar_Fnc(Integer, CTinyJS *Context, char Obj) { return new ////////////////////////////////////////////////////////////////////////// define_ScriptVarPtr_Type(Bool); -class CScriptVarBool : public CScriptVarIntegerBase { +class CScriptVarBool : public CScriptVarPrimitive { protected: CScriptVarBool(CTinyJS *Context, bool Data); - CScriptVarBool(const CScriptVarBool &Copy) : CScriptVarIntegerBase(Copy) {} ///< Copy protected -> use clone for public + CScriptVarBool(const CScriptVarBool &Copy) : CScriptVarPrimitive(Copy), data(Copy.data) {} ///< Copy protected -> use clone for public public: virtual ~CScriptVarBool(); virtual CScriptVarPtr clone(); virtual bool isBool(); // { return true; } - virtual std::string getString(); // {return data!=0?"true":"false";} + + virtual bool toBoolean(); + virtual CNumber toNumber_Callback(); + virtual std::string toCString(int radix=0); + virtual std::string getVarType(); // { return "boolean"; } - virtual CScriptVarPtr getNumericVar(); ///< returns an Integer, a Double, an Infinity or a NaN + + virtual CScriptVarPtr toObject(); +protected: + bool data; + friend define_DEPRECATED_newScriptVar_Fnc(Bool, CTinyJS *, bool); friend define_newScriptVar_NamedFnc(Bool, CTinyJS *Context, bool); }; @@ -1144,56 +1284,138 @@ inline define_newScriptVar_NamedFnc(Bool, CTinyJS *Context, bool Obj) { return n ////////////////////////////////////////////////////////////////////////// -/// CScriptVarInfinity +/// CScriptVarObject ////////////////////////////////////////////////////////////////////////// -class Infinity{public:Infinity(int Sig=1):sig(Sig){} int Sig(){return sig;} private:int sig; } ; -extern Infinity InfinityPositive; -extern Infinity InfinityNegative; -define_ScriptVarPtr_Type(Infinity); -class CScriptVarInfinity : public CScriptVarIntegerBase { +define_dummy_t(Object); +define_ScriptVarPtr_Type(Object); + +class CScriptVarObject : public CScriptVar { protected: - CScriptVarInfinity(CTinyJS *Context, int Data); - CScriptVarInfinity(const CScriptVarInfinity &Copy) : CScriptVarIntegerBase(Copy) {} ///< Copy protected -> use clone for public + CScriptVarObject(CTinyJS *Context); + CScriptVarObject(CTinyJS *Context, const CScriptVarPtr &Prototype) : CScriptVar(Context, Prototype) {} + CScriptVarObject(CTinyJS *Context, const CScriptVarPrimitivePtr &Value, const CScriptVarPtr &Prototype) : CScriptVar(Context, Prototype), value(Value) {} + CScriptVarObject(const CScriptVarObject &Copy) : CScriptVar(Copy) {} ///< Copy protected -> use clone for public public: - virtual ~CScriptVarInfinity(); + virtual ~CScriptVarObject(); virtual CScriptVarPtr clone(); - virtual int isInfinity(); // { return data; } - virtual std::string getString(); // {return data<0?"-Infinity":"Infinity";} - friend define_DEPRECATED_newScriptVar_Fnc(Infinity, CTinyJS *, Infinity); - friend define_newScriptVar_NamedFnc(Infinity, CTinyJS *, Infinity); + + virtual CScriptVarPrimitivePtr getRawPrimitive(); + virtual bool isObject(); // { return true; } + + virtual std::string getParsableString(const std::string &indentString, const std::string &indent, uint32_t uniqueID, bool &hasRecursion); + virtual std::string getVarType(); ///< always "object" + virtual CScriptVarPtr toObject(); + + virtual CScriptVarPtr valueOf_CallBack(); + virtual CScriptVarPtr toString_CallBack(bool &execute, int radix=0); + virtual void setTemporaryID_recursive(uint32_t ID); +protected: +private: + CScriptVarPrimitivePtr value; + friend define_newScriptVar_Fnc(Object, CTinyJS *Context, Object_t); + friend define_newScriptVar_Fnc(Object, CTinyJS *Context, Object_t, const CScriptVarPtr &); + friend define_newScriptVar_Fnc(Object, CTinyJS *Context, const CScriptVarPtr &); + friend define_newScriptVar_Fnc(Object, CTinyJS *Context, const CScriptVarPrimitivePtr &, const CScriptVarPtr &); }; -inline define_DEPRECATED_newScriptVar_Fnc(Infinity, CTinyJS *Context, Infinity Obj) { return new CScriptVarInfinity(Context, Obj.Sig()); } -inline define_newScriptVar_NamedFnc(Infinity, CTinyJS *Context, Infinity Obj) { return new CScriptVarInfinity(Context, Obj.Sig()); } +inline define_newScriptVar_Fnc(Object, CTinyJS *Context, Object_t) { return new CScriptVarObject(Context); } +inline define_newScriptVar_Fnc(Object, CTinyJS *Context, Object_t, const CScriptVarPtr &Prototype) { return new CScriptVarObject(Context, Prototype); } +inline define_newScriptVar_Fnc(Object, CTinyJS *Context, const CScriptVarPtr &Prototype) { return new CScriptVarObject(Context, Prototype); } +inline define_newScriptVar_Fnc(Object, CTinyJS *Context, const CScriptVarPrimitivePtr &Value, const CScriptVarPtr &Prototype) { return new CScriptVarObject(Context, Value, Prototype); } + + +////////////////////////////////////////////////////////////////////////// +/// CScriptVarError +////////////////////////////////////////////////////////////////////////// + +define_ScriptVarPtr_Type(Error); + +class CScriptVarError : public CScriptVarObject { +protected: + CScriptVarError(CTinyJS *Context, ERROR_TYPES type, const char *message, const char *file, int line, int column);// : CScriptVarObject(Context), value(Value) {} + CScriptVarError(const CScriptVarError &Copy) : CScriptVarObject(Copy) {} ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarError(); + virtual CScriptVarPtr clone(); + virtual bool isError(); // { return true; } +// virtual std::string getParsableString(const std::string &indentString, const std::string &indent); ///< get Data as a parsable javascript string + + virtual CScriptVarPtr toString_CallBack(bool &execute, int radix=0); +private: + friend define_newScriptVar_NamedFnc(Error, CTinyJS *Context, ERROR_TYPES type, const char *message, const char *file, int line, int column); +}; +inline define_newScriptVar_NamedFnc(Error, CTinyJS *Context, ERROR_TYPES type, const char *message=0, const char *file=0, int line=-1, int column=-1) { return new CScriptVarError(Context, type, message, file, line, column); } ////////////////////////////////////////////////////////////////////////// -/// CScriptVarDouble +/// CScriptVarArray ////////////////////////////////////////////////////////////////////////// -define_ScriptVarPtr_Type(Double); -class CScriptVarDouble : public CScriptVar { +define_dummy_t(Array); +define_ScriptVarPtr_Type(Array); +class CScriptVarArray : public CScriptVarObject { protected: - CScriptVarDouble(CTinyJS *Context, double Data); - CScriptVarDouble(const CScriptVarDouble &Copy) : CScriptVar(Copy), data(Copy.data) {} ///< Copy protected -> use clone for public + CScriptVarArray(CTinyJS *Context); + CScriptVarArray(const CScriptVarArray &Copy) : CScriptVarObject(Copy) {} ///< Copy protected -> use clone for public public: - virtual ~CScriptVarDouble(); + virtual ~CScriptVarArray(); virtual CScriptVarPtr clone(); - virtual bool isDouble(); // { return true; } - virtual bool isNumber(); // { return true; } - virtual bool isNumeric(); // { return true; } - virtual int getInt(); // {return (int)data; } - virtual bool getBool(); // {return data!=0.0;} - virtual double getDouble(); // {return data;} - virtual std::string getString(); // {return float2string(data);} - virtual std::string getVarType(); // { return "number"; } - virtual CScriptVarPtr getNumericVar(); ///< returns an Integer, a Double, an Infinity or a NaN - virtual CScriptVarPtr _toString(bool execute, int radix=0); + virtual bool isArray(); // { return true; } + + virtual std::string getParsableString(const std::string &indentString, const std::string &indent, uint32_t uniqueID, bool &hasRecursion); + + virtual CScriptVarPtr toString_CallBack(bool &execute, int radix=0); + + friend define_newScriptVar_Fnc(Array, CTinyJS *Context, Array_t); private: - double data; - friend define_newScriptVar_Fnc(Double, CTinyJS *Context, double); + void native_Length(const CFunctionsScopePtr &c, void *data); }; -inline define_newScriptVar_Fnc(Double, CTinyJS *Context, double Obj) { return new CScriptVarDouble(Context, Obj); } +inline define_newScriptVar_Fnc(Array, CTinyJS *Context, Array_t) { return new CScriptVarArray(Context); } + + +////////////////////////////////////////////////////////////////////////// +/// CScriptVarRegExp +////////////////////////////////////////////////////////////////////////// +#ifndef NO_REGEXP + +define_ScriptVarPtr_Type(RegExp); +class CScriptVarRegExp : public CScriptVarObject { +protected: + CScriptVarRegExp(CTinyJS *Context, const std::string &Source, const std::string &Flags); + CScriptVarRegExp(const CScriptVarRegExp &Copy) : CScriptVarObject(Copy), regexp(Copy.regexp), flags(Copy.flags) {} ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarRegExp(); + virtual CScriptVarPtr clone(); + virtual bool isRegExp(); // { return true; } + virtual CScriptVarPtr toString_CallBack(bool &execute, int radix=0); + + CScriptVarPtr exec(const std::string &Input, bool Test=false); + + bool Global() { return flags.find('g')!=std::string::npos; } + bool IgnoreCase() { return flags.find('i')!=std::string::npos; } + bool Multiline() { return true; /* currently always true -- flags.find('m')!=std::string::npos;*/ } + bool Sticky() { return flags.find('y')!=std::string::npos; } + const std::string &Regexp() { return regexp; } + unsigned int LastIndex(); + void LastIndex(unsigned int Idx); + + static const char *ErrorStr(int Error); +protected: + std::string regexp; + std::string flags; +private: + void native_Global(const CFunctionsScopePtr &c, void *data); + void native_IgnoreCase(const CFunctionsScopePtr &c, void *data); + void native_Multiline(const CFunctionsScopePtr &c, void *data); + void native_Sticky(const CFunctionsScopePtr &c, void *data); + void native_Source(const CFunctionsScopePtr &c, void *data); + + friend define_newScriptVar_Fnc(RegExp, CTinyJS *Context, const std::string &, const std::string &); + +}; +inline define_newScriptVar_Fnc(RegExp, CTinyJS *Context, const std::string &Obj, const std::string &Flags) { return new CScriptVarRegExp(Context, Obj, Flags); } + +#endif /* NO_REGEXP */ ////////////////////////////////////////////////////////////////////////// @@ -1212,22 +1434,45 @@ class CScriptVarFunction : public CScriptVarObject { virtual bool isFunction(); // { return true; } virtual bool isPrimitive(); // { return false; } -// virtual std::string getString(); // {return "[ Function ]";} virtual std::string getVarType(); // { return "function"; } - virtual std::string getParsableString(const std::string &indentString, const std::string &indent); - virtual CScriptVarPtr _toString(bool execute, int radix=0); + virtual std::string getParsableString(const std::string &indentString, const std::string &indent, uint32_t uniqueID, bool &hasRecursion); + virtual CScriptVarPtr toString_CallBack(bool &execute, int radix=0); virtual CScriptTokenDataFnc *getFunctionData(); void setFunctionData(CScriptTokenDataFnc *Data); private: CScriptTokenDataFnc *data; - std::string getParsableBlockString(TOKEN_VECT_it &it, TOKEN_VECT_it end, const std::string indentString, const std::string indent); - friend define_newScriptVar_Fnc(Function, CTinyJS *Context, CScriptTokenDataFnc *); }; inline define_newScriptVar_Fnc(Function, CTinyJS *Context, CScriptTokenDataFnc *Obj) { return new CScriptVarFunction(Context, Obj); } +////////////////////////////////////////////////////////////////////////// +/// CScriptVarFunctionBounded +////////////////////////////////////////////////////////////////////////// + +define_ScriptVarPtr_Type(FunctionBounded); +class CScriptVarFunctionBounded : public CScriptVarFunction { +protected: + CScriptVarFunctionBounded(CScriptVarFunctionPtr BoundedFunction, CScriptVarPtr BoundedThis, const std::vector &BoundedArguments); + CScriptVarFunctionBounded(const CScriptVarFunctionBounded &Copy) : CScriptVarFunction(Copy), boundedThis(Copy.boundedThis), boundedArguments(Copy.boundedArguments) { } ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarFunctionBounded(); + virtual CScriptVarPtr clone(); + virtual bool isBounded(); ///< is CScriptVarFunctionBounded + virtual void setTemporaryID_recursive(uint32_t ID); + CScriptVarPtr callFunction(bool &execute, std::vector &Arguments, const CScriptVarPtr &This, CScriptVarPtr *newThis=0); +protected: +private: + CScriptVarFunctionPtr boundedFunction; + CScriptVarPtr boundedThis; + std::vector boundedArguments; + + friend define_newScriptVar_NamedFnc(FunctionBounded, CScriptVarFunctionPtr BoundedFunction, CScriptVarPtr BoundedThis, const std::vector &BoundedArguments); +}; +inline define_newScriptVar_NamedFnc(FunctionBounded, CScriptVarFunctionPtr BoundedFunction, CScriptVarPtr BoundedThis, const std::vector &BoundedArguments) { return new CScriptVarFunctionBounded(BoundedFunction, BoundedThis, BoundedArguments); } + + ////////////////////////////////////////////////////////////////////////// /// CScriptVarFunctionNative ////////////////////////////////////////////////////////////////////////// @@ -1235,15 +1480,13 @@ inline define_newScriptVar_Fnc(Function, CTinyJS *Context, CScriptTokenDataFnc * define_ScriptVarPtr_Type(FunctionNative); class CScriptVarFunctionNative : public CScriptVarFunction { protected: - CScriptVarFunctionNative(CTinyJS *Context, void *Userdata) : CScriptVarFunction(Context, 0), jsUserData(Userdata) { } + CScriptVarFunctionNative(CTinyJS *Context, void *Userdata) : CScriptVarFunction(Context, new CScriptTokenDataFnc), jsUserData(Userdata) { } CScriptVarFunctionNative(const CScriptVarFunctionNative &Copy) : CScriptVarFunction(Copy), jsUserData(Copy.jsUserData) { } ///< Copy protected -> use clone for public public: virtual ~CScriptVarFunctionNative(); virtual CScriptVarPtr clone()=0; virtual bool isNative(); // { return true; } - virtual std::string getString(); // {return "[ Function Native ]";} - virtual void callFunction(const CFunctionsScopePtr &c)=0;// { jsCallback(c, jsCallbackUserData); } protected: void *jsUserData; ///< user data passed as second argument to native functions @@ -1292,6 +1535,49 @@ class CScriptVarFunctionNativeClass : public CScriptVarFunctionNative { template define_newScriptVar_Fnc(FunctionNativeCallback, CTinyJS *Context, native *ClassPtr, void (native::*ClassFnc)(const CFunctionsScopePtr &, void *), void *Userdata) { return new CScriptVarFunctionNativeClass(Context, ClassPtr, ClassFnc, Userdata); } + +////////////////////////////////////////////////////////////////////////// +/// CScriptVarAccessor +////////////////////////////////////////////////////////////////////////// + +define_dummy_t(Accessor); +define_ScriptVarPtr_Type(Accessor); + +class CScriptVarAccessor : public CScriptVarObject { +protected: + CScriptVarAccessor(CTinyJS *Context); + CScriptVarAccessor(CTinyJS *Context, JSCallback getter, void *getterdata, JSCallback setter, void *setterdata); + template CScriptVarAccessor(CTinyJS *Context, C *class_ptr, void(C::*getterFnc)(const CFunctionsScopePtr &, void *), void *getterData, void(C::*setterFnc)(const CFunctionsScopePtr &, void *), void *setterData) : CScriptVarObject(Context) { + if(getterFnc) + addChild(TINYJS_ACCESSOR_GET_VAR, ::newScriptVar(Context, class_ptr, getterFnc, getterData), 0); + if(setterFnc) + addChild(TINYJS_ACCESSOR_SET_VAR, ::newScriptVar(Context, class_ptr, setterFnc, setterData), 0); + } + CScriptVarAccessor(CTinyJS *Context, const CScriptVarFunctionPtr &getter, const CScriptVarFunctionPtr &setter); + + CScriptVarAccessor(const CScriptVarAccessor &Copy) : CScriptVarObject(Copy) {} ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarAccessor(); + virtual CScriptVarPtr clone(); + virtual bool isAccessor(); // { return true; } + virtual bool isPrimitive(); // { return false; } + + virtual std::string getParsableString(const std::string &indentString, const std::string &indent, uint32_t uniqueID, bool &hasRecursion); + virtual std::string getVarType(); // { return "object"; } + + CScriptVarPtr getValue(); + + friend define_newScriptVar_Fnc(Accessor, CTinyJS *Context, Accessor_t); + friend define_newScriptVar_NamedFnc(Accessor, CTinyJS *Context, JSCallback getter, void *getterdata, JSCallback setter, void *setterdata); + template friend define_newScriptVar_NamedFnc(Accessor, CTinyJS *Context, C *class_ptr, void(C::*getterFnc)(const CFunctionsScopePtr &, void *), void *getterData, void(C::*setterFnc)(const CFunctionsScopePtr &, void *), void *setterData); + friend define_newScriptVar_NamedFnc(Accessor, CTinyJS *Context, const CScriptVarFunctionPtr &, const CScriptVarFunctionPtr &); +}; +inline define_newScriptVar_Fnc(Accessor, CTinyJS *Context, Accessor_t) { return new CScriptVarAccessor(Context); } +inline define_newScriptVar_NamedFnc(Accessor, CTinyJS *Context, JSCallback getter, void *getterdata, JSCallback setter, void *setterdata) { return new CScriptVarAccessor(Context, getter, getterdata, setter, setterdata); } +template define_newScriptVar_NamedFnc(Accessor, CTinyJS *Context, C *class_ptr, void(C::*getterFnc)(const CFunctionsScopePtr &, void *), void *getterData, void(C::*setterFnc)(const CFunctionsScopePtr &, void *), void *setterData) { return new CScriptVarAccessor(Context, class_ptr, getterFnc, getterData, setterFnc, setterData); } +inline define_newScriptVar_NamedFnc(Accessor, CTinyJS *Context, const CScriptVarFunctionPtr &getter, const CScriptVarFunctionPtr &setter) { return new CScriptVarAccessor(Context, getter, setter); } + + ////////////////////////////////////////////////////////////////////////// /// CScriptVarScope ////////////////////////////////////////////////////////////////////////// @@ -1308,7 +1594,7 @@ class CScriptVarScope : public CScriptVarObject { virtual ~CScriptVarScope(); virtual CScriptVarPtr scopeVar(); ///< to create var like: var a = ... virtual CScriptVarPtr scopeLet(); ///< to create var like: let a = ... - virtual CScriptVarLink *findInScopes(const std::string &childName); + virtual CScriptVarLinkWorkPtr findInScopes(const std::string &childName); virtual CScriptVarScopePtr getParent(); friend define_newScriptVar_Fnc(Scope, CTinyJS *Context, Scope_t); }; @@ -1324,10 +1610,10 @@ define_ScriptVarPtr_Type(ScopeFnc); class CScriptVarScopeFnc : public CScriptVarScope { protected: // only derived classes or friends can be created CScriptVarScopeFnc(CTinyJS *Context, const CScriptVarScopePtr &Closure) // constructor for FncScope - : CScriptVarScope(Context), closure(Closure ? addChild(TINYJS_FUNCTION_CLOSURE_VAR, Closure, 0) : 0) {} + : CScriptVarScope(Context), closure(Closure ? addChild(TINYJS_FUNCTION_CLOSURE_VAR, Closure, 0) : CScriptVarLinkPtr()) {} public: virtual ~CScriptVarScopeFnc(); - virtual CScriptVarLink *findInScopes(const std::string &childName); + virtual CScriptVarLinkWorkPtr findInScopes(const std::string &childName); void setReturnVar(const CScriptVarPtr &var); ///< Set the result value. Use this when setting complex return data as it avoids a deepCopy() @@ -1338,8 +1624,11 @@ class CScriptVarScopeFnc : public CScriptVarScope { CScriptVarPtr getArgument(int Idx); ///< If this is a function, get the parameter with the given index (for use by native functions) DEPRECATED("getParameterLength is deprecated use getArgumentsLength instead") int getParameterLength(); ///< If this is a function, get the count of parameters int getArgumentsLength(); ///< If this is a function, get the count of parameters + + void throwError(ERROR_TYPES ErrorType, const std::string &message); + protected: - CScriptVarLink *closure; + CScriptVarLinkPtr closure; friend define_newScriptVar_Fnc(ScopeFnc, CTinyJS *Context, ScopeFnc_t, const CScriptVarScopePtr &Closure); }; inline define_newScriptVar_Fnc(ScopeFnc, CTinyJS *Context, ScopeFnc_t, const CScriptVarScopePtr &Closure) { return new CScriptVarScopeFnc(Context, Closure); } @@ -1357,11 +1646,13 @@ class CScriptVarScopeLet : public CScriptVarScope { // : CScriptVarScope(Parent->getContext()), parent( context->getRoot() != Parent ? addChild(TINYJS_SCOPE_PARENT_VAR, Parent, 0) : 0) {} public: virtual ~CScriptVarScopeLet(); - virtual CScriptVarLink *findInScopes(const std::string &childName); + virtual CScriptVarLinkWorkPtr findInScopes(const std::string &childName); virtual CScriptVarPtr scopeVar(); ///< to create var like: var a = ... virtual CScriptVarScopePtr getParent(); + void setletExpressionInitMode(bool Mode) { letExpressionInitMode = Mode; } protected: - CScriptVarLink *parent; + CScriptVarLinkPtr parent; + bool letExpressionInitMode; friend define_newScriptVar_Fnc(ScopeLet, CTinyJS *Context, ScopeLet_t, const CScriptVarScopePtr &Parent); }; inline define_newScriptVar_Fnc(ScopeLet, CTinyJS *, ScopeLet_t, const CScriptVarScopePtr &Parent) { return new CScriptVarScopeLet(Parent); } @@ -1381,16 +1672,20 @@ class CScriptVarScopeWith : public CScriptVarScopeLet { public: virtual ~CScriptVarScopeWith(); virtual CScriptVarPtr scopeLet(); ///< to create var like: let a = ... - virtual CScriptVarLink *findInScopes(const std::string &childName); + virtual CScriptVarLinkWorkPtr findInScopes(const std::string &childName); private: - CScriptVarLink *with; + CScriptVarLinkPtr with; friend define_newScriptVar_Fnc(ScopeWith, CTinyJS *Context, ScopeWith_t, const CScriptVarScopePtr &Parent, const CScriptVarPtr &With); }; inline define_newScriptVar_Fnc(ScopeWith, CTinyJS *, ScopeWith_t, const CScriptVarScopePtr &Parent, const CScriptVarPtr &With) { return new CScriptVarScopeWith(Parent, With); } + ////////////////////////////////////////////////////////////////////////// template inline CScriptVarPtr CScriptVar::newScriptVar(T t) { return ::newScriptVar(context, t); } +template +inline CScriptVarPtr CScriptVar::newScriptVar(T1 t1, T2 t2) { return ::newScriptVar(context, t1, t2); } +//inline CScriptVarPtr newScriptVar(const CNumber &t) { return ::newScriptVar(context, t); } ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// @@ -1410,19 +1705,19 @@ class CTinyJS { * 'undefined' variable type. CScriptVarLink is returned as this will * automatically unref the result as it goes out of scope. If you want to * keep it, you must use ref() and unref() */ - CScriptVarLink evaluateComplex(CScriptTokenizer &Tokenizer); + CScriptVarLinkPtr evaluateComplex(CScriptTokenizer &Tokenizer); /** Evaluate the given code and return a link to a javascript object, * useful for (dangerous) JSON parsing. If nothing to return, will return * 'undefined' variable type. CScriptVarLink is returned as this will * automatically unref the result as it goes out of scope. If you want to * keep it, you must use ref() and unref() */ - CScriptVarLink evaluateComplex(const char *code, const std::string &File="", int Line=0, int Column=0); + CScriptVarLinkPtr evaluateComplex(const char *code, const std::string &File="", int Line=0, int Column=0); /** Evaluate the given code and return a link to a javascript object, * useful for (dangerous) JSON parsing. If nothing to return, will return * 'undefined' variable type. CScriptVarLink is returned as this will * automatically unref the result as it goes out of scope. If you want to * keep it, you must use ref() and unref() */ - CScriptVarLink evaluateComplex(const std::string &code, const std::string &File="", int Line=0, int Column=0); + CScriptVarLinkPtr evaluateComplex(const std::string &code, const std::string &File="", int Line=0, int Column=0); /** Evaluate the given code and return a string. If nothing to return, will return * 'undefined' */ std::string evaluate(CScriptTokenizer &Tokenizer); @@ -1459,9 +1754,9 @@ class CTinyJS { \endcode */ - CScriptVarFunctionNativePtr addNative(const std::string &funcDesc, JSCallback ptr, void *userdata=0, int LinkFlags=SCRIPTVARLINK_NATIVEDEFAULT); + CScriptVarFunctionNativePtr addNative(const std::string &funcDesc, JSCallback ptr, void *userdata=0, int LinkFlags=SCRIPTVARLINK_BUILDINDEFAULT); template - CScriptVarFunctionNativePtr addNative(const std::string &funcDesc, C *class_ptr, void(C::*class_fnc)(const CFunctionsScopePtr &, void *), void *userdata=0, int LinkFlags=SCRIPTVARLINK_NATIVEDEFAULT) + CScriptVarFunctionNativePtr addNative(const std::string &funcDesc, C *class_ptr, void(C::*class_fnc)(const CFunctionsScopePtr &, void *), void *userdata=0, int LinkFlags=SCRIPTVARLINK_BUILDINDEFAULT) { return addNative(funcDesc, ::newScriptVar(this, class_ptr, class_fnc, userdata), LinkFlags); } @@ -1473,14 +1768,15 @@ class CTinyJS { // CScriptVar *root; /// root of symbol table /// newVars & constVars + //CScriptVarPtr newScriptVar(const CNumber &t) { return ::newScriptVar(this, t); } template CScriptVarPtr newScriptVar(T t) { return ::newScriptVar(this, t); } template CScriptVarPtr newScriptVar(T1 t1, T2 t2) { return ::newScriptVar(this, t1, t2); } - const CScriptVarPtr &constScriptVar(Undefined_t) { return constUndefined; } - const CScriptVarPtr &constScriptVar(NaN_t) { return constNaN; } - const CScriptVarPtr &constScriptVar(Infinity t) { return t.Sig()<0 ? constInfinityNegative : constInfinityPositive; } - const CScriptVarPtr &constScriptVar(bool Val) { return Val?constTrue:constFalse; } - const CScriptVarPtr &constScriptVar(Zero_t) { return constZero; } - const CScriptVarPtr &constScriptVar(One_t) { return constOne; } + const CScriptVarPtr &constScriptVar(Undefined_t) { return constUndefined; } + const CScriptVarPtr &constScriptVar(Null_t) { return constNull; } + const CScriptVarPtr &constScriptVar(NaN_t) { return constNaN; } + const CScriptVarPtr &constScriptVar(Infinity t) { return t.Sig()<0 ? constInfinityNegative : constInfinityPositive; } + const CScriptVarPtr &constScriptVar(bool Val) { return Val?constTrue:constFalse; } + const CScriptVarPtr &constScriptVar(NegativeZero_t) { return constNegativZero; } private: @@ -1494,13 +1790,13 @@ class CTinyJS { class CScopeControl { // helper-class to manage scopes private: - CScopeControl(const CScopeControl& Copy); // no copy - CScopeControl& operator =(const CScopeControl& Copy); + CScopeControl(const CScopeControl& Copy) MEMBER_DELETE; // no copy + CScopeControl& operator =(const CScopeControl& Copy) MEMBER_DELETE; public: CScopeControl(CTinyJS *Context) : context(Context), count(0) {} ~CScopeControl() { while(count--) {CScriptVarScopePtr parent = context->scopes.back()->getParent(); if(parent) context->scopes.back() = parent; else context->scopes.pop_back() ;} } void addFncScope(const CScriptVarScopePtr &Scope) { context->scopes.push_back(Scope); count++; } - void addLetScope() { context->scopes.back() = ::newScriptVar(context, ScopeLet, context->scopes.back()); count++; } + CScriptVarScopeLetPtr addLetScope() { count++; return context->scopes.back() = ::newScriptVar(context, ScopeLet, context->scopes.back()); } void addWithScope(const CScriptVarPtr &With) { context->scopes.back() = ::newScriptVar(context, ScopeWith, context->scopes.back(), With); count++; } private: CTinyJS *context; @@ -1513,6 +1809,7 @@ class CTinyJS { CScriptVarPtr objectPrototype_toString; /// Built in object class CScriptVarPtr arrayPrototype; /// Built in array class CScriptVarPtr stringPrototype; /// Built in string class + CScriptVarPtr regexpPrototype; /// Built in string class CScriptVarPtr numberPrototype; /// Built in number class CScriptVarPtr booleanPrototype; /// Built in boolean class CScriptVarPtr functionPrototype; /// Built in function class @@ -1520,57 +1817,66 @@ class CTinyJS { private: CScriptVarPtr errorPrototypes[ERROR_COUNT]; /// Built in error class CScriptVarPtr constUndefined; + CScriptVarPtr constNull; CScriptVarPtr constNaN; CScriptVarPtr constInfinityPositive; CScriptVarPtr constInfinityNegative; + CScriptVarPtr constNegativZero; CScriptVarPtr constTrue; CScriptVarPtr constFalse; - CScriptVarPtr constOne; - CScriptVarPtr constZero; std::vector pseudo_refered; CScriptVarPtr exceptionVar; /// containing the exception var by (runtimeFlags&RUNTIME_THROW) == true; - void CheckRightHandVar(bool &execute, CScriptVarLinkPtr &link) + void CheckRightHandVar(bool &execute, CScriptVarLinkWorkPtr &link) { - if(execute && link && !link->isOwned() && !link->isOwner() && !link->getName().empty()) + if(execute && link && !link->isOwned() && !link.hasReferencedOwner() && !link->getName().empty()) throwError(execute, ReferenceError, link->getName() + " is not defined", t->getPrevPos()); } - void CheckRightHandVar(bool &execute, CScriptVarLinkPtr &link, CScriptTokenizer::ScriptTokenPosition &Pos) + void CheckRightHandVar(bool &execute, CScriptVarLinkWorkPtr &link, CScriptTokenizer::ScriptTokenPosition &Pos) { - if(execute && link && !link->isOwned() && !link->isOwner() && !link->getName().empty()) + if(execute && link && !link->isOwned() && !link.hasReferencedOwner() && !link->getName().empty()) throwError(execute, ReferenceError, link->getName() + " is not defined", Pos); } public: // function call + CScriptVarPtr callFunction(const CScriptVarFunctionPtr &Function, std::vector &Arguments, const CScriptVarPtr &This, CScriptVarPtr *newThis=0); CScriptVarPtr callFunction(bool &execute, const CScriptVarFunctionPtr &Function, std::vector &Arguments, const CScriptVarPtr &This, CScriptVarPtr *newThis=0); + /// clears the RUNTIME_THROW flag + /// clears the exceptionVar + /// returns the old exceptionVar + CScriptVarPtr removeExeptionVar() { runtimeFlags &= ~RUNTIME_THROW; CScriptVarPtr ret = exceptionVar; exceptionVar.clear(); return ret; } // // parsing - in order of precedence CScriptVarPtr mathsOp(bool &execute, const CScriptVarPtr &a, const CScriptVarPtr &b, int op); private: - CScriptVarLinkPtr execute_literals(bool &execute); - CScriptVarLinkPtr execute_member(CScriptVarLinkPtr &parent, bool &execute); - CScriptVarLinkPtr execute_function_call(bool &execute); - CScriptVarLinkPtr execute_unary(bool &execute); - CScriptVarLinkPtr execute_term(bool &execute); - CScriptVarLinkPtr execute_expression(bool &execute); - CScriptVarLinkPtr execute_binary_shift(bool &execute); - CScriptVarLinkPtr execute_relation(bool &execute, int set=LEX_EQUAL, int set_n='<'); - CScriptVarLinkPtr execute_binary_logic(bool &execute, int op='|', int op_n1='^', int op_n2='&'); - CScriptVarLinkPtr execute_logic(bool &execute, int op=LEX_OROR, int op_n=LEX_ANDAND); - CScriptVarLinkPtr execute_condition(bool &execute); + void execute_var_init(bool hideLetScope, bool &execute); + void execute_destructuring(CScriptTokenDataObjectLiteral &Objc, const CScriptVarPtr &Val, bool &execute); + CScriptVarLinkWorkPtr execute_literals(bool &execute); + CScriptVarLinkWorkPtr execute_member(CScriptVarLinkWorkPtr &parent, bool &execute); + CScriptVarLinkWorkPtr execute_function_call(bool &execute); + bool execute_unary_rhs(bool &execute, CScriptVarLinkWorkPtr& a); + CScriptVarLinkWorkPtr execute_unary(bool &execute); + CScriptVarLinkWorkPtr execute_term(bool &execute); + CScriptVarLinkWorkPtr execute_expression(bool &execute); + CScriptVarLinkWorkPtr execute_binary_shift(bool &execute); + CScriptVarLinkWorkPtr execute_relation(bool &execute, int set=LEX_EQUAL, int set_n='<'); + CScriptVarLinkWorkPtr execute_binary_logic(bool &execute, int op='|', int op_n1='^', int op_n2='&'); + CScriptVarLinkWorkPtr execute_logic(bool &execute, int op=LEX_OROR, int op_n=LEX_ANDAND); + CScriptVarLinkWorkPtr execute_condition(bool &execute); + CScriptVarLinkPtr execute_assignment(CScriptVarLinkWorkPtr Lhs, bool &execute); CScriptVarLinkPtr execute_assignment(bool &execute); CScriptVarLinkPtr execute_base(bool &execute); - void execute_block(bool &execute); + void execute_block(bool &execute, bool createLetScope=true); CScriptVarLinkPtr execute_statement(bool &execute); // parsing utility functions - CScriptVarLinkPtr parseFunctionDefinition(CScriptToken &FncToken); - CScriptVarLinkPtr parseFunctionsBodyFromString(const std::string &ArgumentList, const std::string &FncBody); + CScriptVarLinkWorkPtr parseFunctionDefinition(const CScriptToken &FncToken); + CScriptVarLinkWorkPtr parseFunctionsBodyFromString(const std::string &ArgumentList, const std::string &FncBody); public: - CScriptVarLink *findInScopes(const std::string &childName); ///< Finds a child, looking recursively up the scopes + CScriptVarLinkPtr findInScopes(const std::string &childName); ///< Finds a child, looking recursively up the scopes private: ////////////////////////////////////////////////////////////////////////// /// addNative-helper @@ -1578,31 +1884,47 @@ class CTinyJS { ////////////////////////////////////////////////////////////////////////// /// throws an Error & Exception +public: void throwError(bool &execute, ERROR_TYPES ErrorType, const std::string &message); void throwException(ERROR_TYPES ErrorType, const std::string &message); void throwError(bool &execute, ERROR_TYPES ErrorType, const std::string &message, CScriptTokenizer::ScriptTokenPosition &Pos); void throwException(ERROR_TYPES ErrorType, const std::string &message, CScriptTokenizer::ScriptTokenPosition &Pos); - +private: ////////////////////////////////////////////////////////////////////////// /// native Object-Constructors & prototype-functions void native_Object(const CFunctionsScopePtr &c, void *data); - void native_Object_hasOwnProperty(const CFunctionsScopePtr &c, void *data); void native_Object_getPrototypeOf(const CFunctionsScopePtr &c, void *data); - void native_Object_valueOf(const CFunctionsScopePtr &c, void *data); - void native_Object_toString(const CFunctionsScopePtr &c, void *data); + /* Userdate for set-/isObjectState + * 0 - preventExtensions / isExtensible + * 1 - seal / isSealed + * 2 - freeze / isFrozen + */ + void native_Object_setObjectSecure(const CFunctionsScopePtr &c, void *data); + void native_Object_isSecureObject(const CFunctionsScopePtr &c, void *data); + void native_Object_keys(const CFunctionsScopePtr &c, void *data); + void native_Object_getOwnPropertyDescriptor(const CFunctionsScopePtr &c, void *data); + void native_Object_defineProperty(const CFunctionsScopePtr &c, void *data); + void native_Object_defineProperties(const CFunctionsScopePtr &c, void *data); + + void native_Object_prototype_hasOwnProperty(const CFunctionsScopePtr &c, void *data); + void native_Object_prototype_valueOf(const CFunctionsScopePtr &c, void *data); + void native_Object_prototype_toString(const CFunctionsScopePtr &c, void *data); void native_Array(const CFunctionsScopePtr &c, void *data); void native_String(const CFunctionsScopePtr &c, void *data); + void native_RegExp(const CFunctionsScopePtr &c, void *data); + void native_Number(const CFunctionsScopePtr &c, void *data); void native_Boolean(const CFunctionsScopePtr &c, void *data); void native_Function(const CFunctionsScopePtr &c, void *data); - void native_Function_call(const CFunctionsScopePtr &c, void *data); - void native_Function_apply(const CFunctionsScopePtr &c, void *data); + void native_Function_prototype_call(const CFunctionsScopePtr &c, void *data); + void native_Function_prototype_apply(const CFunctionsScopePtr &c, void *data); + void native_Function_prototype_bind(const CFunctionsScopePtr &c, void *data); void native_Error(const CFunctionsScopePtr &c, void *data); void native_EvalError(const CFunctionsScopePtr &c, void *data); @@ -1634,9 +1956,14 @@ class CTinyJS { void ClearUnreferedVars(const CScriptVarPtr &extra=CScriptVarPtr()); }; + ////////////////////////////////////////////////////////////////////////// template inline const CScriptVarPtr &CScriptVar::constScriptVar(T t) { return context->constScriptVar(t); } ////////////////////////////////////////////////////////////////////////// +inline CNumber CScriptVarLink::toNumber() { return var->toNumber(); } +inline CNumber CScriptVarLink::toNumber(bool &execute) { return var->toNumber(execute); } #endif + + diff --git a/TinyJS_Functions.cpp b/TinyJS_Functions.cpp index 1186009..35205a2 100755 --- a/TinyJS_Functions.cpp +++ b/TinyJS_Functions.cpp @@ -14,7 +14,7 @@ * * Authored / Changed By Armin Diedering * - * Copyright (C) 2010 ardisoft + * Copyright (C) 2010-2012 ardisoft * * * Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -41,7 +41,7 @@ #include #include #include -#include "TinyJS_Functions.h" +#include "TinyJS.h" using namespace std; // ----------------------------------------------- Actual Functions @@ -64,7 +64,7 @@ static void scObjectClone(const CFunctionsScopePtr &c, void *) { } static void scIntegerValueOf(const CFunctionsScopePtr &c, void *) { - string str = c->getArgument("str")->getString(); + string str = c->getArgument("str")->toString(); int val = 0; if (str.length()==1) @@ -73,8 +73,10 @@ static void scIntegerValueOf(const CFunctionsScopePtr &c, void *) { } static void scJSONStringify(const CFunctionsScopePtr &c, void *) { - string indent = " ", indentString; - c->setReturnVar(c->newScriptVar(c->getArgument("obj")->getParsableString(indentString, indent))); + uint32_t UniqueID = c->getContext()->getUniqueID(); + bool hasRecursion=false; + c->setReturnVar(c->newScriptVar(c->getArgument("obj")->getParsableString("", " ", UniqueID, hasRecursion))); + if(hasRecursion) c->throwError(TypeError, "cyclic object value"); } static void scArrayContains(const CFunctionsScopePtr &c, void *data) { @@ -85,7 +87,7 @@ static void scArrayContains(const CFunctionsScopePtr &c, void *data) { CScriptVarPtr equal = c->constScriptVar(Undefined); for (int i=0;imathsOp(arr->getArrayIndex(i), LEX_EQUAL); - if(equal->getBool()) { + if(equal->toBoolean()) { c->setReturnVar(c->constScriptVar(true)); return; } @@ -103,7 +105,7 @@ static void scArrayRemove(const CFunctionsScopePtr &c, void *data) { CScriptVarPtr equal = c->constScriptVar(Undefined); for (i=0;imathsOp(arr->getArrayIndex(i), LEX_EQUAL); - if(equal->getBool()) { + if(equal->toBoolean()) { removedIndices.push_back(i); } } @@ -113,7 +115,7 @@ static void scArrayRemove(const CFunctionsScopePtr &c, void *data) { int next_insert = *remove_it++; for (i=next_remove;ifindChild(int2string(i)); + CScriptVarLinkPtr link = arr->findChild(int2string(i)); if(i == next_remove) { if(link) arr->removeLink(link); if(remove_it != removedIndices.end()) @@ -129,14 +131,14 @@ static void scArrayRemove(const CFunctionsScopePtr &c, void *data) { } static void scArrayJoin(const CFunctionsScopePtr &c, void *data) { - string sep = c->getArgument("separator")->getString(); + string sep = c->getArgument("separator")->toString(); CScriptVarPtr arr = c->getArgument("this"); ostringstream sstr; int l = arr->getArrayLength(); for (int i=0;i0) sstr << sep; - sstr << arr->getArrayIndex(i)->getString(); + sstr << arr->getArrayIndex(i)->toString(); } c->setReturnVar(c->newScriptVar(sstr.str())); @@ -144,14 +146,16 @@ static void scArrayJoin(const CFunctionsScopePtr &c, void *data) { // ----------------------------------------------- Register Functions void registerFunctions(CTinyJS *tinyJS) { - tinyJS->addNative("function trace()", scTrace, tinyJS); - tinyJS->addNative("function Object.prototype.dump()", scObjectDump, 0); - tinyJS->addNative("function Object.prototype.clone()", scObjectClone, 0); - - tinyJS->addNative("function Integer.valueOf(str)", scIntegerValueOf, 0); // value of a single character - tinyJS->addNative("function JSON.stringify(obj, replacer)", scJSONStringify, 0); // convert to JSON. replacer is ignored at the moment - tinyJS->addNative("function Array.prototype.contains(obj)", scArrayContains, 0); - tinyJS->addNative("function Array.prototype.remove(obj)", scArrayRemove, 0); - tinyJS->addNative("function Array.prototype.join(separator)", scArrayJoin, 0); +} +extern "C" void _registerFunctions(CTinyJS *tinyJS) { + tinyJS->addNative("function trace()", scTrace, tinyJS, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function Object.prototype.dump()", scObjectDump, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function Object.prototype.clone()", scObjectClone, 0, SCRIPTVARLINK_BUILDINDEFAULT); + + tinyJS->addNative("function Integer.valueOf(str)", scIntegerValueOf, 0, SCRIPTVARLINK_BUILDINDEFAULT); // value of a single character + tinyJS->addNative("function JSON.stringify(obj, replacer)", scJSONStringify, 0, SCRIPTVARLINK_BUILDINDEFAULT); // convert to JSON. replacer is ignored at the moment + tinyJS->addNative("function Array.prototype.contains(obj)", scArrayContains, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function Array.prototype.remove(obj)", scArrayRemove, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function Array.prototype.join(separator)", scArrayJoin, 0, SCRIPTVARLINK_BUILDINDEFAULT); } diff --git a/TinyJS_Functions.h b/TinyJS_Functions.h index 2ddbcfe..105f7d2 100755 --- a/TinyJS_Functions.h +++ b/TinyJS_Functions.h @@ -14,7 +14,7 @@ * * Authored / Changed By Armin Diedering * - * Copyright (C) 2010 ardisoft + * Copyright (C) 2010-2012 ardisoft * * * Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -36,12 +36,14 @@ * SOFTWARE. */ +#pragma message("The include "__FILE__" is deprecated - Functions now registered by default") + #ifndef TINYJS_FUNCTIONS_H #define TINYJS_FUNCTIONS_H #include "TinyJS.h" /// Register useful functions with the TinyJS interpreter -extern void registerFunctions(CTinyJS *tinyJS); +extern void DEPRECATED("is deprecated - Functions now registered by default") registerFunctions(CTinyJS *tinyJS); #endif diff --git a/TinyJS_MathFunctions.cpp b/TinyJS_MathFunctions.cpp index 08e420e..ab4215d 100644 --- a/TinyJS_MathFunctions.cpp +++ b/TinyJS_MathFunctions.cpp @@ -16,7 +16,7 @@ * * Authored / Changed By Armin Diedering * - * Copyright (C) 2010 ardisoft + * Copyright (C) 2010-2012 ardisoft * * * Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -38,11 +38,11 @@ * SOFTWARE. */ -#include +#include #include #include #include -#include "TinyJS_MathFunctions.h" +#include "TinyJS.h" using namespace std; @@ -61,343 +61,381 @@ using namespace std; #define F_SGN(a) ((a)>0 ? 1 : ((a)<0 ? -1 : 0 )) #define F_RNG(a,min,max) ((a)<(min) ? min : ((a)>(max) ? max : a )) -//CScriptVar shortcut macro -#define scIsInt(a) ( c->getArgument(a)->isInt() ) -#define scIsDouble(a) ( c->getArgument(a)->isDouble() ) -#define scGetInt(a) ( c->getArgument(a)->getInt() ) -#define scGetDouble(a) ( c->getArgument(a)->getDouble() ) -#define scReturnInt(a) ( c->setReturnVar(c->newScriptVar((int)a)) ) -#define scReturnDouble(a) ( c->setReturnVar(c->newScriptVar((double)a)) ) -#define scReturnNaN() do { c->setReturnVar(c->constScriptVar(NaN)); return; } while(0) - #ifdef _MSC_VER namespace { - double asinh( const double &value ) - { - double returned; + double asinh( const double &value ) { + double returned; + + if(value>0) + returned = log(value + sqrt(value * value + 1)); + else + returned = -log(-value + sqrt(value * value + 1)); - if(value>0) - returned = log(value + sqrt(value * value + 1)); - else - returned = -log(-value + sqrt(value * value + 1)); + return(returned); + } - return(returned); - } + double acosh( const double &value ) { + double returned; - double acosh( const double &value ) - { - double returned; + if(value>0) + returned = log(value + sqrt(value * value - 1)); + else + returned = -log(-value + sqrt(value * value - 1)); - if(value>0) - returned = log(value + sqrt(value * value - 1)); - else - returned = -log(-value + sqrt(value * value - 1)); + return(returned); + } - return(returned); - } + double atanh( double value ) { + bool neg = value<0; + if(neg) value=-value; + double value_x2 = 2.0*value; + if(value>=0.5) + value = log(1.0+value_x2/(1.0-value))/2.0; + else + value = log(1.0+value_x2+value_x2*value/(1.0-value))/2.0; + return(neg ? -value : value); + } } #endif -#define GET_PARAMETER_AS_NUMERIC_VAR(v,n) CScriptVarPtr v = c->getArgument(n)->getNumericVar() -#define RETURN_NAN_IS_NAN(v) do{ if(v->isNaN()) { c->setReturnVar(v); return; } }while(0) -#define RETURN_NAN_IS_NAN_OR_INFINITY(v) do{ if(v->isNaN() || v->isInfinity()) { c->setReturnVar(v); return; } }while(0) -#define RETURN_INFINITY_IS_INFINITY(v) do{ if(v->isInfinity()) { c->setReturnVar(v); return; } }while(0) - -#define GET_DOUBLE(v) v->getDouble() +#define PARAMETER_TO_NUMBER(v,n) CNumber v = c->getArgument(n)->toNumber() +#define RETURN_NAN_IS_NAN(v) do{ if(v.isNaN()) { c->setReturnVar(c->newScriptVar(v)); return; } }while(0) +#define RETURN_NAN_IS_NAN_OR_INFINITY(v) do{ if(v.isNaN() || v.isInfinity()) { c->setReturnVar(c->newScriptVar(v)); return; } }while(0) +#define RETURN_INFINITY_IS_INFINITY(v) do{ if(v.isInfinity()) { c->setReturnVar(c->newScriptVar(v)); return; } }while(0) +#define RETURN_ZERO_IS_ZERO(v) do{ if(v.isZero()) { c->setReturnVar(c->newScriptVar(v)); return; } }while(0) +#define RETURN(a) do{ c->setReturnVar(c->newScriptVar(a)); return; }while(0) +#define RETURNconst(a) c->setReturnVar(c->constScriptVar(a)) //Math.abs(x) - returns absolute of given value static void scMathAbs(const CFunctionsScopePtr &c, void *userdata) { - GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN(a); - if(a->getInt() < 0) { - CScriptVarPtr zero = c->newScriptVar(0); - c->setReturnVar(zero->mathsOp(a, '-')); - } else - c->setReturnVar(a); + PARAMETER_TO_NUMBER(a,"a"); + RETURN(a.sign()<0?-a:a); } //Math.round(a) - returns nearest round of given value static void scMathRound(const CFunctionsScopePtr &c, void *userdata) { - GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN(a); RETURN_INFINITY_IS_INFINITY(a); - scReturnInt( (int)floor( GET_DOUBLE(a)+0.5 ) ); + PARAMETER_TO_NUMBER(a,"a"); + RETURN(a.round()); } //Math.ceil(a) - returns nearest round of given value static void scMathCeil(const CFunctionsScopePtr &c, void *userdata) { - GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN(a); RETURN_INFINITY_IS_INFINITY(a); - scReturnInt( (int)ceil( GET_DOUBLE(a) ) ); + PARAMETER_TO_NUMBER(a,"a"); RETURN_NAN_IS_NAN(a); RETURN_INFINITY_IS_INFINITY(a); + RETURN(a.ceil()); } //Math.floor(a) - returns nearest round of given value static void scMathFloor(const CFunctionsScopePtr &c, void *userdata) { - GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN(a); RETURN_INFINITY_IS_INFINITY(a); - scReturnInt( (int)floor( GET_DOUBLE(a) ) ); + PARAMETER_TO_NUMBER(a,"a"); + RETURN(a.floor()); } //Math.min(a,b) - returns minimum of two given values static void scMathMin(const CFunctionsScopePtr &c, void *userdata) { int length = c->getArgumentsLength(); - CScriptVarLinkPtr ret(c->constScriptVar(InfinityPositive)); + CNumber ret(InfinityPositive); for(int i=0; iisInfinity() < 0) { c->setReturnVar(a); return; } - CScriptVarPtr result = a->mathsOp(ret, '<'); - if(result->getBool()) - ret = a; + PARAMETER_TO_NUMBER(a,i); RETURN_NAN_IS_NAN(a); + if(ret>a) ret=a; } - c->setReturnVar(ret); + RETURN(ret); } //Math.max(a,b) - returns maximum of two given values static void scMathMax(const CFunctionsScopePtr &c, void *userdata) { int length = c->getArgumentsLength(); - CScriptVarLinkPtr ret(c->constScriptVar(InfinityNegative)); + CNumber ret(InfinityNegative); for(int i=0; iisInfinity() > 0) { c->setReturnVar(a); return; } - CScriptVarPtr result = a->mathsOp(ret, '>'); - if(result->getBool()) - ret = a; + PARAMETER_TO_NUMBER(a,i); RETURN_NAN_IS_NAN(a); + if(retsetReturnVar(ret); + RETURN(ret); } //Math.range(x,a,b) - returns value limited between two given values static void scMathRange(const CFunctionsScopePtr &c, void *userdata) { - GET_PARAMETER_AS_NUMERIC_VAR(x,"x"); RETURN_NAN_IS_NAN(x); - GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN(a); - GET_PARAMETER_AS_NUMERIC_VAR(b,"b"); RETURN_NAN_IS_NAN(b); - - CScriptVarPtr check; - bool check_bool; + PARAMETER_TO_NUMBER(x,"x"); RETURN_NAN_IS_NAN(x); + PARAMETER_TO_NUMBER(a,"a"); RETURN_NAN_IS_NAN(a); + PARAMETER_TO_NUMBER(b,"b"); RETURN_NAN_IS_NAN(b); - check = a->mathsOp(b, LEX_LEQUAL); - check_bool = check->getBool(); - if(!check_bool) scReturnNaN(); - - check = x->mathsOp(a, '<'); - check_bool = check->getBool(); - if(check_bool) { c->setReturnVar(a); return; } - - check = x->mathsOp(b, '>'); - check_bool = check->getBool(); - c->setReturnVar((check_bool ? b:x)); + if(a>b) RETURNconst(NaN); + if(xb) RETURN(b); + RETURN(x); } //Math.sign(a) - returns sign of given value (-1==negative,0=zero,1=positive) static void scMathSign(const CFunctionsScopePtr &c, void *userdata) { - GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN(a); - double a_d = GET_DOUBLE(a); - scReturnInt(a_d>0.0?1:a_d<0.0?-1:0); + PARAMETER_TO_NUMBER(a,"a"); RETURN_NAN_IS_NAN(a); + RETURN(a.isZero() ? 0 : a.sign()); } -static void init_rand(){ +static void scMathRandom(const CFunctionsScopePtr &c, void *) { static int inited=0; if(!inited) { inited = 1; srand((unsigned int)time(NULL)); } -} -static void scMathRandom(const CFunctionsScopePtr &c, void *) { - init_rand(); - scReturnDouble((double)rand()/RAND_MAX); + RETURN(double(rand())/RAND_MAX); } //Math.toDegrees(a) - returns degree value of a given angle in radians static void scMathToDegrees(const CFunctionsScopePtr &c, void *userdata) { - GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN(a); RETURN_INFINITY_IS_INFINITY(a); - scReturnDouble( (180.0/k_PI)*( GET_DOUBLE(a) ) ); + PARAMETER_TO_NUMBER(a,"a"); RETURN_NAN_IS_NAN(a); RETURN_INFINITY_IS_INFINITY(a); + RETURN( (180.0/k_PI)*a ); } //Math.toRadians(a) - returns radians value of a given angle in degrees static void scMathToRadians(const CFunctionsScopePtr &c, void *userdata) { - GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN(a); RETURN_INFINITY_IS_INFINITY(a); - scReturnDouble( (k_PI/180.0)*( GET_DOUBLE(a) ) ); + PARAMETER_TO_NUMBER(a,"a"); RETURN_NAN_IS_NAN(a); RETURN_INFINITY_IS_INFINITY(a); + RETURN( (k_PI/180.0)*a ); } //Math.sin(a) - returns trig. sine of given angle in radians static void scMathSin(const CFunctionsScopePtr &c, void *userdata) { - GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN_OR_INFINITY(a); - scReturnDouble( sin( GET_DOUBLE(a) ) ); + PARAMETER_TO_NUMBER(a,"a"); RETURN_NAN_IS_NAN_OR_INFINITY(a); RETURN_ZERO_IS_ZERO(a); + RETURN( sin(a.toDouble()) ); } //Math.asin(a) - returns trig. arcsine of given angle in radians static void scMathASin(const CFunctionsScopePtr &c, void *userdata) { - GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN_OR_INFINITY(a); - scReturnDouble( asin( GET_DOUBLE(a) ) ); + PARAMETER_TO_NUMBER(a,"a"); RETURN_NAN_IS_NAN(a); RETURN_ZERO_IS_ZERO(a); + if(abs(a)>1) RETURNconst(NaN); + RETURN( asin(a.toDouble()) ); } //Math.cos(a) - returns trig. cosine of given angle in radians static void scMathCos(const CFunctionsScopePtr &c, void *userdata) { - GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN_OR_INFINITY(a); - scReturnDouble( cos( GET_DOUBLE(a) ) ); + PARAMETER_TO_NUMBER(a,"a"); RETURN_NAN_IS_NAN_OR_INFINITY(a); + if(a.isZero()) RETURN(1); + RETURN( cos(a.toDouble()) ); } //Math.acos(a) - returns trig. arccosine of given angle in radians static void scMathACos(const CFunctionsScopePtr &c, void *userdata) { - GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN_OR_INFINITY(a); - scReturnDouble( acos( GET_DOUBLE(a) ) ); + PARAMETER_TO_NUMBER(a,"a"); RETURN_NAN_IS_NAN_OR_INFINITY(a); + if(abs(a)>1) RETURNconst(NaN); + else if(a==1) RETURN(0); + RETURN( acos(a.toDouble()) ); } //Math.tan(a) - returns trig. tangent of given angle in radians static void scMathTan(const CFunctionsScopePtr &c, void *userdata) { - GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN_OR_INFINITY(a); - scReturnDouble( tan( GET_DOUBLE(a) ) ); + PARAMETER_TO_NUMBER(a,"a"); RETURN_NAN_IS_NAN_OR_INFINITY(a); RETURN_ZERO_IS_ZERO(a); + RETURN( tan(a.toDouble()) ); } //Math.atan(a) - returns trig. arctangent of given angle in radians static void scMathATan(const CFunctionsScopePtr &c, void *userdata) { - GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN_OR_INFINITY(a); - scReturnDouble( atan( GET_DOUBLE(a) ) ); + PARAMETER_TO_NUMBER(a,"a"); RETURN_NAN_IS_NAN(a); RETURN_ZERO_IS_ZERO(a); + int infinity=a.isInfinity(); + if(infinity) RETURN(k_PI/(infinity*2)); + RETURN( atan(a.toDouble()) ); +} + +//Math.atan2(a,b) - returns trig. arctangent of given angle in radians +static void scMathATan2(const CFunctionsScopePtr &c, void *userdata) { + PARAMETER_TO_NUMBER(a,"a"); RETURN_NAN_IS_NAN(a); + PARAMETER_TO_NUMBER(b,"b"); RETURN_NAN_IS_NAN(b); + int sign_a = a.sign(); + int sign_b = b.sign(); + if(a.isZero()) + RETURN(sign_a>0 ? (sign_b>0 ? 0.0 : k_PI) : (sign_b>0 ? -0.0 : -k_PI)); + else if(b.isZero()) + RETURN((sign_a>0 ? k_PI : -k_PI)/2.0); + int infinity_a=a.isInfinity(); + int infinity_b=b.isInfinity(); + if(infinity_a) { + if(infinity_b>0) RETURN(k_PI/(infinity_a*4)); + else if(infinity_b<0) RETURN(3.0*k_PI/(infinity_a*4)); + else RETURN(k_PI/(infinity_a*2)); + } else if(infinity_b>0) + RETURN(sign_a>0 ? 0.0 : -0.0); + else if(infinity_b<0) + RETURN(sign_a>0 ? k_PI : -k_PI); + RETURN( atan2(a.toDouble(), b.toDouble()) ); } + //Math.sinh(a) - returns trig. hyperbolic sine of given angle in radians static void scMathSinh(const CFunctionsScopePtr &c, void *userdata) { - GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN_OR_INFINITY(a); - scReturnDouble( sinh( GET_DOUBLE(a) ) ); + PARAMETER_TO_NUMBER(a,"a"); RETURN_NAN_IS_NAN(a); + RETURN_ZERO_IS_ZERO(a); + if(abs(a)>1) RETURNconst(NaN); + RETURN( sinh(a.toDouble()) ); } //Math.asinh(a) - returns trig. hyperbolic arcsine of given angle in radians static void scMathASinh(const CFunctionsScopePtr &c, void *userdata) { - GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN_OR_INFINITY(a); - scReturnDouble( asinh( GET_DOUBLE(a) ) ); + PARAMETER_TO_NUMBER(a,"a"); RETURN_NAN_IS_NAN(a); + RETURN_INFINITY_IS_INFINITY(a); + RETURN_ZERO_IS_ZERO(a); + RETURN( asinh(a.toDouble()) ); } //Math.cosh(a) - returns trig. hyperbolic cosine of given angle in radians static void scMathCosh(const CFunctionsScopePtr &c, void *userdata) { - GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN_OR_INFINITY(a); - scReturnDouble( cosh( GET_DOUBLE(a) ) ); + PARAMETER_TO_NUMBER(a,"a"); RETURN_NAN_IS_NAN(a); + if(a.isInfinity()) RETURNconst(InfinityPositive); + RETURN( cosh(a.toDouble()) ); } //Math.acosh(a) - returns trig. hyperbolic arccosine of given angle in radians static void scMathACosh(const CFunctionsScopePtr &c, void *userdata) { - GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN_OR_INFINITY(a); - scReturnDouble( acosh( GET_DOUBLE(a) ) ); + PARAMETER_TO_NUMBER(a,"a"); RETURN_NAN_IS_NAN(a); + RETURN_INFINITY_IS_INFINITY(a); + if(abs(a)<1) RETURNconst(NaN); + RETURN( acosh(a.toDouble()) ); } //Math.tanh(a) - returns trig. hyperbolic tangent of given angle in radians static void scMathTanh(const CFunctionsScopePtr &c, void *userdata) { - GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN_OR_INFINITY(a); - scReturnDouble( tanh( GET_DOUBLE(a) ) ); + PARAMETER_TO_NUMBER(a,"a"); RETURN_NAN_IS_NAN(a); + RETURN_ZERO_IS_ZERO(a); + if(a.isInfinity()) RETURN(a.sign()); + RETURN( tanh(a.toDouble()) ); } -//Math.atan(a) - returns trig. hyperbolic arctangent of given angle in radians +//Math.atanh(a) - returns trig. hyperbolic arctangent of given angle in radians static void scMathATanh(const CFunctionsScopePtr &c, void *userdata) { - GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN_OR_INFINITY(a); - scReturnDouble( atan( GET_DOUBLE(a) ) ); + PARAMETER_TO_NUMBER(a,"a"); RETURN_NAN_IS_NAN(a); + RETURN_ZERO_IS_ZERO(a); + CNumber abs_a = abs(a); + if(abs_a > 1) RETURNconst(NaN); + if(abs_a == 1) RETURNconst(Infinity(a.sign())); + RETURN( atanh(a.toDouble()) ); } //Math.log(a) - returns natural logaritm (base E) of given value static void scMathLog(const CFunctionsScopePtr &c, void *userdata) { - GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN(a); - int a_i = a->isInfinity(); - double a_d = a->getDouble(); - if(a_i>0) { c->setReturnVar(c->constScriptVar(InfinityPositive)); return; } - else if(a_i<0 || a_d<0.0) scReturnNaN(); - scReturnDouble( log( a_d ) ); + PARAMETER_TO_NUMBER(a,"a"); RETURN_NAN_IS_NAN(a); + if(a.isZero()) RETURNconst(InfinityNegative); + if(a.sign()<0) RETURNconst(NaN); + if(a.isInfinity()) RETURNconst(InfinityPositive); + RETURN( log( a.toDouble()) ); } //Math.log10(a) - returns logaritm(base 10) of given value static void scMathLog10(const CFunctionsScopePtr &c, void *userdata) { - GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN(a); - int a_i = a->isInfinity(); - double a_d = a->getDouble(); - if(a_i>0) { c->setReturnVar(c->constScriptVar(InfinityPositive)); return; } - else if(a_i<0 || a_d<0.0) scReturnNaN(); - scReturnDouble( log10( a_d ) ); + PARAMETER_TO_NUMBER(a,"a"); RETURN_NAN_IS_NAN(a); + if(a.isZero()) RETURNconst(InfinityNegative); + if(a.sign()<0) RETURNconst(NaN); + if(a.isInfinity()) RETURNconst(InfinityPositive); + RETURN( log10( a.toDouble()) ); } //Math.exp(a) - returns e raised to the power of a given number static void scMathExp(const CFunctionsScopePtr &c, void *userdata) { - GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN(a); - int a_i = a->isInfinity(); - if(a_i>0) { c->setReturnVar(c->constScriptVar(InfinityPositive)); return; } - else if(a_i<0) { c->setReturnVar(c->newScriptVar(0)); return; } - scReturnDouble( exp( GET_DOUBLE(a) ) ); + PARAMETER_TO_NUMBER(a,"a"); RETURN_NAN_IS_NAN(a); + if(a.isZero()) RETURN(1); + int a_i = a.isInfinity(); + if(a_i>0) RETURNconst(InfinityPositive); + else if(a_i<0) RETURN(0); + RETURN( exp(a.toDouble()) ); } //Math.pow(a,b) - returns the result of a number raised to a power (a)^(b) static void scMathPow(const CFunctionsScopePtr &c, void *userdata) { - GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN(a); - GET_PARAMETER_AS_NUMERIC_VAR(b,"b"); RETURN_NAN_IS_NAN(b); - int a_i = a->isInfinity(), b_i = b->isInfinity(); - double a_d = a->getDouble(), b_d = b->getDouble(); - if(b_i>0) { - if(a_i || a_d>1.0 || a_d<-1.0) { c->setReturnVar(c->constScriptVar(InfinityPositive)); return; } - else if(a_i==0 && (a_d==1.0 || a_d==-1.0)) { c->setReturnVar(c->newScriptVar(1)); return; } - if(a_i==0 && a_d<1.0 && a_d>-1.0) { c->setReturnVar(c->newScriptVar(0)); return; } - } else if(b_i<0) { c->setReturnVar(c->newScriptVar(0)); return; } - else if(b_d == 0.0) { c->setReturnVar(c->newScriptVar(1)); return; } - - if(a_i) a_d = a_i; - double result = pow(a_d, b_d); - if(a_i) { c->setReturnVar(c->constScriptVar(Infinity(result>=0.0?1:-1))); return; } - scReturnDouble( result ); + PARAMETER_TO_NUMBER(a,"a"); + PARAMETER_TO_NUMBER(b,"b"); RETURN_NAN_IS_NAN(b); + if(b.isZero()) RETURN(1); + RETURN_NAN_IS_NAN(a); + if(b==1) RETURN(a); + + int sign; + CNumber a_abs = abs(a); + if((sign = b.isInfinity())) { + if( a_abs==1 ) RETURNconst(NaN); + else if( (a_abs>1) ^ (sign<0) ) RETURN(b); else RETURN(0); + } else if((sign = a.isInfinity())) { + if(sign>0) { + if(b.sign()>0) RETURN(a); else RETURN(0); + } else { + bool b_is_odd_int = ((b+1)/2).isInteger(); + if(b.sign()>0) RETURNconst(b_is_odd_int?InfinityNegative:InfinityPositive); + else RETURN(b_is_odd_int?CNumber(NegativeZero):CNumber(0)); + } + } else if(a.isZero()) { + if(a.isNegativeZero()) { + bool b_is_odd_int = ((b+1)/2).isInteger(); + if(b.sign()>0) RETURN(b_is_odd_int?CNumber(NegativeZero):CNumber(0)); + else RETURNconst(b_is_odd_int?InfinityNegative:InfinityPositive); + } else + if(b.sign()>0) RETURN(a); else RETURNconst(InfinityPositive); + } + if(a.sign()<0 && !b.isInteger()) RETURNconst(NaN); + + RETURN( pow(a.toDouble(), b.toDouble()) ); } //Math.sqr(a) - returns square of given value static void scMathSqr(const CFunctionsScopePtr &c, void *userdata) { - GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN(a); RETURN_INFINITY_IS_INFINITY(a); - scReturnDouble( ( GET_DOUBLE(a) * GET_DOUBLE(a) ) ); + PARAMETER_TO_NUMBER(a,"a"); + RETURN( a*a ); } //Math.sqrt(a) - returns square root of given value static void scMathSqrt(const CFunctionsScopePtr &c, void *userdata) { - GET_PARAMETER_AS_NUMERIC_VAR(a,"a"); RETURN_NAN_IS_NAN(a); RETURN_INFINITY_IS_INFINITY(a); - scReturnDouble( sqrt( GET_DOUBLE(a) ) ); + PARAMETER_TO_NUMBER(a,"a"); RETURN_NAN_IS_NAN(a); + RETURN_ZERO_IS_ZERO(a); + if(a.sign()<0) RETURNconst(NaN); + RETURN_INFINITY_IS_INFINITY(a); + RETURN( sqrt(a.toDouble()) ); } // ----------------------------------------------- Register Functions -void registerMathFunctions(CTinyJS *tinyJS) { +void registerMathFunctions(CTinyJS *tinyJS) {} +extern "C" void _registerMathFunctions(CTinyJS *tinyJS) { - CScriptVarPtr Math = tinyJS->getRoot()->addChild("Math", tinyJS->newScriptVar(Object)); + CScriptVarPtr Math = tinyJS->getRoot()->addChild("Math", tinyJS->newScriptVar(Object), SCRIPTVARLINK_CONSTANT); // --- Math and Trigonometry functions --- - tinyJS->addNative("function Math.abs(a)", scMathAbs, 0); - tinyJS->addNative("function Math.round(a)", scMathRound, 0); - tinyJS->addNative("function Math.ceil(a)", scMathCeil, 0); - tinyJS->addNative("function Math.floor(a)", scMathFloor, 0); - tinyJS->addNative("function Math.min()", scMathMin, 0); - tinyJS->addNative("function Math.max()", scMathMax, 0); - tinyJS->addNative("function Math.range(x,a,b)", scMathRange, 0); - tinyJS->addNative("function Math.sign(a)", scMathSign, 0); - tinyJS->addNative("function Math.random(a)", scMathRandom, 0); + tinyJS->addNative("function Math.abs(a)", scMathAbs, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function Math.round(a)", scMathRound, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function Math.ceil(a)", scMathCeil, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function Math.floor(a)", scMathFloor, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function Math.min()", scMathMin, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function Math.max()", scMathMax, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function Math.range(x,a,b)", scMathRange, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function Math.sign(a)", scMathSign, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function Math.random(a)", scMathRandom, 0, SCRIPTVARLINK_BUILDINDEFAULT); // atan2, ceil, floor, random, round, - Math->addChild("LN2", tinyJS->newScriptVar(k_LN2), SCRIPTVARLINK_ENUMERABLE); - Math->addChild("LN10", tinyJS->newScriptVar(k_LN10), SCRIPTVARLINK_ENUMERABLE); - Math->addChild("LOG2E", tinyJS->newScriptVar(k_LOG2E), SCRIPTVARLINK_ENUMERABLE); - Math->addChild("LOG10E", tinyJS->newScriptVar(k_LOG10E), SCRIPTVARLINK_ENUMERABLE); - Math->addChild("SQRT1_2", tinyJS->newScriptVar(k_SQRT1_2), SCRIPTVARLINK_ENUMERABLE); - Math->addChild("SQRT2", tinyJS->newScriptVar(k_SQRT2), SCRIPTVARLINK_ENUMERABLE); - Math->addChild("PI", tinyJS->newScriptVar(k_PI), SCRIPTVARLINK_ENUMERABLE); + Math->addChild("LN2", tinyJS->newScriptVar(k_LN2), SCRIPTVARLINK_READONLY); + Math->addChild("LN10", tinyJS->newScriptVar(k_LN10), SCRIPTVARLINK_READONLY); + Math->addChild("LOG2E", tinyJS->newScriptVar(k_LOG2E), SCRIPTVARLINK_READONLY); + Math->addChild("LOG10E", tinyJS->newScriptVar(k_LOG10E), SCRIPTVARLINK_READONLY); + Math->addChild("SQRT1_2", tinyJS->newScriptVar(k_SQRT1_2), SCRIPTVARLINK_READONLY); + Math->addChild("SQRT2", tinyJS->newScriptVar(k_SQRT2), SCRIPTVARLINK_READONLY); + Math->addChild("PI", tinyJS->newScriptVar(k_PI), SCRIPTVARLINK_READONLY); // tinyJS->addNative("function Math.PI()", scMathPI, 0); - tinyJS->addNative("function Math.toDegrees(a)", scMathToDegrees, 0); - tinyJS->addNative("function Math.toRadians(a)", scMathToRadians, 0); - tinyJS->addNative("function Math.sin(a)", scMathSin, 0); - tinyJS->addNative("function Math.asin(a)", scMathASin, 0); - tinyJS->addNative("function Math.cos(a)", scMathCos, 0); - tinyJS->addNative("function Math.acos(a)", scMathACos, 0); - tinyJS->addNative("function Math.tan(a)", scMathTan, 0); - tinyJS->addNative("function Math.atan(a)", scMathATan, 0); - tinyJS->addNative("function Math.sinh(a)", scMathSinh, 0); - tinyJS->addNative("function Math.asinh(a)", scMathASinh, 0); - tinyJS->addNative("function Math.cosh(a)", scMathCosh, 0); - tinyJS->addNative("function Math.acosh(a)", scMathACosh, 0); - tinyJS->addNative("function Math.tanh(a)", scMathTanh, 0); - tinyJS->addNative("function Math.atanh(a)", scMathATanh, 0); + tinyJS->addNative("function Math.toDegrees(a)", scMathToDegrees, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function Math.toRadians(a)", scMathToRadians, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function Math.sin(a)", scMathSin, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function Math.asin(a)", scMathASin, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function Math.cos(a)", scMathCos, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function Math.acos(a)", scMathACos, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function Math.tan(a)", scMathTan, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function Math.atan(a)", scMathATan, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function Math.atan2(a,b)", scMathATan2, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function Math.sinh(a)", scMathSinh, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function Math.asinh(a)", scMathASinh, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function Math.cosh(a)", scMathCosh, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function Math.acosh(a)", scMathACosh, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function Math.tanh(a)", scMathTanh, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function Math.atanh(a)", scMathATanh, 0, SCRIPTVARLINK_BUILDINDEFAULT); - Math->addChild("E", tinyJS->newScriptVar(k_E), SCRIPTVARLINK_ENUMERABLE); -// tinyJS->addNative("function Math.E()", scMathE, 0); - tinyJS->addNative("function Math.log(a)", scMathLog, 0); - tinyJS->addNative("function Math.log10(a)", scMathLog10, 0); - tinyJS->addNative("function Math.exp(a)", scMathExp, 0); - tinyJS->addNative("function Math.pow(a,b)", scMathPow, 0); + Math->addChild("E", tinyJS->newScriptVar(k_E), SCRIPTVARLINK_READONLY); + tinyJS->addNative("function Math.log(a)", scMathLog, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function Math.log10(a)", scMathLog10, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function Math.exp(a)", scMathExp, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function Math.pow(a,b)", scMathPow, 0, SCRIPTVARLINK_BUILDINDEFAULT); - tinyJS->addNative("function Math.sqr(a)", scMathSqr, 0); - tinyJS->addNative("function Math.sqrt(a)", scMathSqrt, 0); + tinyJS->addNative("function Math.sqr(a)", scMathSqr, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function Math.sqrt(a)", scMathSqrt, 0, SCRIPTVARLINK_BUILDINDEFAULT); } diff --git a/TinyJS_MathFunctions.h b/TinyJS_MathFunctions.h index 5249e1f..f135ebb 100644 --- a/TinyJS_MathFunctions.h +++ b/TinyJS_MathFunctions.h @@ -16,7 +16,7 @@ * * Authored / Changed By Armin Diedering * - * Copyright (C) 2010 ardisoft + * Copyright (C) 2010-2012 ardisoft * * * Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -38,12 +38,14 @@ * SOFTWARE. */ +#pragma message("The include "__FILE__" is deprecated - MathFunctions now registered by default") + #ifndef TINYJS_MATHFUNCTIONS_H #define TINYJS_MATHFUNCTIONS_H #include "TinyJS.h" /// Register useful math. functions with the TinyJS interpreter -extern void registerMathFunctions(CTinyJS *tinyJS); +extern void DEPRECATED("is deprecated - MathFunctions now registered by default") registerMathFunctions(CTinyJS *tinyJS); #endif diff --git a/TinyJS_StringFunctions.cpp b/TinyJS_StringFunctions.cpp index 990cfb6..cc8907a 100644 --- a/TinyJS_StringFunctions.cpp +++ b/TinyJS_StringFunctions.cpp @@ -5,7 +5,7 @@ * * Authored By Armin Diedering * - * Copyright (C) 2010 ardisoft + * Copyright (C) 2010-2012 ardisoft * * * Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -30,12 +30,34 @@ #include #include "TinyJS.h" +#ifndef NO_REGEXP +# if defined HAVE_TR1_REGEX +# include + using namespace std::tr1; +# elif defined HAVE_BOOST_REGEX +# include + using namespace boost; +# else +# include +# endif +#endif using namespace std; // ----------------------------------------------- Actual Functions +#define CheckObjectCoercible(var) do { \ + if(var->isUndefined() || var->isNull())\ + c->throwError(TypeError, "can't convert undefined to object");\ + }while(0) + +static string this2string(const CFunctionsScopePtr &c) { + CScriptVarPtr This = c->getArgument("this"); + CheckObjectCoercible(This); + return This->toString(); +} + static void scStringCharAt(const CFunctionsScopePtr &c, void *) { - string str = c->getArgument("this")->getString(); - int p = c->getArgument("pos")->getInt(); + string str = this2string(c); + int p = c->getArgument("pos")->toNumber().toInt32(); if (p>=0 && p<(int)str.length()) c->setReturnVar(c->newScriptVar(str.substr(p, 1))); else @@ -43,39 +65,248 @@ static void scStringCharAt(const CFunctionsScopePtr &c, void *) { } static void scStringCharCodeAt(const CFunctionsScopePtr &c, void *) { - string str = c->getArgument("this")->getString(); - int p = c->getArgument("pos")->getInt(); + string str = this2string(c); + int p = c->getArgument("pos")->toNumber().toInt32(); if (p>=0 && p<(int)str.length()) - c->setReturnVar(c->newScriptVar(str.at(p))); + c->setReturnVar(c->newScriptVar((unsigned char)str.at(p))); else - c->setReturnVar(c->newScriptVar(0)); + c->setReturnVar(c->constScriptVar(NaN)); } static void scStringConcat(const CFunctionsScopePtr &c, void *userdata) { int length = c->getArgumentsLength(); - string str = c->getArgument("this")->getString(); + string str = this2string(c); for(int i=(int)userdata; igetArgument(i)->getString()); + str.append(c->getArgument(i)->toString()); c->setReturnVar(c->newScriptVar(str)); } static void scStringIndexOf(const CFunctionsScopePtr &c, void *userdata) { - string str = c->getArgument("this")->getString(); - string search = c->getArgument("search")->getString(); - size_t p = (userdata==0) ? str.find(search) : str.rfind(search); + string str = this2string(c); + string search = c->getArgument("search")->toString(); + CNumber pos_n = c->getArgument("pos")->toNumber(); + string::size_type pos; + pos = (userdata) ? string::npos : 0; + if(pos_n.sign()<0) pos = 0; + else if(pos_n.isInfinity()) pos = string::npos; + else if(pos_n.isFinite()) pos = pos_n.toInt32(); + string::size_type p = (userdata==0) ? str.find(search) : str.rfind(search); int val = (p==string::npos) ? -1 : p; c->setReturnVar(c->newScriptVar(val)); } +static void scStringLocaleCompare(const CFunctionsScopePtr &c, void *userdata) { + string str = this2string(c); + string compareString = c->getArgument("compareString")->toString(); + int val = 0; + if(strcompareString) val = 1; + c->setReturnVar(c->newScriptVar(val)); +} + +static void scStringQuote(const CFunctionsScopePtr &c, void *userdata) { + string str = this2string(c); + c->setReturnVar(c->newScriptVar(getJSString(str))); +} + +#ifndef NO_REGEXP +// helper-function for replace search +static bool regex_search(const string &str, const string::const_iterator &search_begin, const string &substr, bool ignoreCase, bool sticky, string::const_iterator &match_begin, string::const_iterator &match_end, smatch &match) { + regex::flag_type flags = regex_constants::ECMAScript; + if(ignoreCase) flags |= regex_constants::icase; + regex_constants::match_flag_type mflag = sticky?regex_constants::match_continuous:regex_constants::format_default; + if(str.begin() != search_begin) mflag |= regex_constants::match_prev_avail; + if(regex_search(search_begin, str.end(), match, regex(substr, flags), mflag)) { + match_begin = match[0].first; + match_end = match[0].second; + return true; + } + return false; +} +static bool regex_search(const string &str, const string::const_iterator &search_begin, const string &substr, bool ignoreCase, bool sticky, string::const_iterator &match_begin, string::const_iterator &match_end) { + smatch match; + return regex_search(str, search_begin, substr, ignoreCase, sticky, match_begin, match_end, match); +} +#endif /* NO_REGEXP */ + +static bool charcmp (char i, char j) { return (i==j); } +static bool charicmp (char i, char j) { return (toupper(i)==toupper(j)); } +// helper-function for replace search +static bool string_search(const string &str, const string::const_iterator &search_begin, const string &substr, bool ignoreCase, bool sticky, string::const_iterator &match_begin, string::const_iterator &match_end) { + bool (*cmp)(char,char) = ignoreCase ? charicmp : charcmp; + if(sticky) { + match_begin = match_end = search_begin; + string::const_iterator s1e=str.end(); + string::const_iterator s2=substr.begin(), s2e=substr.end(); + while(match_end!=s1e && s2!=s2e && cmp(*match_end++, *s2++)); + return s2==s2e; + } + match_begin = search(search_begin, str.end(), substr.begin(), substr.end(), cmp); + if(match_begin==str.end()) return false; + match_end = match_begin + substr.length(); + return true; +} +//************************************ +// Method: getRegExpData +// FullName: getRegExpData +// Access: public static +// Returns: bool true if regexp-param=RegExp-Object / other false +// Qualifier: +// Parameter: const CFunctionsScopePtr & c +// Parameter: const string & regexp - parameter name of the regexp +// Parameter: bool noUndefined - true an undefined regexp aims in "" else in "undefined" +// Parameter: const string & flags - parameter name of the flags +// Parameter: string & substr - rgexp.source +// Parameter: bool & global +// Parameter: bool & ignoreCase +// Parameter: bool & sticky +//************************************ +static CScriptVarPtr getRegExpData(const CFunctionsScopePtr &c, const string ®exp, bool noUndefined, const char *flags_argument, string &substr, bool &global, bool &ignoreCase, bool &sticky) { + CScriptVarPtr regexpVar = c->getArgument(regexp); + if(regexpVar->isRegExp()) { +#ifndef NO_REGEXP + CScriptVarRegExpPtr RegExp(regexpVar); + substr = RegExp->Regexp(); + ignoreCase = RegExp->IgnoreCase(); + global = RegExp->Global(); + sticky = RegExp->Sticky(); + return RegExp; +#endif /* NO_REGEXP */ + } else { + substr.clear(); + if(!noUndefined || !regexpVar->isUndefined()) substr = regexpVar->toString(); + CScriptVarPtr flagVar; + if(flags_argument && (flagVar = c->getArgument(flags_argument)) && !flagVar->isUndefined()) { + string flags = flagVar->toString(); + string::size_type pos = flags.find_first_not_of("gimy"); + if(pos != string::npos) { + c->throwError(SyntaxError, string("invalid regular expression flag ")+flags[pos]); + } + global = flags.find_first_of('g')!=string::npos; + ignoreCase = flags.find_first_of('i')!=string::npos; + sticky = flags.find_first_of('y')!=string::npos; + } else + global = ignoreCase = sticky = false; + } + return CScriptVarPtr(); +} + +static void scStringReplace(const CFunctionsScopePtr &c, void *) { + const string str = this2string(c); + CScriptVarPtr newsubstrVar = c->getArgument("newsubstr"); + string substr, ret_str; + bool global, ignoreCase, sticky; + bool isRegExp = getRegExpData(c, "substr", false, "flags", substr, global, ignoreCase, sticky); + if(isRegExp && !newsubstrVar->isFunction()) { +#ifndef NO_REGEXP + regex::flag_type flags = regex_constants::ECMAScript; + if(ignoreCase) flags |= regex_constants::icase; + regex_constants::match_flag_type mflags = regex_constants::match_default; + if(!global) mflags |= regex_constants::format_first_only; + if(sticky) mflags |= regex_constants::match_continuous; + ret_str = regex_replace(str, regex(substr, flags), newsubstrVar->toString(), mflags); +#endif /* NO_REGEXP */ + } else { + bool (*search)(const string &, const string::const_iterator &, const string &, bool, bool, string::const_iterator &, string::const_iterator &); +#ifndef NO_REGEXP + if(isRegExp) + search = regex_search; + else +#endif /* NO_REGEXP */ + search = string_search; + string newsubstr; + vector arguments; + if(!newsubstrVar->isFunction()) + newsubstr = newsubstrVar->toString(); + global = global && substr.length(); + string::const_iterator search_begin=str.begin(), match_begin, match_end; + if(search(str, search_begin, substr, ignoreCase, sticky, match_begin, match_end)) { + do { + ret_str.append(search_begin, match_begin); + if(newsubstrVar->isFunction()) { + arguments.push_back(c->newScriptVar(string(match_begin, match_end))); + newsubstr = c->getContext()->callFunction(newsubstrVar, arguments, c)->toString(); + arguments.pop_back(); + } + ret_str.append(newsubstr); + search_begin = match_end; + } while(global && search(str, search_begin, substr, ignoreCase, sticky, match_begin, match_end)); + } + ret_str.append(search_begin, str.end()); + } + c->setReturnVar(c->newScriptVar(ret_str)); +} +#ifndef NO_REGEXP +static void scStringMatch(const CFunctionsScopePtr &c, void *) { + string str = this2string(c); + + string flags="flags", substr, newsubstr, match; + bool global, ignoreCase, sticky; + CScriptVarRegExpPtr RegExp = getRegExpData(c, "regexp", true, "flags", substr, global, ignoreCase, sticky); + if(!global) { + if(!RegExp) + RegExp = ::newScriptVar(c->getContext(), substr, flags); + if(RegExp) { + try { + c->setReturnVar(RegExp->exec(str)); + } catch(regex_error e) { + c->throwError(SyntaxError, string(e.what())+" - "+CScriptVarRegExp::ErrorStr(e.code())); + } + } + } else { + try { + CScriptVarArrayPtr retVar = c->newScriptVar(Array); + int idx=0; + string::size_type offset=0; + global = global && substr.length(); + string::const_iterator search_begin=str.begin(), match_begin, match_end; + if(regex_search(str, search_begin, substr, ignoreCase, sticky, match_begin, match_end)) { + do { + offset = match_begin-str.begin(); + retVar->addChild(int2string(idx++), c->newScriptVar(string(match_begin, match_end))); + search_begin = match_end; + } while(global && regex_search(str, search_begin, substr, ignoreCase, sticky, match_begin, match_end)); + } + if(idx) { + retVar->addChild("input", c->newScriptVar(str)); + retVar->addChild("index", c->newScriptVar((int)offset)); + c->setReturnVar(retVar); + } else + c->setReturnVar(c->constScriptVar(Null)); + } catch(regex_error e) { + c->throwError(SyntaxError, string(e.what())+" - "+CScriptVarRegExp::ErrorStr(e.code())); + } + } +} +#endif /* NO_REGEXP */ + +static void scStringSearch(const CFunctionsScopePtr &c, void *userdata) { + string str = this2string(c); + + string substr; + bool global, ignoreCase, sticky; + getRegExpData(c, "regexp", true, "flags", substr, global, ignoreCase, sticky); + string::const_iterator search_begin=str.begin(), match_begin, match_end; +#ifndef NO_REGEXP + try { + c->setReturnVar(c->newScriptVar(regex_search(str, search_begin, substr, ignoreCase, sticky, match_begin, match_end)?match_begin-search_begin:-1)); + } catch(regex_error e) { + c->throwError(SyntaxError, string(e.what())+" - "+CScriptVarRegExp::ErrorStr(e.code())); + } +#else /* NO_REGEXP */ + c->setReturnVar(c->newScriptVar(string_search(str, search_begin, substr, ignoreCase, sticky, match_begin, match_end)?match_begin-search_begin:-1)); +#endif /* NO_REGEXP */ +} + static void scStringSlice(const CFunctionsScopePtr &c, void *userdata) { - string str = c->getArgument("this")->getString(); + string str = this2string(c); int length = c->getArgumentsLength()-((int)userdata & 1); bool slice = ((int)userdata & 2) == 0; - int start = c->getArgument("start")->getInt(); + int start = c->getArgument("start")->toNumber().toInt32(); int end = (int)str.size(); if(slice && start<0) start = str.size()+start; if(length>1) { - end = c->getArgument("end")->getInt(); + end = c->getArgument("end")->toNumber().toInt32(); if(slice && end<0) end = str.size()+end; } if(!slice && end < start) { end^=start; start^=end; end^=start; } @@ -89,10 +320,15 @@ static void scStringSlice(const CFunctionsScopePtr &c, void *userdata) { } static void scStringSplit(const CFunctionsScopePtr &c, void *) { - string str = c->getArgument("this")->getString(); + const string str = this2string(c); + + string seperator; + bool global, ignoreCase, sticky; + CScriptVarRegExpPtr RegExp = getRegExpData(c, "separator", true, 0, seperator, global, ignoreCase, sticky); + CScriptVarPtr sep_var = c->getArgument("separator"); CScriptVarPtr limit_var = c->getArgument("limit"); - int limit = limit_var->isUndefined() ? 0x7fffffff : limit_var->getInt(); + int limit = limit_var->isUndefined() ? 0x7fffffff : limit_var->toNumber().toInt32(); CScriptVarPtr result(newScriptVar(c->getContext(), Array)); c->setReturnVar(result); @@ -102,49 +338,71 @@ static void scStringSplit(const CFunctionsScopePtr &c, void *) { result->setArrayIndex(0, c->newScriptVar(str)); return; } - string sep = sep_var->getString(); - if(sep.size() == 0) { - for(int i=0; isetArrayIndex(i, c->newScriptVar(str.substr(i,1))); return; } int length = 0; - string::size_type pos = 0, last_pos=0; - do { - pos = str.find(sep, last_pos); - result->setArrayIndex(length++, c->newScriptVar(str.substr(last_pos ,pos-last_pos))); - if(length == limit || pos == string::npos) break; - last_pos = pos+sep.size(); - } while (last_pos < str.size()); + string::const_iterator search_begin=str.begin(), match_begin, match_end; + smatch match; + bool found=true; + while(found) { + if(RegExp) { + try { + found = regex_search(str, search_begin, seperator, ignoreCase, sticky, match_begin, match_end, match); + } catch(regex_error e) { + c->throwError(SyntaxError, string(e.what())+" - "+CScriptVarRegExp::ErrorStr(e.code())); + } + } else /* NO_REGEXP */ + found = string_search(str, search_begin, seperator, ignoreCase, sticky, match_begin, match_end); + string f; + if(found) { + result->setArrayIndex(length++, c->newScriptVar(string(search_begin, match_begin))); + if(length>=limit) break; + for(uint32_t i=1; isetArrayIndex(length++, c->newScriptVar(string(match[i].first, match[i].second))); + else + result->setArrayIndex(length++, c->constScriptVar(Undefined)); + if(length>=limit) break; + } + if(length>=limit) break; + search_begin = match_end; + } else { + result->setArrayIndex(length++, c->newScriptVar(string(search_begin,str.end()))); + if(length>=limit) break; + } + } } static void scStringSubstr(const CFunctionsScopePtr &c, void *userdata) { - string str = c->getArgument("this")->getString(); + string str = this2string(c); int length = c->getArgumentsLength()-(int)userdata; - int start = c->getArgument("start")->getInt(); + int start = c->getArgument("start")->toNumber().toInt32(); if(start<0 || start>=(int)str.size()) c->setReturnVar(c->newScriptVar("")); else if(length>1) { - int length = c->getArgument("length")->getInt(); + int length = c->getArgument("length")->toNumber().toInt32(); c->setReturnVar(c->newScriptVar(str.substr(start, length))); } else c->setReturnVar(c->newScriptVar(str.substr(start))); } static void scStringToLowerCase(const CFunctionsScopePtr &c, void *) { - string str = c->getArgument("this")->getString(); + string str = this2string(c); transform(str.begin(), str.end(), str.begin(), ::tolower); c->setReturnVar(c->newScriptVar(str)); } static void scStringToUpperCase(const CFunctionsScopePtr &c, void *) { - string str = c->getArgument("this")->getString(); + string str = this2string(c); transform(str.begin(), str.end(), str.begin(), ::toupper); c->setReturnVar(c->newScriptVar(str)); } static void scStringTrim(const CFunctionsScopePtr &c, void *userdata) { - string str = c->getArgument("this")->getString(); + string str = this2string(c); string::size_type start = 0; string::size_type end = string::npos; if((((int)userdata) & 2) == 0) { @@ -161,7 +419,7 @@ static void scStringTrim(const CFunctionsScopePtr &c, void *userdata) { static void scCharToInt(const CFunctionsScopePtr &c, void *) { - string str = c->getArgument("ch")->getString();; + string str = c->getArgument("ch")->toString();; int val = 0; if (str.length()>0) val = (int)str.c_str()[0]; @@ -171,60 +429,107 @@ static void scCharToInt(const CFunctionsScopePtr &c, void *) { static void scStringFromCharCode(const CFunctionsScopePtr &c, void *) { char str[2]; - str[0] = c->getArgument("char")->getInt(); + str[0] = c->getArgument("char")->toNumber().toInt32(); str[1] = 0; c->setReturnVar(c->newScriptVar(str)); } +////////////////////////////////////////////////////////////////////////// +// RegExp-Stuff +////////////////////////////////////////////////////////////////////////// + +#ifndef NO_REGEXP + +static void scRegExpTest(const CFunctionsScopePtr &c, void *) { + CScriptVarRegExpPtr This = c->getArgument("this"); + if(This) + c->setReturnVar(This->exec(c->getArgument("str")->toString(), true)); + else + c->throwError(TypeError, "Object is not a RegExp-Object in test(str)"); +} +static void scRegExpExec(const CFunctionsScopePtr &c, void *) { + CScriptVarRegExpPtr This = c->getArgument("this"); + if(This) + c->setReturnVar(This->exec(c->getArgument("str")->toString())); + else + c->throwError(TypeError, "Object is not a RegExp-Object in exec(str)"); +} +#endif /* NO_REGEXP */ // ----------------------------------------------- Register Functions -void registerStringFunctions(CTinyJS *tinyJS) { +void registerStringFunctions(CTinyJS *tinyJS) {} +extern "C" void _registerStringFunctions(CTinyJS *tinyJS) { CScriptVarPtr fnc; // charAt - tinyJS->addNative("function String.prototype.charAt(pos)", scStringCharAt, 0); - tinyJS->addNative("function String.charAt(this,pos)", scStringCharAt, 0); + tinyJS->addNative("function String.prototype.charAt(pos)", scStringCharAt, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function String.charAt(this,pos)", scStringCharAt, 0, SCRIPTVARLINK_BUILDINDEFAULT); // charCodeAt - tinyJS->addNative("function String.prototype.charCodeAt(pos)", scStringCharCodeAt, 0); - tinyJS->addNative("function String.charCodeAt(this,pos)", scStringCharCodeAt, 0); + tinyJS->addNative("function String.prototype.charCodeAt(pos)", scStringCharCodeAt, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function String.charCodeAt(this,pos)", scStringCharCodeAt, 0, SCRIPTVARLINK_BUILDINDEFAULT); // concat - tinyJS->addNative("function String.prototype.concat()", scStringConcat, 0); - tinyJS->addNative("function String.concat(this)", scStringConcat, (void*)1); + tinyJS->addNative("function String.prototype.concat()", scStringConcat, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function String.concat(this)", scStringConcat, (void*)1, SCRIPTVARLINK_BUILDINDEFAULT); // indexOf - tinyJS->addNative("function String.prototype.indexOf(search)", scStringIndexOf, 0); // find the position of a string in a string, -1 if not - tinyJS->addNative("function String.indexOf(this,search)", scStringIndexOf, 0); // find the position of a string in a string, -1 if not + tinyJS->addNative("function String.prototype.indexOf(search,pos)", scStringIndexOf, 0, SCRIPTVARLINK_BUILDINDEFAULT); // find the position of a string in a string, -1 if not + tinyJS->addNative("function String.indexOf(this,search,pos)", scStringIndexOf, 0, SCRIPTVARLINK_BUILDINDEFAULT); // find the position of a string in a string, -1 if not // lastIndexOf - tinyJS->addNative("function String.prototype.lastIndexOf(search)", scStringIndexOf, (void*)-1); // find the last position of a string in a string, -1 if not - tinyJS->addNative("function String.lastIndexOf(this,search)", scStringIndexOf, (void*)-1); // find the last position of a string in a string, -1 if not + tinyJS->addNative("function String.prototype.lastIndexOf(search,pos)", scStringIndexOf, (void*)-1, SCRIPTVARLINK_BUILDINDEFAULT); // find the last position of a string in a string, -1 if not + tinyJS->addNative("function String.lastIndexOf(this,search,pos)", scStringIndexOf, (void*)-1, SCRIPTVARLINK_BUILDINDEFAULT); // find the last position of a string in a string, -1 if not + // localeCompare + tinyJS->addNative("function String.prototype.localeCompare(compareString)", scStringLocaleCompare, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function String.localeCompare(this,compareString)", scStringLocaleCompare, 0, SCRIPTVARLINK_BUILDINDEFAULT); + // quote + tinyJS->addNative("function String.prototype.quote()", scStringQuote, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function String.quote(this)", scStringQuote, 0, SCRIPTVARLINK_BUILDINDEFAULT); +#ifndef NO_REGEXP + // match + tinyJS->addNative("function String.prototype.match(regexp, flags)", scStringMatch, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function String.match(this, regexp, flags)", scStringMatch, 0, SCRIPTVARLINK_BUILDINDEFAULT); +#endif /* !REGEXP */ + // replace + tinyJS->addNative("function String.prototype.replace(substr, newsubstr, flags)", scStringReplace, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function String.replace(this, substr, newsubstr, flags)", scStringReplace, 0, SCRIPTVARLINK_BUILDINDEFAULT); + // search + tinyJS->addNative("function String.prototype.search(regexp, flags)", scStringSearch, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function String.search(this, regexp, flags)", scStringSearch, 0, SCRIPTVARLINK_BUILDINDEFAULT); // slice - tinyJS->addNative("function String.prototype.slice(start,end)", scStringSlice, 0); // find the last position of a string in a string, -1 if not - tinyJS->addNative("function String.slice(this,start,end)", scStringSlice, (void*)1); // find the last position of a string in a string, -1 if not + tinyJS->addNative("function String.prototype.slice(start,end)", scStringSlice, 0, SCRIPTVARLINK_BUILDINDEFAULT); // find the last position of a string in a string, -1 if not + tinyJS->addNative("function String.slice(this,start,end)", scStringSlice, (void*)1, SCRIPTVARLINK_BUILDINDEFAULT); // find the last position of a string in a string, -1 if not // split - tinyJS->addNative("function String.prototype.split(separator,limit)", scStringSplit, 0); - tinyJS->addNative("function String.split(this,separator,limit)", scStringSplit, 0); + tinyJS->addNative("function String.prototype.split(separator,limit)", scStringSplit, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function String.split(this,separator,limit)", scStringSplit, 0, SCRIPTVARLINK_BUILDINDEFAULT); // substr - tinyJS->addNative("function String.prototype.substr(start,length)", scStringSubstr, 0); - tinyJS->addNative("function String.substr(this,start,length)", scStringSubstr, (void*)1); + tinyJS->addNative("function String.prototype.substr(start,length)", scStringSubstr, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function String.substr(this,start,length)", scStringSubstr, (void*)1, SCRIPTVARLINK_BUILDINDEFAULT); // substring - tinyJS->addNative("function String.prototype.substring(start,end)", scStringSlice, (void*)2); - tinyJS->addNative("function String.substring(this,start,end)", scStringSlice, (void*)3); - // toLowerCase - tinyJS->addNative("function String.prototype.toLowerCase()", scStringToLowerCase, 0); - tinyJS->addNative("function String.toLowerCase(this)", scStringToLowerCase, 0); - // toUpperCase - tinyJS->addNative("function String.prototype.toUpperCase()", scStringToUpperCase, 0); - tinyJS->addNative("function String.toUpperCase(this)", scStringToUpperCase, 0); + tinyJS->addNative("function String.prototype.substring(start,end)", scStringSlice, (void*)2, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function String.substring(this,start,end)", scStringSlice, (void*)3, SCRIPTVARLINK_BUILDINDEFAULT); + // toLowerCase toLocaleLowerCase currently the same function + tinyJS->addNative("function String.prototype.toLowerCase()", scStringToLowerCase, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function String.toLowerCase(this)", scStringToLowerCase, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function String.prototype.toLocaleLowerCase()", scStringToLowerCase, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function String.toLocaleLowerCase(this)", scStringToLowerCase, 0, SCRIPTVARLINK_BUILDINDEFAULT); + // toUpperCase toLocaleUpperCase currently the same function + tinyJS->addNative("function String.prototype.toUpperCase()", scStringToUpperCase, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function String.toUpperCase(this)", scStringToUpperCase, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function String.prototype.toLocaleUpperCase()", scStringToUpperCase, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function String.toLocaleUpperCase(this)", scStringToUpperCase, 0, SCRIPTVARLINK_BUILDINDEFAULT); // trim - tinyJS->addNative("function String.prototype.trim()", scStringTrim, 0); - tinyJS->addNative("function String.trim(this)", scStringTrim, 0); + tinyJS->addNative("function String.prototype.trim()", scStringTrim, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function String.trim(this)", scStringTrim, 0, SCRIPTVARLINK_BUILDINDEFAULT); // trimLeft - tinyJS->addNative("function String.prototype.trimLeft()", scStringTrim, (void*)1); - tinyJS->addNative("function String.trimLeft(this)", scStringTrim, (void*)1); + tinyJS->addNative("function String.prototype.trimLeft()", scStringTrim, (void*)1, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function String.trimLeft(this)", scStringTrim, (void*)1, SCRIPTVARLINK_BUILDINDEFAULT); // trimRight - tinyJS->addNative("function String.prototype.trimRight()", scStringTrim, (void*)2); - tinyJS->addNative("function String.trimRight(this)", scStringTrim, (void*)2); + tinyJS->addNative("function String.prototype.trimRight()", scStringTrim, (void*)2, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function String.trimRight(this)", scStringTrim, (void*)2, SCRIPTVARLINK_BUILDINDEFAULT); - tinyJS->addNative("function charToInt(ch)", scCharToInt, 0); // convert a character to an int - get its value + tinyJS->addNative("function charToInt(ch)", scCharToInt, 0, SCRIPTVARLINK_BUILDINDEFAULT); // convert a character to an int - get its value - tinyJS->addNative("function String.prototype.fromCharCode(char)", scStringFromCharCode, 0); + tinyJS->addNative("function String.prototype.fromCharCode(char)", scStringFromCharCode, 0, SCRIPTVARLINK_BUILDINDEFAULT); +#ifndef NO_REGEXP + tinyJS->addNative("function RegExp.prototype.test(str)", scRegExpTest, 0, SCRIPTVARLINK_BUILDINDEFAULT); + tinyJS->addNative("function RegExp.prototype.exec(str)", scRegExpExec, 0, SCRIPTVARLINK_BUILDINDEFAULT); +#endif /* NO_REGEXP */ } diff --git a/TinyJS_StringFunctions.h b/TinyJS_StringFunctions.h index 8549d24..9db9728 100644 --- a/TinyJS_StringFunctions.h +++ b/TinyJS_StringFunctions.h @@ -5,7 +5,7 @@ * * Authored By Armin Diedering * - * Copyright (C) 2010 ardisoft + * Copyright (C) 2010-2012 ardisoft * * * Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -27,12 +27,14 @@ * SOFTWARE. */ +#pragma message("The include "__FILE__" is deprecated - StringFunctions now registered by default") + #ifndef TINYJS_STRINGFUNCTIONS_H #define TINYJS_STRINGFUNCTIONS_H #include "TinyJS.h" /// Register useful functions with the TinyJS interpreter -extern void registerStringFunctions(CTinyJS *tinyJS); +extern void DEPRECATED("is deprecated - StringFunctions now registered by default") registerStringFunctions(CTinyJS *tinyJS); #endif diff --git a/TinyJS_Threading.cpp b/TinyJS_Threading.cpp new file mode 100644 index 0000000..048711d --- /dev/null +++ b/TinyJS_Threading.cpp @@ -0,0 +1,88 @@ +#include "TinyJS_Threading.h" + +#undef HAVE_THREADING +#if !defined(NO_THREADING) && !defined(HAVE_CUSTOM_THREADING_IMPL) +# define HAVE_THREADING +# if defined(WIN32) && !defined(HAVE_PTHREAD) +# include +# else +# include +# endif +#endif + +#ifdef HAVE_THREADING + +#ifndef HAVE_PTHREAD +// simple pthreads 4 windows +# define pthread_t HANDLE +# define pthread_create(t, stack, fnc, a) *(t) = CreateThread(NULL, stack, (LPTHREAD_START_ROUTINE)fnc, a, 0, NULL) +# define pthread_join(t, v) WaitForSingleObject(t, INFINITE), GetExitCodeThread(t,(LPDWORD)v), CloseHandle(t) + +# define pthread_mutex_t HANDLE +# define pthread_mutex_init(m, a) *(m) = CreateMutex(NULL, false, NULL) +# define pthread_mutex_destroy(m) CloseHandle(*(m)); +# define pthread_mutex_lock(m) WaitForSingleObject(*(m), INFINITE); +# define pthread_mutex_unlock(m) ReleaseMutex(*(m)); +#endif + +class CScriptMutex_impl : public CScriptMutex::CScriptMutex_t { +public: + CScriptMutex_impl() { + pthread_mutex_init(&mutex, NULL); + } + ~CScriptMutex_impl() { + pthread_mutex_destroy(&mutex); + } + void lock() { + pthread_mutex_lock(&mutex); + } + void unlock() { + pthread_mutex_unlock(&mutex); + } + pthread_mutex_t mutex; +}; + +CScriptMutex::CScriptMutex() { + mutex = new CScriptMutex_impl; +} + +CScriptMutex::~CScriptMutex() { + delete mutex; +} + +class CScriptThread_impl : public CScriptThread::CScriptThread_t { +public: + CScriptThread_impl(CScriptThread *_this) : activ(false), running(false), This(_this) {} + ~CScriptThread_impl() {} + void Run() { + if(running) return; + activ = true; + pthread_create(&thread, NULL, (void*(*)(void*))ThreadFnc, this); + while(!running); + } + int Stop() { + if(!running) return -1; + void *retvar; + activ = false; + pthread_join(thread, &retvar); + running = false; + return (int)retvar; + } + bool isActiv() { return activ; } + static void *ThreadFnc(CScriptThread_impl *This) { + This->running = true; + return (void*) This->This->ThreadFnc(); + } + bool activ; + bool running; + CScriptThread *This; + pthread_t thread; +}; + +CScriptThread::CScriptThread() { + thread = new CScriptThread_impl(this); +} +CScriptThread::~CScriptThread() { + delete thread; +} +#endif // HAVE_THREADING \ No newline at end of file diff --git a/TinyJS_Threading.h b/TinyJS_Threading.h new file mode 100644 index 0000000..790c8fe --- /dev/null +++ b/TinyJS_Threading.h @@ -0,0 +1,70 @@ +#ifndef TinyJS_Threading_h__ +#define TinyJS_Threading_h__ +#include "config.h" +#ifndef NO_THREADING +/* + * 42TinyJS + * + * A fork of TinyJS with the goal to makes a more JavaScript/ECMA compliant engine + * + * Authored By Armin Diedering + * + * Copyright (C) 2010-2012 ardisoft + * + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +class CScriptMutex { +public: + CScriptMutex(); + ~CScriptMutex(); + void lock() { mutex->lock(); } + void unlock() { mutex->unlock(); } + class CScriptMutex_t{ + public: +// virtual ~CScriptMutex_t()=0; + virtual void lock()=0; + virtual void unlock()=0; + }; +private: + CScriptMutex_t *mutex; + +}; + +class CScriptThread { +public: + CScriptThread(); + virtual ~CScriptThread(); + void Run() { thread->Run(); } + int Stop() { return thread->Stop(); } + virtual int ThreadFnc()=0; + bool isActiv() { return thread->isActiv(); } + class CScriptThread_t{ + public: + virtual void Run()=0; + virtual int Stop()=0; + virtual bool isActiv()=0; + }; +private: + CScriptThread_t *thread; +}; + +#endif // NO_THREADING +#endif // TinyJS_Threading_h__ diff --git a/config.h b/config.h new file mode 100644 index 0000000..041a512 --- /dev/null +++ b/config.h @@ -0,0 +1,128 @@ +#ifndef _42TinyJS_config_h__ +#define _42TinyJS_config_h__ + +/* + * 42TinyJS + * + * A fork of TinyJS with the goal to makes a more JavaScript/ECMA compliant engine + * + * Authored By Armin Diedering + * + * Copyright (C) 2010-2012 ardisoft + * + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +////////////////////////////////////////////////////////////////////////// + +/* POOL-ALLOCATOR + * ============== + * To speed-up new & delete 42TinyJS adds an object-pool + * The pool is activated by default. + * To deactivate this stuff define NO_POOL_ALLOCATOR + */ +//#define NO_POOL_ALLOCATOR + +/* + * for debugging-stuff you can define DEBUG_POOL_ALLOCATOR + * if a memory-leak detected the allocator usage is printed to stderr + */ +//#define DEBUG_POOL_ALLOCATOR +/* + * with define LOG_POOL_ALLOCATOR_MEMORY_USAGE + * the allocator usage is always printed to stderr + */ +//#define LOG_POOL_ALLOCATOR_MEMORY_USAGE + +// NOTE: _DEBUG or LOG_POOL_ALLOCATOR_MEMORY_USAGE implies DEBUG_POOL_ALLOCATOR + +////////////////////////////////////////////////////////////////////////// + +/* REGEXP-SUPPORT + * ============== + * The RegExp-support needs boost-regex or TR1-regex + * To deactivate this stuff define NO_REGEXP + */ +//#define NO_REGEXP + +/* if NO_REGEXP not defined is included and std::regex is used + * you can define HAVE_BOOST_REGEX and is included and boost::regex is used + */ +//#define HAVE_BOOST_REGEX + +/* or you can define HAVE_TR1_REGEX and is included and std::tr1::regex is used + */ +//#define HAVE_TR1_REGEX + + +////////////////////////////////////////////////////////////////////////// + +/* LET-STUFF + * ========= + * Redeclaration of LET-vars is not allowed in block-scopes. + * But in the root- and functions-scopes it is currently allowed + * In future ECMAScript versions this will be also in root-and functions-scopes forbidden + * To enable the future behavior define PREVENT_REDECLARATION_IN_FUNCTION_SCOPES + */ +//#define PREVENT_REDECLARATION_IN_FUNCTION_SCOPES + + +////////////////////////////////////////////////////////////////////////// + +/* MULTI-THREADING + * =============== + * 42TinyJS is basically thread-save. + * You can run different or the same JS-code simultaneously in different instances of class TinyJS. + * The threading-stuff is currently only needed by the pool-allocator + * to deactivate threading define NO_THREADING + * NOTE: if NO_POOL_ALLOCATOR not defined you can not run JS-code simultaneously + * NO_POOL_ALLOCATOR implies NO_THREADING + */ +//#define NO_THREADING + +/* on Windows the windows-threading-API is used by default. + * on non-Windows (WIN32 is not defined) it is tried to use the POSIX pthread-API + * to force the pthread-API define HAVE_PTHREAD (windows needs in this case + * a pthread-lib e.g http://http://sourceware.org/pthreads-win32/) + */ +//#define HAVE_PTHREAD + +/* you can implement your own custom thread-implementation. + * to prevent the using of the win- or pthread-API define HAVE_CUSTOM_THREADING_IMPL + */ +//#define HAVE_CUSTOM_THREADING_IMPL + +//////////////////////////////////////////////// +// DO NOT MAKE CHANGES OF THE FOLLOWING STUFF // +//////////////////////////////////////////////// + +#if defined(NO_POOL_ALLOCATOR) && !defined(NO_THREADING) +# define NO_THREADING +#endif + +#if !defined(NO_POOL_ALLOCATOR) && defined(NO_THREADING) +#pragma message("\n***********************************************************************\n\ +* You have defined NO_THREADING and not defined NO_POOL_ALLOCATOR\n\ +* NOTE: you can not run JS-code simultaneously in different threads\n\ +***********************************************************************\n") +#endif + + +#endif // _42TinyJS_config_h__ diff --git a/lib-tiny-js.vcxproj b/lib-tiny-js.vcxproj index 80a5e7c..7223dad 100644 --- a/lib-tiny-js.vcxproj +++ b/lib-tiny-js.vcxproj @@ -44,6 +44,7 @@ Build\$(ProjectName)\$(Configuration)\ $(SolutionDir)$(Configuration)\ Build\$(ProjectName)\$(Configuration)\ + E:\Eigene Dateien\Visual Studio 2010\Projects\42tiny-js\pthread\pthreads-w32-2-9-1-release;$(IncludePath) @@ -73,15 +74,17 @@ + + - + diff --git a/lib-tiny-js.vcxproj.filters b/lib-tiny-js.vcxproj.filters index 745a842..ec27f62 100644 --- a/lib-tiny-js.vcxproj.filters +++ b/lib-tiny-js.vcxproj.filters @@ -30,6 +30,9 @@ Quelldateien + + Quelldateien + @@ -41,9 +44,6 @@ Headerdateien - - Headerdateien - Headerdateien @@ -53,5 +53,11 @@ Headerdateien + + Headerdateien + + + Headerdateien + \ No newline at end of file diff --git a/pool_allocator.cpp b/pool_allocator.cpp index b794ad3..92739c9 100644 --- a/pool_allocator.cpp +++ b/pool_allocator.cpp @@ -5,7 +5,7 @@ * * Authored By Armin Diedering * - * Copyright (C) 2010 ardisoft + * Copyright (C) 2010-2012 ardisoft * * * Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -206,15 +206,20 @@ static class _allocator_pool TimeLoggerCreate(alloc, false); TimeLoggerCreate(free, false); -fixed_size_allocator_lock *fixed_size_allocator::locker = 0; +#ifdef NO_THREADING +# define LOCK do{}while(0) +#else +CScriptMutex fixed_size_allocator::locker; class lock_help { public: - lock_help() { if(fixed_size_allocator::locker) fixed_size_allocator::locker->lock(); } - ~lock_help() { if(fixed_size_allocator::locker) fixed_size_allocator::locker->unlock(); } + lock_help() { fixed_size_allocator::locker.lock(); } + ~lock_help() { fixed_size_allocator::locker.unlock(); } }; +#define LOCK lock_help lock +#endif void* fixed_size_allocator::alloc(size_t size, const char *for_class) { TimeLoggerHelper(alloc); - lock_help lock; + LOCK; if(!allocator_pool.allocator_pool) { allocator_pool.allocator_pool = new allocator_pool_t(); allocator_pool.last_allocate_allocator = allocator_pool.last_free_allocator = 0; @@ -232,7 +237,7 @@ void* fixed_size_allocator::alloc(size_t size, const char *for_class) { } void fixed_size_allocator::free(void *p, size_t size) { TimeLoggerHelper(free); - lock_help lock; + LOCK; if(!allocator_pool.allocator_pool) { ASSERT(0/* free called but not allocator defined*/); return; diff --git a/pool_allocator.h b/pool_allocator.h index 8c839fb..4a16c0c 100644 --- a/pool_allocator.h +++ b/pool_allocator.h @@ -5,7 +5,7 @@ * * Authored By Armin Diedering * - * Copyright (C) 2010 ardisoft + * Copyright (C) 2010-2012 ardisoft * * * Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -34,7 +34,8 @@ #include #include #include - +#include "config.h" +#include "TinyJS_Threading.h" /************************************************************************ * TinyJS must many many times allocates and frees objects @@ -44,44 +45,9 @@ * from the heap. ************************************************************************/ -//#define LOG_POOL_ALLOCATOR_MEMORY_USAGE - -#if defined(_DEBUG) || defined(LOG_POOL_ALLOCATOR_MEMORY_USAGE) +#if !defined(DEBUG_POOL_ALLOCATOR) && (defined(_DEBUG) || defined(LOG_POOL_ALLOCATOR_MEMORY_USAGE)) # define DEBUG_POOL_ALLOCATOR #endif -/************************************************************************/ -/* M U L T I - T H R E A D I N G */ -/* The fixed_size_allocator is alone not thread-save. */ -/* For multi-threading you must make it explicit thread-save. */ -/* To do this, you must declare a class derived from "class */ -/* fixed_size_allocator_lock" and overload the member-methods */ -/* "lock" and "unlock" and assign a pointer of a instance of this class */ -/* to "fixed_size_allocator::locker" */ -/* */ -/* Example: */ -/* class my_fixed_size_lock : public fixed_size_allocator_lock { */ -/* public: */ -/* my_fixed_size_lock() { create_mutex(&mutex); } */ -/* ~my_fixed_size_lock() { delete_mutex(&mutex); } */ -/* virtual void lock() { lock_mutex(&mutex); } */ -/* virtual void unlock() { unlock_mutex(&mutex); } */ -/* private: */ -/* mutex_t mutex; */ -/* }; */ -/* */ -/* int main() { */ -/* my_fixed_size_lock lock; */ -/* fixed_size_allocator::locker = &lock; */ -/* .... */ -/* } */ -/* */ -/************************************************************************/ -class fixed_size_allocator_lock { -public: - virtual void lock()=0; - virtual void unlock()=0; -}; - struct block_head; class fixed_size_allocator { @@ -90,7 +56,9 @@ class fixed_size_allocator { static void *alloc(size_t,const char* for_class=0); static void free(void *, size_t); size_t objectSize() { return object_size; } - static fixed_size_allocator_lock *locker; +#ifndef NO_THREADING + static CScriptMutex locker; +#endif private: fixed_size_allocator(size_t num_objects, size_t object_size, const char* for_class); fixed_size_allocator(const fixed_size_allocator&); diff --git a/run_tests.cpp b/run_tests.cpp index ae28e69..19e4165 100644 --- a/run_tests.cpp +++ b/run_tests.cpp @@ -14,7 +14,7 @@ * * Authored / Changed By Armin Diedering * - * Copyright (C) 2010 ardisoft + * Copyright (C) 2010-2012 ardisoft * * * Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -55,9 +55,9 @@ #define _CRT_SECURE_NO_WARNINGS #include "TinyJS.h" -#include "TinyJS_Functions.h" -#include "TinyJS_MathFunctions.h" -#include "TinyJS_StringFunctions.h" +//#include "TinyJS_Functions.h" +//#include "TinyJS_MathFunctions.h" +//#include "TinyJS_StringFunctions.h" #include #include #include @@ -228,7 +228,9 @@ class end { // this is for VisualStudio debugging stuff. It's holds the console } bool active; } end; - +void js_print(const CFunctionsScopePtr &v, void *) { + printf("> %s\n", v->getArgument("text")->toString().c_str()); +} bool run_test(const char *filename) { printf("TEST %s ", filename); struct stat results; @@ -250,20 +252,22 @@ bool run_test(const char *filename) { fclose(file); CTinyJS s; - registerFunctions(&s); - registerMathFunctions(&s); - registerStringFunctions(&s); + s.addNative("function print(text)", &js_print, 0); + +// registerFunctions(&s); +// registerMathFunctions(&s); +// registerStringFunctions(&s); s.getRoot()->addChild("result", s.newScriptVar(0)); #ifdef WITH_TIME_LOGGER TimeLoggerCreate(Test, true, filename); #endif try { - s.execute(buffer); + s.execute(buffer, filename); } catch (CScriptException *e) { printf("%s\n", e->toString().c_str()); delete e; } - bool pass = (*s.getRoot()->findChild("result"))->getBool(); + bool pass = s.getRoot()->findChild("result")->toBoolean(); #ifdef WITH_TIME_LOGGER TimeLoggerLogprint(Test); #endif @@ -271,15 +275,18 @@ bool run_test(const char *filename) { if (pass) printf("PASS\n"); else { - char fn[64]; + char fn[64]; sprintf(fn, "%s.fail.txt", filename); + + /* logging is currently deactivated because stack-overflow by recursive vars FILE *f = fopen(fn, "wt"); if (f) { + std::string symbols = s.getRoot()->getParsableString("", " "); fprintf(f, "%s", symbols.c_str()); fclose(f); } - + */ printf("FAIL - symbols written to %s\n", fn); } @@ -294,8 +301,9 @@ int main(int argc, char **argv) #endif printf("TinyJS test runner\n"); printf("USAGE:\n"); - printf(" ./run_tests [-k] test.js [test2.js] : run tests\n"); + printf(" ./run_tests [-k] tests/test001.js [tests/42tests/test002.js] : run tests\n"); printf(" ./run_tests [-k] : run all tests\n"); + printf(" -k needs press enter at the end of runs\n"); int arg_num = 1; bool runs = false; for(; arg_numApplication true MultiByte + false + false Application false true MultiByte + false + false @@ -75,9 +79,6 @@ true - - - diff --git a/run_tests.vcxproj.filters b/run_tests.vcxproj.filters index c2dec68..8390eb7 100644 --- a/run_tests.vcxproj.filters +++ b/run_tests.vcxproj.filters @@ -14,9 +14,6 @@ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - Quelldateien diff --git a/time_logger.h b/time_logger.h index 0daaec8..9af7223 100644 --- a/time_logger.h +++ b/time_logger.h @@ -5,7 +5,7 @@ * * Authored By Armin Diedering * - * Copyright (C) 2010 ardisoft + * Copyright (C) 2010-2012 ardisoft * * * Permission is hereby granted, free of charge, to any person obtaining a copy of From 47a4f5a054966243e26f13d84d3ce8fd47777d45 Mon Sep 17 00:00:00 2001 From: armin Date: Sun, 19 Oct 2014 12:39:18 +0000 Subject: [PATCH 43/43] 42TinyJS: Milestone-3 for changelog visit https://code.google.com/p/42tiny-js/source/list Release 61-76 Main-Feature: Generator-Support (function with containing yield) see tests/42tests/test004.js --- Makefile | 6 +- Script.2012.vcxproj | 107 ++ Script.2012.vcxproj.filters | 22 + Script.cpp | 32 +- TinyJS.cpp | 2916 +++++++++++++++++------------- TinyJS.h | 633 +++++-- TinyJS_Functions.cpp | 11 +- TinyJS_Functions.h | 2 +- TinyJS_MathFunctions.cpp | 2 +- TinyJS_MathFunctions.h | 2 +- TinyJS_StringFunctions.cpp | 44 +- TinyJS_StringFunctions.h | 2 +- TinyJS_Threading.cpp | 196 +- TinyJS_Threading.h | 96 +- config.h | 59 +- lib-tiny-js.2012.vcxproj | 95 + lib-tiny-js.2012.vcxproj.filters | 63 + pool_allocator.cpp | 2 +- pool_allocator.h | 2 +- run_tests.2012.vcxproj | 96 + run_tests.2012.vcxproj.filters | 22 + run_tests.2012.vcxproj.user | 11 + run_tests.cpp | 18 +- run_tests.vcxproj.user | 11 + time_logger.h | 2 +- tiny-js.2012.sln | 34 + 26 files changed, 3059 insertions(+), 1427 deletions(-) create mode 100644 Script.2012.vcxproj create mode 100644 Script.2012.vcxproj.filters create mode 100644 lib-tiny-js.2012.vcxproj create mode 100644 lib-tiny-js.2012.vcxproj.filters create mode 100644 run_tests.2012.vcxproj create mode 100644 run_tests.2012.vcxproj.filters create mode 100644 run_tests.2012.vcxproj.user create mode 100644 run_tests.vcxproj.user create mode 100644 tiny-js.2012.sln diff --git a/Makefile b/Makefile index 9b88f45..d48f859 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,11 @@ LDFLAGS=-g -rdynamic SOURCES= \ TinyJS.cpp \ -TinyJS_Functions.cpp +pool_allocator.cpp \ +TinyJS_Functions.cpp \ +TinyJS_MathFunctions.cpp \ +TinyJS_StringFunctions.cpp \ +TinyJS_Threading.cpp OBJECTS=$(SOURCES:.cpp=.o) diff --git a/Script.2012.vcxproj b/Script.2012.vcxproj new file mode 100644 index 0000000..a804906 --- /dev/null +++ b/Script.2012.vcxproj @@ -0,0 +1,107 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {70D538BA-867B-4564-8DEE-F2C78C5AD4C8} + Script + Win32Proj + Script + + + + Application + false + false + MultiByte + true + v110 + + + Application + false + false + MultiByte + v110 + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + $(SolutionDir)$(Configuration)\ + $(ProjectName)\$(Configuration)\ + true + $(SolutionDir)$(Configuration)\ + $(ProjectName)\$(Configuration)\ + false + + + + Disabled + %(AdditionalIncludeDirectories) + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + + Level4 + EditAndContinue + + + true + Console + MachineX86 + false + + + + + MaxSpeed + true + %(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + + Level3 + ProgramDatabase + + + true + Console + true + true + MachineX86 + + + + + + + + {9751c465-0294-43cd-a5d9-bd038aba3961} + false + + + + + + \ No newline at end of file diff --git a/Script.2012.vcxproj.filters b/Script.2012.vcxproj.filters new file mode 100644 index 0000000..7a7dc59 --- /dev/null +++ b/Script.2012.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav + + + + + Quelldateien + + + \ No newline at end of file diff --git a/Script.cpp b/Script.cpp index 85e142e..a9d06d9 100755 --- a/Script.cpp +++ b/Script.cpp @@ -14,7 +14,7 @@ * * Authored / Changed By Armin Diedering * - * Copyright (C) 2010-2012 ardisoft + * Copyright (C) 2010-2014 ardisoft * * * Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -47,6 +47,7 @@ //#include "TinyJS_MathFunctions.h" #include #include +#include #ifdef _DEBUG # ifndef _MSC_VER @@ -68,8 +69,19 @@ void js_dump(const CFunctionsScopePtr &v, void *) { } +char *topOfStack; +#define sizeOfStack 1*1024*1024 /* for example 1 MB depend of Compiler-Options */ +#define sizeOfSafeStack 50*1024 /* safety area */ + int main(int , char **) { + char dummy; + topOfStack = &dummy; +// printf("%i %i\n", __cplusplus, _MSC_VER); + +// printf("Locale:%s\n",setlocale( LC_ALL, 0 )); +// setlocale( LC_ALL, ".858" ); +// printf("Locale:%s\n",setlocale( LC_ALL, 0 )); CTinyJS *js = new CTinyJS(); /* add the functions from TinyJS_Functions.cpp */ // registerFunctions(js); @@ -80,17 +92,31 @@ int main(int , char **) // js->addNative("function dump()", &js_dump, js); /* Execute out bit of code - we could call 'evaluate' here if we wanted something returned */ + js->setStackBase(topOfStack-(sizeOfStack-sizeOfSafeStack)); try { js->execute("var lets_quit = 0; function quit() { lets_quit = 1; }"); js->execute("print(\"Interactive mode... Type quit(); to exit, or print(...); to print something, or dump() to dump the symbol table!\");"); + js->execute("print(function () {print(\"gen\");yield 5;yield 6;}().next());", "yield-test.js"); + js->execute("for each(i in function () {print(\"gen\");yield 5;yield 6;}()) print(i);", "yield-test.js"); + js->execute("function g(){ \n\n" + " throw \"error\"\n" + " try{ \n" + " yield 1; yield 2 \n" + " }finally{ \n" + " print(\"finally\") \n" + " yield 3; \n" + " throw StopIteration \n" + " } \n" + " print(\"after finally\") \n" + "}t=g()", "test"); } catch (CScriptException *e) { printf("%s\n", e->toString().c_str()); delete e; } int lineNumber = 0; while (js->evaluate("lets_quit") == "0") { - char buffer[2048]; - fgets ( buffer, sizeof(buffer), stdin ); + std::string buffer; + if(!std::getline(std::cin, buffer)) break; try { js->execute(buffer, "console.input", lineNumber++); } catch (CScriptException *e) { diff --git a/TinyJS.cpp b/TinyJS.cpp index 55c4200..9cabbef 100755 --- a/TinyJS.cpp +++ b/TinyJS.cpp @@ -14,7 +14,7 @@ * * Authored / Changed By Armin Diedering * - * Copyright (C) 2010-2012 ardisoft + * Copyright (C) 2010-2014 ardisoft * * * Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -41,7 +41,9 @@ # define DEBUG_MEMORY 1 # endif #endif +#include #include +#include #include "TinyJS.h" @@ -59,6 +61,10 @@ # else # include # endif +#else +# include +# include +# include #endif using namespace std; @@ -291,6 +297,7 @@ void CScriptLex::getNextCh() { } currCh = nextCh; if ( (nextCh = *dataPos) != LEX_EOF ) dataPos++; // stay on EOF +// if(nextCh == -124) nextCh = '\n'; // if(currCh == '\r') { // Windows or Mac if(nextCh == '\n') getNextCh(); // Windows '\r\n\' --> skip '\r' @@ -299,7 +306,7 @@ void CScriptLex::getNextCh() { } } -static uint16_t not_allowed_tokens_befor_regexp[] = {LEX_ID, LEX_INT, LEX_FLOAT, LEX_STR, LEX_R_TRUE, LEX_R_FALSE, LEX_R_NULL, ']', ')', '.', LEX_EOF}; +static uint16_t not_allowed_tokens_befor_regexp[] = {LEX_ID, LEX_INT, LEX_FLOAT, LEX_STR, LEX_R_TRUE, LEX_R_FALSE, LEX_R_NULL, ']', ')', '.', LEX_PLUSPLUS, LEX_MINUSMINUS, LEX_EOF}; void CScriptLex::getNextToken() { while (currCh && isWhitespace(currCh)) getNextCh(); // newline comments @@ -329,6 +336,11 @@ void CScriptLex::getNextToken() { getNextCh(); } tk = CScriptToken::isReservedWord(tkStr); +#ifdef NO_GENERATORS + if(tk == LEX_R_YIELD) + throw new CScriptException(Error, "42TinyJS was built without support of generators (yield expression)", currentFile, pos.currentLine, currentColumn()); +#endif + } else if (isNumeric(currCh) || (currCh=='.' && isNumeric(nextCh))) { // Numbers if(currCh=='.') tkStr+='0'; bool isHex = false, isOct=false; @@ -506,7 +518,7 @@ void CScriptLex::getNextToken() { } if(tk == LEX_REGEXP) { #ifdef NO_REGEXP - throw new CScriptException(Error, "42TinyJS was built without support for regular expressions", currentFile, pos.currentLine, currentColumn()); + throw new CScriptException(Error, "42TinyJS was built without support of regular expressions", currentFile, pos.currentLine, currentColumn()); #endif tkStr = "/"; while (currCh && currCh!='/' && currCh!='\n') { @@ -550,8 +562,8 @@ bool CScriptTokenDataForwards::compare_fnc_token_by_name::operator()(const CScri return lhs.Fnc().name < rhs.Fnc().name; } bool CScriptTokenDataForwards::checkRedefinition(const string &Str, bool checkVarsInLetScope) { - STRING_SET_it it = lets.find(Str); - if(it!=lets.end()) return false; + STRING_SET_it it = varNames[LETS].find(Str); + if(it!=varNames[LETS].end()) return false; else if(checkVarsInLetScope) { STRING_SET_it it = vars_in_letscope.find(Str); if(it!=vars_in_letscope.end()) return false; @@ -560,7 +572,10 @@ bool CScriptTokenDataForwards::checkRedefinition(const string &Str, bool checkVa } void CScriptTokenDataForwards::addVars( STRING_VECTOR_t &Vars ) { - vars.insert(Vars.begin(), Vars.end()); + varNames[VARS].insert(Vars.begin(), Vars.end()); +} +void CScriptTokenDataForwards::addConsts( STRING_VECTOR_t &Vars ) { + varNames[CONSTS].insert(Vars.begin(), Vars.end()); } std::string CScriptTokenDataForwards::addVarsInLetscope( STRING_VECTOR_t &Vars ) { @@ -575,12 +590,53 @@ std::string CScriptTokenDataForwards::addLets( STRING_VECTOR_t &Lets ) { for(STRING_VECTOR_it it=Lets.begin(); it!=Lets.end(); ++it) { if(!checkRedefinition(*it, true)) return *it; - lets.insert(*it); + varNames[LETS].insert(*it); } return ""; } +////////////////////////////////////////////////////////////////////////// +// CScriptTokenDataLoop +////////////////////////////////////////////////////////////////////////// + +std::string CScriptTokenDataLoop::getParsableString(const string &IndentString/*=""*/, const string &Indent/*=""*/ ) { + static const char *heads[] = {"for each(", "for(", "for(", "for(", "while(", "do "}; + static const char *ops[] = {" in ", " in ", " of ", "; "}; + string out = heads[type]; + if(init.size() && type==FOR)out.append(CScriptToken::getParsableString(init)); + if(type<=WHILE) out.append(CScriptToken::getParsableString(condition.begin(), condition.end()-(type>=FOR ? 0 : 7))); + if(type<=FOR) out.append(ops[type]); + if(iter.size()) out.append(CScriptToken::getParsableString(iter)); + out.append(")"); + out.append(CScriptToken::getParsableString(body)); + if(type==DO)out.append(" while(").append(CScriptToken::getParsableString(condition)).append(");"); + return out; +} + + +////////////////////////////////////////////////////////////////////////// +// CScriptTokenDataTry +////////////////////////////////////////////////////////////////////////// + +std::string CScriptTokenDataTry::getParsableString( const string &IndentString/*=""*/, const string &Indent/*=""*/ ) { + string out = "try "; + string nl = Indent.size() ? "\n"+IndentString : " "; + + out.append(CScriptToken::getParsableString(tryBlock, IndentString, Indent)); + for(CScriptTokenDataTry::CatchBlock_it catchBlock = catchBlocks.begin(); catchBlock!=catchBlocks.end(); catchBlock++) { + out.append(nl).append("catch(").append(catchBlock->indentifiers->getParsableString()); + if(catchBlock->condition.size()>1) { + out.append(" if ").append(CScriptToken::getParsableString(catchBlock->condition.begin()+1, catchBlock->condition.end())); + } + out.append(") ").append(CScriptToken::getParsableString(catchBlock->block, IndentString, Indent)); + } + if(finallyBlock.size()) + out.append(nl).append("finally ").append(CScriptToken::getParsableString(finallyBlock, IndentString, Indent)); + return out; +} + + ////////////////////////////////////////////////////////////////////////// // CScriptTokenDataFnc ////////////////////////////////////////////////////////////////////////// @@ -718,6 +774,7 @@ static token2str_t reserved_words_begin[] ={ { LEX_R_RETURN, "return", true }, { LEX_R_VAR, "var", true }, { LEX_R_LET, "let", true }, + { LEX_R_CONST, "const", true }, { LEX_R_WITH, "with", true }, { LEX_R_TRUE, "true", true }, { LEX_R_FALSE, "false", true }, @@ -734,6 +791,7 @@ static token2str_t reserved_words_begin[] ={ { LEX_R_SWITCH, "switch", true }, { LEX_R_CASE, "case", true }, { LEX_R_DEFAULT, "default", true }, + { LEX_R_YIELD, "yield", true }, }; #define ARRAY_LENGTH(array) (sizeof(array)/sizeof(array[0])) #define ARRAY_END(array) (&array[ARRAY_LENGTH(array)]) @@ -772,16 +830,17 @@ static token2str_t tokens2str_begin[] = { { LEX_SLASHEQUAL, "/=", false }, { LEX_PERCENTEQUAL, "%=", false }, // special tokens - { LEX_T_FOR_IN, "for", true }, { LEX_T_OF, "of", true }, - { LEX_T_FOR_EACH_IN, "for each", true }, { LEX_T_FUNCTION_OPERATOR, "function", true }, { LEX_T_GET, "get", true }, { LEX_T_SET, "set", true }, - { LEX_T_SKIP, "LEX_SKIP", true }, + { LEX_T_EXCEPTION_VAR, "LEX_T_EXCEPTION_VAR", false }, + { LEX_T_SKIP, "LEX_SKIP", false }, { LEX_T_DUMMY_LABEL, "LABEL", true }, { LEX_T_LABEL, "LABEL", true }, - { LEX_T_LOOP_LABEL, "LEX_LOOP_LABEL", true }, + { LEX_T_LOOP, "LEX_LOOP", true }, + { LEX_T_FOR_IN, "LEX_FOR_IN", true }, + { LEX_T_FORWARD, "LEX_T_FORWARD", false }, { LEX_T_OBJECT_LITERAL, "LEX_OBJECT_LITERAL", false }, { LEX_T_DESTRUCTURING_VAR, "Destructuring Var", false }, }; @@ -825,10 +884,17 @@ CScriptToken::CScriptToken(CScriptLex *l, int Match, int Alternate) : line(l->cu (tokenData = new CScriptTokenDataString(l->tkStr))->ref(); else if(LEX_TOKEN_DATA_FUNCTION(token)) (tokenData = new CScriptTokenDataFnc)->ref(); + else if (LEX_TOKEN_DATA_LOOP(token)) + (tokenData = new CScriptTokenDataLoop)->ref(); + else if (LEX_TOKEN_DATA_TRY(token)) + (tokenData = new CScriptTokenDataTry)->ref(); if(Match>=0) l->match(Match, Alternate); else l->match(l->tk); +#ifdef _DEBUG + token_str = getTokenStr(token); +#endif } CScriptToken::CScriptToken(uint16_t Tk, int IntData) : line(0), column(0), token(Tk), intData(0) { if (LEX_TOKEN_DATA_SIMPLE(token)) @@ -839,20 +905,34 @@ CScriptToken::CScriptToken(uint16_t Tk, int IntData) : line(0), column(0), token (tokenData = new CScriptTokenDataDestructuringVar)->ref(); else if (LEX_TOKEN_DATA_OBJECT_LITERAL(token)) (tokenData = new CScriptTokenDataObjectLiteral)->ref(); + else if (LEX_TOKEN_DATA_LOOP(token)) + (tokenData = new CScriptTokenDataLoop)->ref(); + else if (LEX_TOKEN_DATA_TRY(token)) + (tokenData = new CScriptTokenDataTry)->ref(); else if (LEX_TOKEN_DATA_FORWARDER(token)) (tokenData = new CScriptTokenDataForwards)->ref(); else ASSERT(0); +#ifdef _DEBUG + token_str = getTokenStr(token); +#endif } CScriptToken::CScriptToken(uint16_t Tk, const string &TkStr) : line(0), column(0), token(Tk), intData(0) { ASSERT(LEX_TOKEN_DATA_STRING(token)); (tokenData = new CScriptTokenDataString(TkStr))->ref(); +#ifdef _DEBUG + token_str = getTokenStr(token); +#endif } CScriptToken &CScriptToken::operator =(const CScriptToken &Copy) { + if(this == &Copy) return *this; clear(); +#ifdef _DEBUG + token_str = Copy.token_str; +#endif line = Copy.line; column = Copy.column; token = Copy.token; @@ -880,9 +960,7 @@ string CScriptToken::getParsableString(TOKEN_VECT_it Begin, TOKEN_VECT_it End, c bool old_block_start = block_start; bool old_need_space = need_space; add_nl = block_start = need_space =false; - if(it->token == LEX_T_LOOP_LABEL) { - // ignore BLIND_LABLE - } else if(it->token == LEX_STR) + if(it->token == LEX_STR) OutString.append(getJSString(it->String())), need_space=true; else if(LEX_TOKEN_DATA_STRING(it->token)) OutString.append(it->String()), need_space=true; @@ -895,11 +973,14 @@ string CScriptToken::getParsableString(TOKEN_VECT_it Begin, TOKEN_VECT_it End, c if(it->Fnc().name.size() ) OutString.append(it->Fnc().name); OutString.append(it->Fnc().getArgumentsString()); - OutString.append(getParsableString(it->Fnc().body, IndentString, Indent)); + OutString.append(getParsableString(it->Fnc().body, my_indentString, Indent)); if(it->Fnc().body.front().token != '{') { OutString.append(";"); -// token; } + } else if(LEX_TOKEN_DATA_LOOP(it->token)) { + OutString.append(it->Loop().getParsableString(my_indentString, Indent)); + } else if(LEX_TOKEN_DATA_TRY(it->token)) { + OutString.append(it->Try().getParsableString(my_indentString, Indent)); } else if(LEX_TOKEN_DATA_DESTRUCTURING_VAR(it->token)) { OutString.append(it->DestructuringVar().getParsableString()); } else if(LEX_TOKEN_DATA_OBJECT_LITERAL(it->token)) { @@ -1006,19 +1087,18 @@ void CScriptTokenizer::tokenizeCode(CScriptLex &Lexer) { l=&Lexer; tokens.clear(); tokenScopeStack.clear(); - vector blockStart(1, 0), marks; - pushForwarder(tokens, blockStart); - STRING_VECTOR_t labels, loopLabels; + ScriptTokenState state; + pushForwarder(state); if(l->tk == '�') { // special-Token at Start means the code begins not at Statement-Level l->match('�'); - int State=0; - tokenizeLiteral(tokens, blockStart, marks, labels, loopLabels, 0, State); + tokenizeLiteral(state, 0); } else do { - tokenizeStatement(tokens, blockStart, marks, labels, loopLabels, 0); + tokenizeStatement(state, 0); } while (l->tk!=LEX_EOF); - pushToken(tokens, LEX_EOF); // add LEX_EOF-Token - removeEmptyForwarder(tokens, blockStart, marks); - TOKEN_VECT(tokens).swap(tokens);// tokens.shrink_to_fit(); + pushToken(state.Tokens, LEX_EOF); // add LEX_EOF-Token + removeEmptyForwarder(state); +// TOKEN_VECT(tokens).swap(tokens);// tokens.shrink_to_fit(); + tokens.swap(state.Tokens); pushTokenScope(tokens); currentFile = l->currentFile; tk = getToken().token; @@ -1072,20 +1152,15 @@ void CScriptTokenizer::setPos(ScriptTokenPosition &TokenPos) { tk = getToken().token; } void CScriptTokenizer::skip(int Tokens) { - tokenScopeStack.back().pos+=Tokens; - tk = getToken().token; + ASSERT(tokenScopeStack.back().tokens->end()-tokenScopeStack.back().pos-Tokens>=0); + tokenScopeStack.back().pos+=Tokens-1; + getNextToken(); } -static inline void setTokenSkip(TOKEN_VECT &Tokens, vector &Marks) { - int tokenBeginIdx = Marks.back(); - Marks.pop_back(); - Tokens[tokenBeginIdx].Int() = Tokens.size()-tokenBeginIdx; -} -static void fix_BlockStarts_Marks(vector &BlockStart, vector &Marks, int start, int diff) { - for(vector::iterator it = BlockStart.begin(); it != BlockStart.end(); ++it) - if(*it >= start) *it += diff; - for(vector::iterator it = Marks.begin(); it != Marks.end(); ++it) - if(*it >= start) *it += diff; +static inline void setTokenSkip(CScriptTokenizer::ScriptTokenState &State) { + int tokenBeginIdx = State.Marks.back(); + State.Marks.pop_back(); + State.Tokens[tokenBeginIdx].Int() = State.Tokens.size()-tokenBeginIdx; } enum { @@ -1093,352 +1168,347 @@ enum { TOKENIZE_FLAGS_canBreak = 1<<1, TOKENIZE_FLAGS_canContinue = 1<<2, TOKENIZE_FLAGS_canReturn = 1<<3, - TOKENIZE_FLAGS_asStatement = 1<<4, - TOKENIZE_FLAGS_forFor = 1<<5, - TOKENIZE_FLAGS_isAccessor = 1<<6, - TOKENIZE_FLAGS_callForNew = 1<<7, - TOKENIZE_FLAGS_noBlockStart = 1<<8, - TOKENIZE_FLAGS_nestedObject = 1<<9, -}; -enum { - TOKENIZE_STATE_leftHand = 1<<0, - TOKENIZE_STATE_Destructuring = 1<<1, + TOKENIZE_FLAGS_canYield = 1<<4, + TOKENIZE_FLAGS_asStatement = 1<<5, + TOKENIZE_FLAGS_noIn = 1<<6, + TOKENIZE_FLAGS_isAccessor = 1<<7, + TOKENIZE_FLAGS_callForNew = 1<<8, + TOKENIZE_FLAGS_noBlockStart = 1<<9, + TOKENIZE_FLAGS_nestedObject = 1<<10, }; -void CScriptTokenizer::tokenizeCatch(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { - Marks.push_back(pushToken(Tokens, LEX_R_CATCH)); // push Token & push tokenBeginIdx - pushToken(Tokens, '('); - pushToken(Tokens, LEX_ID); - if(l->tk == LEX_R_IF) { - pushToken(Tokens); - tokenizeExpression(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); - } - pushToken(Tokens, ')'); - tokenizeBlock(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); - setTokenSkip(Tokens, Marks); -} -void CScriptTokenizer::tokenizeTry(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { - bool isTry = l->tk == LEX_R_TRY; - Marks.push_back(pushToken(Tokens)); // push Token & push tokenBeginIdx - - // inject LEX_T_LOOP_LABEL - if(isTry && Tokens.size()>=3) { - int label_count = 0; - for(TOKEN_VECT::reverse_iterator it = Tokens.rbegin()+1; it!=Tokens.rend(); ++it) { - if(it->token == ':' && (++it)->token == LEX_T_LABEL) { - ++label_count; - it->token = LEX_T_DUMMY_LABEL; - } else break; - } - for(int i=0; imatch(LEX_R_TRY); + CScriptToken TryToken(LEX_T_TRY); + CScriptTokenDataTry &TryData = TryToken.Try(); + pushToken(State.Tokens, TryToken); + + TOKEN_VECT mainTokens; + State.Tokens.swap(mainTokens); + + // try-block + tokenizeBlock(State, Flags); + State.Tokens.swap(TryData.tryBlock); + + // catch-blocks + l->check(LEX_R_CATCH, LEX_R_FINALLY); + bool unconditionalCatch = false; + while(l->tk == LEX_R_CATCH) { + if(unconditionalCatch) throw new CScriptException(SyntaxError, "catch after unconditional catch", l->currentFile, l->currentLine(), l->currentColumn()); + + // vars & condition + l->match(LEX_R_CATCH); + l->match('('); + TryData.catchBlocks.resize(TryData.catchBlocks.size()+1); + CScriptTokenDataTry::CatchBlock &catchBlock = TryData.catchBlocks.back(); + pushForwarder(State, true); + STRING_VECTOR_t vars; + catchBlock.indentifiers = tokenizeVarIdentifier(&vars).DestructuringVar(); + State.Forwarders.back()->addLets(vars); + if(l->tk == LEX_R_IF) { + l->match(LEX_R_IF); + tokenizeExpression(State, Flags); + } else + unconditionalCatch = true; + State.Tokens.swap(catchBlock.condition); + l->match(')'); - if(l->tk != LEX_R_FINALLY && isTry) { - l->check(LEX_R_CATCH, LEX_R_FINALLY); - while(l->tk == LEX_R_CATCH && isTry) - tokenizeCatch(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + // catch-block + tokenizeBlock(State, Flags | TOKENIZE_FLAGS_noBlockStart); + State.Tokens.swap(catchBlock.block); + State.Forwarders.pop_back(); } - if(l->tk == LEX_R_FINALLY && isTry) - tokenizeTry(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + // finally-block + if(l->tk == LEX_R_FINALLY) { + l->match(LEX_R_FINALLY); + tokenizeBlock(State, Flags); + State.Tokens.swap(TryData.finallyBlock); + } + State.Tokens.swap(mainTokens); } -void CScriptTokenizer::tokenizeSwitch(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { +void CScriptTokenizer::tokenizeSwitch(ScriptTokenState &State, int Flags) { - Marks.push_back(pushToken(Tokens)); // push Token & push tokenBeginIdx - pushToken(Tokens, '('); - tokenizeExpression(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); - pushToken(Tokens, ')'); + State.Marks.push_back(pushToken(State.Tokens)); // push Token & push tokenBeginIdx + pushToken(State.Tokens, '('); + tokenizeExpression(State, Flags); + pushToken(State.Tokens, ')'); - Marks.push_back(pushToken(Tokens, '{')); // push Token & push blockBeginIdx - pushForwarder(Tokens, BlockStart); + State.Marks.push_back(pushToken(State.Tokens, '{')); // push Token & push blockBeginIdx + pushForwarder(State); - vector::size_type MarksSize = Marks.size(); + vector::size_type MarksSize = State.Marks.size(); Flags |= TOKENIZE_FLAGS_canBreak; for(bool hasDefault=false;;) { if( l->tk == LEX_R_CASE || l->tk == LEX_R_DEFAULT) { if(l->tk == LEX_R_CASE) { - Marks.push_back(pushToken(Tokens)); // push Token & push caseBeginIdx - Marks.push_back(pushToken(Tokens,CScriptToken(LEX_T_SKIP))); // skipper to skip case-expression - tokenizeExpression(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); - setTokenSkip(Tokens, Marks); + State.Marks.push_back(pushToken(State.Tokens)); // push Token & push caseBeginIdx + State.Marks.push_back(pushToken(State.Tokens,CScriptToken(LEX_T_SKIP))); // skipper to skip case-expression + tokenizeExpression(State, Flags); + setTokenSkip(State); } else { // default - Marks.push_back(pushToken(Tokens)); // push Token & push caseBeginIdx + State.Marks.push_back(pushToken(State.Tokens)); // push Token & push caseBeginIdx if(hasDefault) throw new CScriptException(SyntaxError, "more than one switch default", l->currentFile, l->currentLine(), l->currentColumn()); hasDefault = true; } - Marks.push_back(pushToken(Tokens, ':')); + State.Marks.push_back(pushToken(State.Tokens, ':')); while(l->tk != '}' && l->tk != LEX_R_CASE && l->tk != LEX_R_DEFAULT && l->tk != LEX_EOF ) - tokenizeStatement(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); - setTokenSkip(Tokens, Marks); + tokenizeStatement(State, Flags); + setTokenSkip(State); } else if(l->tk == '}') break; else throw new CScriptException(SyntaxError, "invalid switch statement", l->currentFile, l->currentLine(), l->currentColumn()); } - while(MarksSize < Marks.size()) setTokenSkip(Tokens, Marks); - removeEmptyForwarder(Tokens, BlockStart, Marks); // remove Forwarder if empty - pushToken(Tokens, '}'); - setTokenSkip(Tokens, Marks); // switch-block - setTokenSkip(Tokens, Marks); // switch-statement + while(MarksSize < State.Marks.size()) setTokenSkip(State); + removeEmptyForwarder(State); // remove Forwarder if empty + pushToken(State.Tokens, '}'); + setTokenSkip(State); // switch-block + setTokenSkip(State); // switch-statement } -void CScriptTokenizer::tokenizeWith(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { - - Marks.push_back(pushToken(Tokens)); // push Token & push tokenBeginIdx +void CScriptTokenizer::tokenizeWith(ScriptTokenState &State, int Flags) { + State.Marks.push_back(pushToken(State.Tokens)); // push Token & push tokenBeginIdx - pushToken(Tokens, '('); - tokenizeExpression(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); - pushToken(Tokens, ')'); + pushToken(State.Tokens, '('); + tokenizeExpression(State, Flags); + pushToken(State.Tokens, ')'); + tokenizeStatementNoLet(State, Flags); - BlockStart.push_back(Tokens.size()); // set a blockStart - tokenizeStatement(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); - BlockStart.pop_back(); - - setTokenSkip(Tokens, Marks); + setTokenSkip(State); } -static inline int PushLoopLabels(TOKEN_VECT &Tokens, STRING_VECTOR_t *LoopLabels=0) { - int label_count = 0; - if(Tokens.size()>=3) { - for(TOKEN_VECT::reverse_iterator it = Tokens.rbegin()+1; it!=Tokens.rend(); ++it) { +static inline uint32_t GetLoopLabels(CScriptTokenizer::ScriptTokenState &State, CScriptTokenDataLoop &LoopData) { + uint32_t label_count = 0; + if(State.Tokens.size()>=2) { + for(TOKEN_VECT::reverse_iterator it = State.Tokens.rbegin(); it!=State.Tokens.rend(); ++it) { if(it->token == ':' && (++it)->token == LEX_T_LABEL) { ++label_count; - if(LoopLabels) LoopLabels->push_back(it->String()); + LoopData.labels.push_back(it->String()); + State.LoopLabels.push_back(it->String()); it->token = LEX_T_DUMMY_LABEL; - } else break; + } else + break; } - for(int i=0; irbegin()+i))); } return label_count; } -static inline void PopLoopLabels(int label_count, STRING_VECTOR_t &LoopLabels) { - while(label_count--) - LoopLabels.pop_back(); +static inline void PopLoopLabels(uint32_t label_count, STRING_VECTOR_t &LoopLabels) { + ASSERT(label_count <= LoopLabels.size()); + LoopLabels.resize(LoopLabels.size()-label_count); } -void CScriptTokenizer::tokenizeWhile(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { - Marks.push_back(pushToken(Tokens)); // push Token & push tokenBeginIdx - - // inject & push LEX_T_LOOP_LABEL - int label_count = PushLoopLabels(Tokens, &LoopLabels); - - pushToken(Tokens, '('); - tokenizeExpression(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); - pushToken(Tokens, ')'); - - Marks.push_back(Tokens.size()); // push skiperBeginIdx - Tokens.push_back(CScriptToken(LEX_T_SKIP)); // skip - - BlockStart.push_back(Tokens.size()); // set a blockStart - tokenizeStatement(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags | TOKENIZE_FLAGS_canBreak | TOKENIZE_FLAGS_canContinue); - BlockStart.pop_back(); - - setTokenSkip(Tokens, Marks); // statement - - // pop LEX_T_LOOP_LABEL - PopLoopLabels(label_count, LoopLabels); +void CScriptTokenizer::tokenizeWhileAndDo(ScriptTokenState &State, int Flags) { + + bool do_while = l->tk==LEX_R_DO; - setTokenSkip(Tokens, Marks); // while -} -void CScriptTokenizer::tokenizeDo(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { - Marks.push_back(pushToken(Tokens)); // push Token & push tokenBeginIdx + CScriptToken LoopToken(LEX_T_LOOP); + CScriptTokenDataLoop &LoopData = LoopToken.Loop(); + LoopData.type = do_while ? CScriptTokenDataLoop::DO : CScriptTokenDataLoop::WHILE; - // inject & push LEX_T_LOOP_LABEL - int label_count = PushLoopLabels(Tokens, &LoopLabels); + // get loop-labels + uint32_t label_count = GetLoopLabels(State, LoopData); - BlockStart.push_back(Tokens.size()); // set a blockStart - tokenizeStatement(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags | TOKENIZE_FLAGS_canBreak | TOKENIZE_FLAGS_canContinue); - BlockStart.pop_back(); - Marks.push_back(pushToken(Tokens, LEX_R_WHILE)); // push Token & push tokenBeginIdx - pushToken(Tokens, '('); - tokenizeExpression(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); - pushToken(Tokens, ')'); - pushToken(Tokens, ';'); - setTokenSkip(Tokens, Marks); + l->match(l->tk); // match while or do - // pop LEX_T_LOOP_LABEL - PopLoopLabels(label_count, LoopLabels); + pushToken(State.Tokens, LoopToken); + + TOKEN_VECT mainTokens; + State.Tokens.swap(mainTokens); - setTokenSkip(Tokens, Marks); + if(!do_while) { + l->match('('); + tokenizeExpression(State, Flags); + State.Tokens.swap(LoopData.condition); + l->match(')'); + } + tokenizeStatementNoLet(State, Flags | TOKENIZE_FLAGS_canBreak | TOKENIZE_FLAGS_canContinue); + State.Tokens.swap(LoopData.body); + if(do_while) { + l->match(LEX_R_WHILE); + l->match('('); + tokenizeExpression(State, Flags); + State.Tokens.swap(LoopData.condition); + l->match(')'); + l->match(';'); + } + State.Tokens.swap(mainTokens); + PopLoopLabels(label_count, State.LoopLabels); } -void CScriptTokenizer::tokenizeIf(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { - - Marks.push_back(pushToken(Tokens)); // push Token & push tokenBeginIdx +void CScriptTokenizer::tokenizeIf(ScriptTokenState &State, int Flags) { - pushToken(Tokens, '('); - tokenizeExpression(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); - pushToken(Tokens, ')'); + State.Marks.push_back(pushToken(State.Tokens)); // push Token & push tokenBeginIdx - Marks.push_back(Tokens.size()); // push skiperBeginIdx - Tokens.push_back(CScriptToken(LEX_T_SKIP)); // skip + pushToken(State.Tokens, '('); + tokenizeExpression(State, Flags); + pushToken(State.Tokens, ')'); + State.Marks.push_back(pushToken(State.Tokens, CScriptToken(LEX_T_SKIP))); // push skip & skiperBeginIdx + tokenizeStatementNoLet(State, Flags); - BlockStart.push_back(Tokens.size()); // set a blockStart - tokenizeStatement(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); - BlockStart.pop_back(); - - setTokenSkip(Tokens, Marks); + setTokenSkip(State); if(l->tk == LEX_R_ELSE) { - Marks.push_back(pushToken(Tokens)); // push Token & push tokenBeginIdx - - BlockStart.push_back(Tokens.size()); // set a blockStart - tokenizeStatement(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); - BlockStart.pop_back(); - - setTokenSkip(Tokens, Marks); + State.Marks.push_back(pushToken(State.Tokens)); // push Token & push tokenBeginIdx + tokenizeStatementNoLet(State, Flags); + setTokenSkip(State); } - setTokenSkip(Tokens, Marks); + setTokenSkip(State); } -void CScriptTokenizer::tokenizeFor(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { +void CScriptTokenizer::tokenizeFor(ScriptTokenState &State, int Flags) { + bool for_in=false, for_of=false, for_each_in=false; + CScriptToken LoopToken(LEX_T_LOOP); + CScriptTokenDataLoop &LoopData = LoopToken.Loop(); + + // get loop-labels + uint32_t label_count = GetLoopLabels(State, LoopData); - CScriptLex::POS prev_pos = l->pos; - bool for_in, for_of, for_each_in; l->match(LEX_R_FOR); - if((for_in = for_each_in = (l->tk == LEX_ID && l->tkStr == "each"))) { + if((for_in = for_each_in = (l->tk == LEX_ID && l->tkStr == "each"))) l->match(LEX_ID); // match "each" - } - if(!for_in) { - l->match('('); + + pushToken(State.Tokens, LoopToken); + + l->match('('); + TOKEN_VECT mainTokens; + State.Tokens.swap(mainTokens); + + bool haveLetScope = false; + + if(l->tk == LEX_R_VAR || l->tk == LEX_R_LET) { if(l->tk == LEX_R_VAR) - l->match(LEX_R_VAR); - else if(l->tk == LEX_R_LET) - l->match(LEX_R_LET); - if(l->tk == LEX_ID) { - l->match(LEX_ID); - if(l->tk == LEX_R_IN) - for_in = true; - else if(l->tk == LEX_ID && l->tkStr == "of") - for_in = for_of = true; + tokenizeVarNoConst(State, Flags | TOKENIZE_FLAGS_noIn); + else { //if(l->tk == LEX_R_LET) + haveLetScope = true; + pushForwarder(State, true); // no clean up empty tokenizer + tokenizeLet(State, Flags | TOKENIZE_FLAGS_noIn | TOKENIZE_FLAGS_asStatement); } - } - l->reset(prev_pos); - - Marks.push_back(pushToken(Tokens)); // push Token & push tokenBeginIdx - if(for_in) Tokens[Tokens.size()-1].token = for_each_in?LEX_T_FOR_EACH_IN:LEX_T_FOR_IN; - if(for_each_in) l->match(LEX_ID); // match "each" + } else if(l->tk!=';') { + tokenizeExpression(State, Flags | TOKENIZE_FLAGS_noIn); + } + if((for_in=(l->tk==LEX_R_IN || (l->tk==LEX_ID && l->tkStr=="of")))) { + if(!State.LeftHand) + throw new CScriptException(ReferenceError, "invalid for/in left-hand side", l->currentFile, l->currentLine(), l->currentColumn()); + if(l->tk==LEX_ID && l->tkStr=="of") l->tk = LEX_T_OF; // fake token + if((for_of = (!for_each_in && l->tk==LEX_T_OF))) { + l->match(LEX_T_OF); + LoopData.type = CScriptTokenDataLoop::FOR_OF; + } else { + l->match(LEX_R_IN); + LoopData.type = for_each_in ? CScriptTokenDataLoop::FOR_EACH : CScriptTokenDataLoop::FOR_IN; + } + State.Tokens.swap(LoopData.condition); - // inject & push LEX_T_LOOP_LABEL - int label_count = PushLoopLabels(Tokens, &LoopLabels); - - pushToken(Tokens, '('); - pushForwarder(Tokens, BlockStart); - if(for_in) { - if(l->tk == LEX_R_VAR) { - pushToken(Tokens, LEX_R_VAR); - Tokens[BlockStart.front()].Forwarder().vars.insert(l->tkStr); - } else if(l->tk == LEX_R_LET) { - pushToken(Tokens, LEX_R_LET); - Tokens[BlockStart.back()].Forwarder().lets.insert(l->tkStr); + if(LoopData.condition.front().token == LEX_T_FORWARD) { + LoopData.init.push_back(LoopData.condition.front()); + LoopData.condition.erase(LoopData.condition.begin()); } - pushToken(Tokens, LEX_ID); - if(for_of) l->tk = LEX_T_OF; // fake token - pushToken(Tokens, LEX_R_IN, LEX_T_OF); + mainTokens.back().token = LEX_T_FOR_IN; } else { - if(l->tk == LEX_R_VAR) - tokenizeVar(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags | TOKENIZE_FLAGS_forFor); - else if(l->tk == LEX_R_LET) - tokenizeLet(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags | TOKENIZE_FLAGS_forFor | TOKENIZE_FLAGS_asStatement); - else if(l->tk != ';') - tokenizeExpression(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); l->check(';'); // no automatic ;-injection - pushToken(Tokens, ';'); - if(l->tk != ';') tokenizeExpression(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + pushToken(State.Tokens, ';'); + State.Tokens.swap(LoopData.init); + if(l->tk != ';') tokenizeExpression(State, Flags); l->check(';'); // no automatic ;-injection - pushToken(Tokens, ';'); + l->match(';'); // no automatic ;-injection + State.Tokens.swap(LoopData.condition); } - if(for_in || l->tk != ')') tokenizeExpression(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); - pushToken(Tokens, ')'); - - BlockStart.push_back(Tokens.size()); // set a blockStart - tokenizeStatement(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags | TOKENIZE_FLAGS_canBreak | TOKENIZE_FLAGS_canContinue); - BlockStart.pop_back(); - - removeEmptyForwarder(Tokens, BlockStart, Marks); - - // pop LEX_T_LOOP_LABEL - PopLoopLabels(label_count, LoopLabels); - setTokenSkip(Tokens, Marks); -} + if(for_in || l->tk != ')') tokenizeExpression(State, Flags); + l->match(')'); + State.Tokens.swap(LoopData.iter); + Flags = (Flags & (TOKENIZE_FLAGS_canReturn | TOKENIZE_FLAGS_canYield)) | TOKENIZE_FLAGS_canBreak | TOKENIZE_FLAGS_canContinue; + if(haveLetScope) Flags |= TOKENIZE_FLAGS_noBlockStart; + tokenizeStatementNoLet(State, Flags); + if(haveLetScope) State.Forwarders.pop_back(); -void CScriptTokenizer::_tokenizeDeconstructionVarObject(DESTRUCTURING_VARS_t &Vars, STRING_VECTOR_t &VarNames) { - l->match('{'); - while(l->tk != '}') { - CScriptLex::POS prev_pos = l->pos; - string Path = l->tkStr; - l->match(LEX_ID, LEX_STR); - if(l->tk == ':') { - l->match(':'); - _tokenizeDestructionVar(Vars, Path, VarNames); + State.Tokens.swap(LoopData.body); + State.Tokens.swap(mainTokens); + if(for_in) { + LoopData.condition.push_back('='); + LoopData.condition.push_back(LEX_T_EXCEPTION_VAR); + LoopData.condition.push_back('.'); + LoopData.condition.push_back(CScriptToken(LEX_ID, "next")); + LoopData.condition.push_back('('); + LoopData.condition.push_back(')'); + LoopData.condition.push_back(';'); + } + PopLoopLabels(label_count, State.LoopLabels); +} + +static void tokenizeVarIdentifierDestructuring( CScriptLex *Lexer, DESTRUCTURING_VARS_t &Vars, const std::string &Path, STRING_VECTOR_t *VarNames ); +static void tokenizeVarIdentifierDestructuringObject(CScriptLex *Lexer, DESTRUCTURING_VARS_t &Vars, STRING_VECTOR_t *VarNames) { + Lexer->match('{'); + while(Lexer->tk != '}') { + CScriptLex::POS prev_pos = Lexer->pos; + string Path = Lexer->tkStr; + Lexer->match(LEX_ID, LEX_STR); + if(Lexer->tk == ':') { + Lexer->match(':'); + tokenizeVarIdentifierDestructuring(Lexer, Vars, Path, VarNames); } else { - l->reset(prev_pos); - VarNames.push_back(l->tkStr); - Vars.push_back(DESTRUCTURING_VAR_t(l->tkStr, l->tkStr)); - l->match(LEX_ID); + Lexer->reset(prev_pos); + if(VarNames) VarNames->push_back(Lexer->tkStr); + Vars.push_back(DESTRUCTURING_VAR_t(Lexer->tkStr, Lexer->tkStr)); + Lexer->match(LEX_ID); } - if (l->tk!='}') l->match(',', '}'); + if (Lexer->tk!='}') Lexer->match(',', '}'); } - l->match('}'); + Lexer->match('}'); } -void CScriptTokenizer::_tokenizeDeconstructionVarArray(DESTRUCTURING_VARS_t &Vars, STRING_VECTOR_t &VarNames) { +static void tokenizeVarIdentifierDestructuringArray(CScriptLex *Lexer, DESTRUCTURING_VARS_t &Vars, STRING_VECTOR_t *VarNames) { int idx = 0; - l->match('['); - while(l->tk != ']') { - if(l->tk == ',') + Lexer->match('['); + while(Lexer->tk != ']') { + if(Lexer->tk == ',') Vars.push_back(DESTRUCTURING_VAR_t("", "")); // empty else - _tokenizeDestructionVar(Vars, int2string(idx), VarNames); + tokenizeVarIdentifierDestructuring(Lexer, Vars, int2string(idx), VarNames); ++idx; - if (l->tk!=']') l->match(',',']'); + if (Lexer->tk!=']') Lexer->match(',',']'); } - l->match(']'); + Lexer->match(']'); } -void CScriptTokenizer::_tokenizeDestructionVar(DESTRUCTURING_VARS_t &Vars, const string &Path, STRING_VECTOR_t &VarNames) { - if(l->tk == '[') { +static void tokenizeVarIdentifierDestructuring(CScriptLex *Lexer, DESTRUCTURING_VARS_t &Vars, const std::string &Path, STRING_VECTOR_t *VarNames ) { + if(Lexer->tk == '[') { Vars.push_back(DESTRUCTURING_VAR_t(Path, "[")); // marks array begin - _tokenizeDeconstructionVarArray(Vars, VarNames); + tokenizeVarIdentifierDestructuringArray(Lexer, Vars, VarNames); Vars.push_back(DESTRUCTURING_VAR_t("", "]")); // marks array end - } else if(l->tk == '{') { + } else if(Lexer->tk == '{') { Vars.push_back(DESTRUCTURING_VAR_t(Path, "{")); // marks object begin - _tokenizeDeconstructionVarObject(Vars, VarNames); + tokenizeVarIdentifierDestructuringObject(Lexer, Vars, VarNames); Vars.push_back(DESTRUCTURING_VAR_t("", "}")); // marks object end } else { - VarNames.push_back(l->tkStr); - Vars.push_back(DESTRUCTURING_VAR_t(Path, l->tkStr)); - l->match(LEX_ID); + if(VarNames) VarNames->push_back(Lexer->tkStr); + Vars.push_back(DESTRUCTURING_VAR_t(Path, Lexer->tkStr)); + Lexer->match(LEX_ID); } } -CScriptToken CScriptTokenizer::tokenizeDestructionVar(STRING_VECTOR_t &VarNames) { +CScriptToken CScriptTokenizer::tokenizeVarIdentifier( STRING_VECTOR_t *VarNames/*=0*/, bool *NeedAssignment/*=0*/ ) { CScriptToken token(LEX_T_DESTRUCTURING_VAR); + if(NeedAssignment) *NeedAssignment=(l->tk == '[' || l->tk=='{'); token.column = l->currentColumn(); token.line = l->currentLine(); - _tokenizeDestructionVar(token.DestructuringVar().vars, "", VarNames); + tokenizeVarIdentifierDestructuring(l, token.DestructuringVar().vars, "", VarNames); return token; } -void CScriptTokenizer::tokenizeFunction(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { +void CScriptTokenizer::tokenizeFunction(ScriptTokenState &State, int Flags, bool noLetDef/*=false*/) { bool forward = false; bool Statement = (Flags & TOKENIZE_FLAGS_asStatement) != 0; bool Accessor = (Flags & TOKENIZE_FLAGS_isAccessor) != 0; - Flags &= ~(TOKENIZE_FLAGS_asStatement | TOKENIZE_FLAGS_isAccessor); + CScriptLex::POS functionPos = l->pos; int tk = l->tk; if(Accessor) { - tk = Tokens.back().String()=="get"?LEX_T_GET:LEX_T_SET; - Tokens.pop_back(); + tk = State.Tokens.back().String()=="get"?LEX_T_GET:LEX_T_SET; + State.Tokens.pop_back(); } else { l->match(LEX_R_FUNCTION); if(!Statement) tk = LEX_T_FUNCTION_OPERATOR; } if(tk == LEX_R_FUNCTION) // only forward functions - forward = BlockStart.front() == BlockStart.back(); + forward = !noLetDef && State.Forwarders.front() == State.Forwarders.back(); CScriptToken FncToken(tk); CScriptTokenDataFnc &FncData = FncToken.Fnc(); @@ -1450,11 +1520,7 @@ void CScriptTokenizer::tokenizeFunction(TOKEN_VECT &Tokens, vector &BlockSt throw new CScriptException(SyntaxError, "Function statement requires a name.", l->currentFile, l->currentLine(), l->currentColumn()); l->match('('); while(l->tk != ')') { - if(l->tk == '[' || l->tk=='{') { - STRING_VECTOR_t names; - FncData.arguments.push_back(tokenizeDestructionVar(names)); - } else - pushToken(FncData.arguments, LEX_ID); + FncData.arguments.push_back(tokenizeVarIdentifier()); if (l->tk!=')') l->match(',',')'); } // l->match(')'); @@ -1464,147 +1530,164 @@ void CScriptTokenizer::tokenizeFunction(TOKEN_VECT &Tokens, vector &BlockSt FncData.file = l->currentFile; FncData.line = l->currentLine(); - vector functionBlockStart, marks; - STRING_VECTOR_t labels, loopLabels; - + ScriptTokenState functionState; + functionState.HaveReturnValue = functionState.FunctionIsGenerator = false; if(l->tk == '{' || tk==LEX_T_GET || tk==LEX_T_SET) - tokenizeBlock(FncData.body, functionBlockStart, marks, labels, loopLabels, TOKENIZE_FLAGS_canReturn); + tokenizeBlock(functionState, TOKENIZE_FLAGS_canReturn | TOKENIZE_FLAGS_canYield); else { -// FncToken.token+=2; // SHORT-Version -// FncData.body.push_back(CScriptToken('{')); -// FncData.body.push_back(CScriptToken(LEX_R_RETURN)); - tokenizeExpression(FncData.body, functionBlockStart, marks, labels, loopLabels, 0); + tokenizeExpression(functionState, TOKENIZE_FLAGS_canYield); l->match(';'); -// pushToken(FncData.body, ';'); -// FncData.body.push_back(CScriptToken(';')); -// FncData.body.push_back(CScriptToken('}')); + functionState.HaveReturnValue = true; } + if(functionState.HaveReturnValue == true && functionState.FunctionIsGenerator == true) + throw new CScriptException(TypeError, "generator function returns a value.", l->currentFile, functionPos.currentLine, functionPos.currentColumn()); + FncData.isGenerator = functionState.FunctionIsGenerator; + + functionState.Tokens.swap(FncData.body); if(forward) { - Tokens[BlockStart.front()].Forwarder().functions.insert(FncToken); - FncToken.token = LEX_R_FUNCTION_PLACEHOLDER; + State.Forwarders.front()->functions.insert(FncToken); + FncToken.token = LEX_T_FUNCTION_PLACEHOLDER; } - Tokens.push_back(FncToken); + State.Tokens.push_back(FncToken); } -void CScriptTokenizer::tokenizeLet(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { - bool forFor = (Flags & TOKENIZE_FLAGS_forFor)!=0; - bool Statement = (Flags & TOKENIZE_FLAGS_asStatement)!=0; - Flags &= ~(TOKENIZE_FLAGS_forFor | TOKENIZE_FLAGS_asStatement); - bool expression=false; +void CScriptTokenizer::tokenizeLet(ScriptTokenState &State, int Flags, bool noLetDef/*=false*/) { + bool Definition = (Flags & TOKENIZE_FLAGS_asStatement)!=0; + bool noIN = (Flags & TOKENIZE_FLAGS_noIn)!=0; + bool Statement = Definition & !noIN; + bool Expression = !Definition; + Flags &= ~(TOKENIZE_FLAGS_asStatement); + if(!Definition) noIN=false, Flags &= ~TOKENIZE_FLAGS_noIn; + + bool foundIN = false; + bool leftHand = true; int currLine = l->currentLine(), currColumn = l->currentColumn(); - Marks.push_back(pushToken(Tokens)); // push Token & push BeginIdx + State.Marks.push_back(pushToken(State.Tokens)); // push Token & push BeginIdx - if(!forFor && (l->tk == '(' || !Statement)) { - expression = true; - pushToken(Tokens, '('); - pushForwarder(Tokens, BlockStart); - } + if(l->tk == '(' || !Definition) { // no definition needs statement or expression + leftHand = false; + Expression = true; + pushToken(State.Tokens, '('); + pushForwarder(State); + } else if(noLetDef) + throw new CScriptException(SyntaxError, "let declaration not directly within block", l->currentFile, currLine, currColumn); STRING_VECTOR_t vars; for(;;) { - bool isDestruction = false; - if(l->tk == '[' || l->tk=='{') { - isDestruction = true; - Tokens.push_back(tokenizeDestructionVar(vars)); - } else { - vars.push_back(l->tkStr); - pushToken(Tokens, LEX_ID); + bool needAssignment = false; + State.Tokens.push_back(tokenizeVarIdentifier(&vars, &needAssignment)); + if(noIN && (foundIN=(l->tk==LEX_R_IN || (l->tk==LEX_ID && l->tkStr=="of")))) + break; + if(needAssignment || l->tk=='=') { + leftHand = false; + pushToken(State.Tokens, '='); + tokenizeAssignment(State, Flags); + if(noIN && (foundIN=(l->tk==LEX_R_IN || (l->tk==LEX_ID && l->tkStr=="of")))) + break; } - if(isDestruction || l->tk=='=') { - pushToken(Tokens, '='); - tokenizeAssignment(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + if(l->tk==',') { + leftHand = false; + pushToken(State.Tokens); } - if(l->tk==',') - pushToken(Tokens); else break; } - if(expression) { - string redeclared = Tokens[BlockStart.back()].Forwarder().addLets(vars); + if(Expression) { + string redeclared = State.Forwarders.back()->addLets(vars); if(redeclared.size()) throw new CScriptException(TypeError, "redeclaration of variable '"+redeclared+"'", l->currentFile, currLine, currColumn); - pushToken(Tokens, ')'); - if(Statement) { - if(l->tk == '{') // no extra BlockStart by expression - tokenizeBlock(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags|=TOKENIZE_FLAGS_noBlockStart); - else - tokenizeStatement(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); - } else - tokenizeExpression(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); - + if(!foundIN) { + pushToken(State.Tokens, ')'); + if(Statement) { + if(l->tk == '{') // no extra BlockStart by expression + tokenizeBlock(State, Flags|=TOKENIZE_FLAGS_noBlockStart); + else + tokenizeStatementNoLet(State, Flags); + } else + tokenizeAssignment(State, Flags); + } // never remove Forwarder-token here -- popForwarder(Tokens, BlockStart, Marks); - Tokens[BlockStart.back()].Forwarder().vars_in_letscope.clear(); // only clear vars_in_letscope + State.Forwarders.back()->vars_in_letscope.clear(); // only clear vars_in_letscope + State.Marks.pop_back(); } else { - if(!forFor) pushToken(Tokens, ';'); + if(!noIN) pushToken(State.Tokens, ';'); string redeclared; - if(BlockStart.front() == BlockStart.back()) { + if(State.Forwarders.size()<=1) { // Currently it is allowed in javascript, to redeclare "let"-declared vars // in root- or function-scopes. In this case, "let" handled like "var" // To prevent redeclaration in root- or function-scopes define PREVENT_REDECLARATION_IN_FUNCTION_SCOPES #ifdef PREVENT_REDECLARATION_IN_FUNCTION_SCOPES - redeclared = Tokens[BlockStart.front()].Forwarder().addLets(vars); + redeclared = State.Forwarders.front()->addLets(vars); #else - Tokens[BlockStart.front()].Forwarder().addVars(vars); + State.Forwarders.front()->addVars(vars); #endif - } else if(Tokens[BlockStart.back()].token == LEX_T_FORWARD){ - redeclared = Tokens[BlockStart.back()].Forwarder().addLets(vars); } else - throw new CScriptException(SyntaxError, "let declaration not directly within block", l->currentFile, currLine, currColumn); + redeclared = State.Forwarders.back()->addLets(vars); if(redeclared.size()) throw new CScriptException(TypeError, "redeclaration of variable '"+redeclared+"'", l->currentFile, currLine, currColumn); } - setTokenSkip(Tokens, Marks); + setTokenSkip(State); + if(leftHand) State.LeftHand = true; } -void CScriptTokenizer::tokenizeVar(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { - bool forFor = (Flags & TOKENIZE_FLAGS_forFor)!=0; - Flags &= ~TOKENIZE_FLAGS_forFor; +void CScriptTokenizer::tokenizeVarNoConst( ScriptTokenState &State, int Flags) { + l->check(LEX_R_VAR); + tokenizeVarAndConst(State, Flags); +} +void CScriptTokenizer::tokenizeVarAndConst( ScriptTokenState &State, int Flags) { + bool noIN = (Flags & TOKENIZE_FLAGS_noIn)!=0; int currLine = l->currentLine(), currColumn = l->currentColumn(); - Marks.push_back(pushToken(Tokens)); // push Token & push BeginIdx + bool leftHand = true; + int tk = l->tk; + State.Marks.push_back(pushToken(State.Tokens)); // push Token & push BeginIdx STRING_VECTOR_t vars; for(;;) { - bool isDestruction = false; - if(l->tk == '[' || l->tk=='{') { - isDestruction = true; - Tokens.push_back(tokenizeDestructionVar(vars)); - } else { - vars.push_back(l->tkStr); - pushToken(Tokens, LEX_ID); + bool needAssignment = false; + State.Tokens.push_back(tokenizeVarIdentifier(&vars, &needAssignment)); + if(noIN && (l->tk==LEX_R_IN || (l->tk==LEX_ID && l->tkStr=="of"))) + break; + if(needAssignment || l->tk=='=') { + leftHand = false; + pushToken(State.Tokens, '='); + tokenizeAssignment(State, Flags); + if(noIN && (l->tk==LEX_R_IN || (l->tk==LEX_ID && l->tkStr=="of"))) + break; } - if(isDestruction || l->tk=='=') { - pushToken(Tokens, '='); - tokenizeAssignment(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + if(l->tk==',') { + leftHand = false; + pushToken(State.Tokens); } - if(l->tk==',') - pushToken(Tokens); else break; } - if(!forFor) pushToken(Tokens, ';'); + if(!noIN) pushToken(State.Tokens, ';'); - setTokenSkip(Tokens, Marks); + setTokenSkip(State); - Tokens[BlockStart.front()].Forwarder().addVars(vars); + if(tk==LEX_R_VAR) + State.Forwarders.front()->addVars(vars); + else + State.Forwarders.front()->addConsts(vars); string redeclared; - if(BlockStart.front() != BlockStart.back()) { - if(Tokens[BlockStart.back()].token == LEX_T_FORWARD) - redeclared = Tokens[BlockStart.back()].Forwarder().addVarsInLetscope(vars); - } + if(State.Forwarders.size()>1) // have let-scope + redeclared = State.Forwarders.back()->addVarsInLetscope(vars); #ifdef PREVENT_REDECLARATION_IN_FUNCTION_SCOPES - else { - redeclared = Tokens[BlockStart.front()].Forwarder().addVarsInLetscope(vars); - } + else + redeclared = State.Forwarders.front()->addVarsInLetscope(vars); #endif if(redeclared.size()) throw new CScriptException(TypeError, "redeclaration of variable '"+redeclared+"'", l->currentFile, currLine, currColumn); + if(leftHand) State.LeftHand = true; } -void CScriptTokenizer::_tokenizeLiteralObject(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags, int &State) { - bool nestedObject = (Flags & TOKENIZE_FLAGS_nestedObject) != 0; Flags &= ~TOKENIZE_FLAGS_nestedObject; +void CScriptTokenizer::_tokenizeLiteralObject(ScriptTokenState &State, int Flags) { + bool forFor = (Flags & TOKENIZE_FLAGS_noIn)!=0; + bool nestedObject = (Flags & TOKENIZE_FLAGS_nestedObject) != 0; + Flags &= ~(TOKENIZE_FLAGS_noIn | TOKENIZE_FLAGS_nestedObject); CScriptToken ObjectToken(LEX_T_OBJECT_LITERAL); CScriptTokenDataObjectLiteral &Objc = ObjectToken.Object(); @@ -1618,13 +1701,17 @@ void CScriptTokenizer::_tokenizeLiteralObject(TOKEN_VECT &Tokens, vector &B while (l->tk != '}') { CScriptTokenDataObjectLiteral::ELEMENT element; bool assign = false; + if(CScriptToken::isReservedWord(l->tk)) + l->tk = LEX_ID; // fake reserved-word as member.ID if(l->tk == LEX_ID) { element.id = l->tkStr; CScriptToken Token(l, LEX_ID); if((l->tk==LEX_ID || l->tk==LEX_STR ) && (element.id=="get" || element.id=="set")) { element.id = l->tkStr; element.value.push_back(Token); - tokenizeFunction(element.value, BlockStart, Marks, Labels, LoopLabels, Flags|TOKENIZE_FLAGS_isAccessor); + State.Tokens.swap(element.value); + tokenizeFunction(State, Flags|TOKENIZE_FLAGS_isAccessor); + State.Tokens.swap(element.value); Objc.destructuring = false; } else { if(Objc.destructuring && (l->tk == ',' || l->tk == '}')) { @@ -1657,9 +1744,12 @@ void CScriptTokenizer::_tokenizeLiteralObject(TOKEN_VECT &Tokens, vector &B if(assign) { l->match(':'); int dFlags = Flags | (l->tk == '{' || l->tk == '[') ? TOKENIZE_FLAGS_nestedObject : 0; - int dState = TOKENIZE_STATE_Destructuring; - tokenizeAssignment(element.value, BlockStart, Marks, Labels, LoopLabels, dFlags, dState); - if(Objc.destructuring) Objc.destructuring = dState == (TOKENIZE_STATE_leftHand | TOKENIZE_STATE_Destructuring); + State.pushLeftHandState(); + State.Tokens.swap(element.value); + tokenizeAssignment(State, dFlags); + State.Tokens.swap(element.value); + if(Objc.destructuring) Objc.destructuring = State.LeftHand; + State.popLeftHandeState(); } if(!Objc.destructuring && msg.size()) @@ -1674,16 +1764,21 @@ void CScriptTokenizer::_tokenizeLiteralObject(TOKEN_VECT &Tokens, vector &B Objc.destructuring = false; } else - Objc.setMode(l->tk=='='); - } else if(!Objc.destructuring && msg.size()) - throw new CScriptException(SyntaxError, msg, msgFile, msgLine, msgColumn); + Objc.setMode(l->tk=='=' || (forFor && (l->tk==LEX_R_IN ||(l->tk==LEX_ID && l->tkStr=="of")))); + } else { + if(!Objc.destructuring && msg.size()) + throw new CScriptException(SyntaxError, msg, msgFile, msgLine, msgColumn); + if(!nestedObject) Objc.setMode(Objc.destructuring); + } if(Objc.destructuring) - State |= TOKENIZE_STATE_leftHand | TOKENIZE_STATE_Destructuring; - Tokens.push_back(ObjectToken); + State.LeftHand = true; + State.Tokens.push_back(ObjectToken); } -void CScriptTokenizer::_tokenizeLiteralArray(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags, int &State) { - bool nestedObject = (Flags & TOKENIZE_FLAGS_nestedObject) != 0; Flags &= ~TOKENIZE_FLAGS_nestedObject; +void CScriptTokenizer::_tokenizeLiteralArray(ScriptTokenState &State, int Flags) { + bool forFor = (Flags & TOKENIZE_FLAGS_noIn)!=0; + bool nestedObject = (Flags & TOKENIZE_FLAGS_nestedObject) != 0; + Flags &= ~(TOKENIZE_FLAGS_noIn | TOKENIZE_FLAGS_nestedObject); CScriptToken ObjectToken(LEX_T_OBJECT_LITERAL); CScriptTokenDataObjectLiteral &Objc = ObjectToken.Object(); @@ -1697,9 +1792,12 @@ void CScriptTokenizer::_tokenizeLiteralArray(TOKEN_VECT &Tokens, vector &Bl element.id = int2string(idx++); if(l->tk != ',') { int dFlags = Flags | (l->tk == '{' || l->tk == '[') ? TOKENIZE_FLAGS_nestedObject : 0; - int dState = TOKENIZE_STATE_Destructuring; - tokenizeAssignment(element.value, BlockStart, Marks, Labels, LoopLabels, dFlags, dState); - if(Objc.destructuring) Objc.destructuring = dState == (TOKENIZE_STATE_leftHand | TOKENIZE_STATE_Destructuring); + State.pushLeftHandState(); + State.Tokens.swap(element.value); + tokenizeAssignment(State, dFlags); + State.Tokens.swap(element.value); + if(Objc.destructuring) Objc.destructuring = State.LeftHand; + State.popLeftHandeState(); } Objc.elements.push_back(element); if (l->tk != ']') l->match(',', ']'); @@ -1711,64 +1809,36 @@ void CScriptTokenizer::_tokenizeLiteralArray(TOKEN_VECT &Tokens, vector &Bl Objc.destructuring = false; } else - Objc.setMode(l->tk=='='); - } + Objc.setMode(l->tk=='=' || (forFor && (l->tk==LEX_R_IN ||(l->tk==LEX_ID && l->tkStr=="of")))); + } else + if(!nestedObject) Objc.setMode(Objc.destructuring); if(Objc.destructuring) - State |= TOKENIZE_STATE_leftHand | TOKENIZE_STATE_Destructuring; - Tokens.push_back(ObjectToken); + State.LeftHand = true; + State.Tokens.push_back(ObjectToken); } -void CScriptTokenizer::tokenizeObjectLiteral(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags, int &State) { -//--> begin testing - CScriptLex::POS pos = l->pos; - _tokenizeLiteralObject(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags, State); - l->reset(pos); - Tokens.pop_back(); - Flags &= ~TOKENIZE_FLAGS_nestedObject; -//--> end testing - - Marks.push_back(pushToken(Tokens)); // push Token & push BeginIdx - while (l->tk != '}') { - if(l->tk == LEX_ID) { - string id = l->tkStr; - pushToken(Tokens); - if((l->tk==LEX_ID || l->tk==LEX_STR ) && (id=="get" || id=="set")) - tokenizeFunction(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags|TOKENIZE_FLAGS_isAccessor); - else { - pushToken(Tokens, ':'); - tokenizeAssignment(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); - } - } else if(l->tk == LEX_STR) { - pushToken(Tokens); - pushToken(Tokens, ':'); - tokenizeAssignment(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); - } - if (l->tk != '}') pushToken(Tokens, ',', '}'); - } - pushToken(Tokens); - setTokenSkip(Tokens, Marks); -} -void CScriptTokenizer::tokenizeLiteral(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags, int &State) { - State &= ~TOKENIZE_STATE_leftHand; - bool canLabel = Flags & TOKENIZE_FLAGS_canLabel; - Flags &= ~TOKENIZE_FLAGS_canLabel; +void CScriptTokenizer::tokenizeLiteral(ScriptTokenState &State, int Flags) { + State.LeftHand = 0; + bool canLabel = Flags & TOKENIZE_FLAGS_canLabel; Flags &= ~TOKENIZE_FLAGS_canLabel; + int ObjectLiteralFlags = Flags; + Flags &= ~TOKENIZE_FLAGS_noIn; switch(l->tk) { case LEX_ID: { string label = l->tkStr; - pushToken(Tokens); + pushToken(State.Tokens); if(l->tk==':' && canLabel) { - if(find(Labels.begin(), Labels.end(), label) != Labels.end()) + if(find(State.Labels.begin(), State.Labels.end(), label) != State.Labels.end()) throw new CScriptException(SyntaxError, "dublicate label '"+label+"'", l->currentFile, l->currentLine(), l->currentColumn()-label.size()); - Tokens[Tokens.size()-1].token = LEX_T_LABEL; // change LEX_ID to LEX_T_LABEL - Labels.push_back(label); + State.Tokens[State.Tokens.size()-1].token = LEX_T_LABEL; // change LEX_ID to LEX_T_LABEL + State.Labels.push_back(label); } else if(label=="this") { if( l->tk == '=' || (l->tk >= LEX_ASSIGNMENTS_BEGIN && l->tk <= LEX_ASSIGNMENTS_END) ) throw new CScriptException(SyntaxError, "invalid assignment left-hand side", l->currentFile, l->currentLine(), l->currentColumn()-label.size()); if( l->tk==LEX_PLUSPLUS || l->tk==LEX_MINUSMINUS ) throw new CScriptException(SyntaxError, l->tk==LEX_PLUSPLUS?"invalid increment operand":"invalid decrement operand", l->currentFile, l->currentLine(), l->currentColumn()-label.size()); } else - State |= TOKENIZE_STATE_leftHand; + State.LeftHand = true; } break; case LEX_INT: @@ -1778,72 +1848,89 @@ void CScriptTokenizer::tokenizeLiteral(TOKEN_VECT &Tokens, vector &BlockSta case LEX_R_TRUE: case LEX_R_FALSE: case LEX_R_NULL: - pushToken(Tokens); + pushToken(State.Tokens); break; case '{': - _tokenizeLiteralObject(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags, State); -// tokenizeObjectLiteral(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags, State); + _tokenizeLiteralObject(State, ObjectLiteralFlags); break; case '[': - _tokenizeLiteralArray(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags, State); + _tokenizeLiteralArray(State, ObjectLiteralFlags); break; case LEX_R_LET: // let as expression - tokenizeLet(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + tokenizeLet(State, Flags); break; case LEX_R_FUNCTION: - tokenizeFunction(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + tokenizeFunction(State, Flags); break; case LEX_R_NEW: - Marks.push_back(pushToken(Tokens)); // push Token & push BeginIdx + State.Marks.push_back(pushToken(State.Tokens)); // push Token & push BeginIdx { - int State; - tokenizeFunctionCall(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags | TOKENIZE_FLAGS_callForNew, State); + tokenizeFunctionCall(State, (Flags | TOKENIZE_FLAGS_callForNew) & ~TOKENIZE_FLAGS_noIn); + State.LeftHand = 0; + } + setTokenSkip(State); + break; +#ifndef NO_GENERATORS + case LEX_R_YIELD: + if( (Flags & TOKENIZE_FLAGS_canYield)==0) + throw new CScriptException(SyntaxError, "'yield' expression, but not in a function.", l->currentFile, l->currentLine(), l->currentColumn()); + pushToken(State.Tokens); + if(l->tk != ';' && l->tk != '}' && !l->lineBreakBeforeToken) { + tokenizeExpression(State, Flags); } - setTokenSkip(Tokens, Marks); + State.FunctionIsGenerator = true; break; +#endif case '(': - Marks.push_back(pushToken(Tokens)); // push Token & push BeginIdx - tokenizeExpression(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); - pushToken(Tokens, ')'); - setTokenSkip(Tokens, Marks); + State.Marks.push_back(pushToken(State.Tokens)); // push Token & push BeginIdx + tokenizeExpression(State, Flags & ~TOKENIZE_FLAGS_noIn); + State.LeftHand = 0; + pushToken(State.Tokens, ')'); + setTokenSkip(State); break; default: l->check(LEX_EOF); } } -void CScriptTokenizer::tokenizeMember(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags, int &State) { +void CScriptTokenizer::tokenizeMember(ScriptTokenState &State, int Flags) { while(l->tk == '.' || l->tk == '[') { if(l->tk == '.') { - pushToken(Tokens); - pushToken(Tokens , LEX_ID); + pushToken(State.Tokens); + if(CScriptToken::isReservedWord(l->tk)) + l->tk = LEX_ID; // fake reserved-word as member.ID + pushToken(State.Tokens , LEX_ID); } else { - Marks.push_back(pushToken(Tokens)); - tokenizeExpression(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); - pushToken(Tokens, ']'); - setTokenSkip(Tokens, Marks); + State.Marks.push_back(pushToken(State.Tokens)); + State.pushLeftHandState(); + tokenizeExpression(State, Flags & ~TOKENIZE_FLAGS_noIn); + State.popLeftHandeState(); + pushToken(State.Tokens, ']'); + setTokenSkip(State); } - State |= TOKENIZE_STATE_leftHand; + State.LeftHand = true; } } -void CScriptTokenizer::tokenizeFunctionCall(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags, int &State) { +void CScriptTokenizer::tokenizeFunctionCall(ScriptTokenState &State, int Flags) { bool for_new = (Flags & TOKENIZE_FLAGS_callForNew)!=0; Flags &= ~TOKENIZE_FLAGS_callForNew; - tokenizeLiteral(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags, State); - tokenizeMember(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags, State); + tokenizeLiteral(State, Flags); + tokenizeMember(State, Flags); while(l->tk == '(') { - State &= ~TOKENIZE_STATE_leftHand; - Marks.push_back(pushToken(Tokens)); // push Token & push BeginIdx + State.LeftHand = false; + State.Marks.push_back(pushToken(State.Tokens)); // push Token & push BeginIdx + State.pushLeftHandState(); while(l->tk!=')') { - tokenizeAssignment(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); - if (l->tk!=')') pushToken(Tokens, ',', ')'); + tokenizeAssignment(State, Flags & ~TOKENIZE_FLAGS_noIn); + if (l->tk!=')') pushToken(State.Tokens, ',', ')'); } - pushToken(Tokens); - setTokenSkip(Tokens, Marks); + State.popLeftHandeState(); + pushToken(State.Tokens); + setTokenSkip(State); if(for_new) break; - tokenizeMember(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags, State); + tokenizeMember(State, Flags); } } -void CScriptTokenizer::tokenizeSubExpression(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags, int &State) { +void CScriptTokenizer::tokenizeSubExpression(ScriptTokenState &State, int Flags) { static int Left2Right_begin[] = { /* Precedence 5 */ '*', '/', '%', /* Precedence 6 */ '+', '-', @@ -1851,7 +1938,6 @@ void CScriptTokenizer::tokenizeSubExpression(TOKEN_VECT &Tokens, vector &Bl /* Precedence 8 */ LEX_EQUAL, LEX_NEQUAL, LEX_TYPEEQUAL, LEX_NTYPEEQUAL, /* Precedence 9 */ '<', LEX_LEQUAL, '>', LEX_GEQUAL, LEX_R_IN, LEX_R_INSTANCEOF, /* Precedence 10-12 */ '&', '^', '|', - /* Precedence 13-14 */ LEX_ANDAND, LEX_OROR, }; static int *Left2Right_end = &Left2Right_begin[sizeof(Left2Right_begin)/sizeof(Left2Right_begin[0])]; static bool Left2Right_sorted = false; @@ -1870,7 +1956,7 @@ void CScriptTokenizer::tokenizeSubExpression(TOKEN_VECT &Tokens, vector &Bl case LEX_R_DELETE: Flags &= ~TOKENIZE_FLAGS_canLabel; noLeftHand = true; - pushToken(Tokens); // Precedence 3 + pushToken(State.Tokens); // Precedence 3 break; case LEX_PLUSPLUS: // pre-increment case LEX_MINUSMINUS: // pre-decrement @@ -1878,7 +1964,7 @@ void CScriptTokenizer::tokenizeSubExpression(TOKEN_VECT &Tokens, vector &Bl int tk = l->tk; Flags &= ~TOKENIZE_FLAGS_canLabel; noLeftHand = true; - pushToken(Tokens); // Precedence 4 + pushToken(State.Tokens); // Precedence 4 if(l->tk == LEX_ID && l->tkStr == "this") throw new CScriptException(SyntaxError, tk==LEX_PLUSPLUS?"invalid increment operand":"invalid decrement operand", l->currentFile, l->currentLine(), l->currentColumn()); } @@ -1886,131 +1972,168 @@ void CScriptTokenizer::tokenizeSubExpression(TOKEN_VECT &Tokens, vector &Bl right2left_end = true; } } - tokenizeFunctionCall(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags, State); + tokenizeFunctionCall(State, Flags); if (!l->lineBreakBeforeToken && (l->tk==LEX_PLUSPLUS || l->tk==LEX_MINUSMINUS)) { // post-in-/de-crement noLeftHand = true;; - pushToken(Tokens); // Precedence 4 + pushToken(State.Tokens); // Precedence 4 } + if(Flags&TOKENIZE_FLAGS_noIn && l->tk==LEX_R_IN) + break; int *found = lower_bound(Left2Right_begin, Left2Right_end, l->tk); if(found != Left2Right_end && *found == l->tk) { noLeftHand = true; - pushToken(Tokens); // Precedence 5-14 + pushToken(State.Tokens); // Precedence 5-14 } else break; } - if(noLeftHand) State &= ~TOKENIZE_STATE_leftHand; + if(noLeftHand) State.LeftHand = false; } -void CScriptTokenizer::tokenizeCondition(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags, int &State) { - tokenizeSubExpression(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags, State); - if(l->tk == '?') { - pushToken(Tokens); - tokenizeCondition(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags, State); - pushToken(Tokens, ':'); - tokenizeCondition(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags, State); - State &= ~TOKENIZE_STATE_leftHand; +void CScriptTokenizer::tokenizeLogic(ScriptTokenState &State, int Flags, int op /*= LEX_OROR*/, int op_n /*= LEX_ANDAND*/) { + op_n ? tokenizeLogic(State, Flags, op_n, 0) : tokenizeSubExpression(State, Flags); + if(l->tk==op) { + unsigned int marks_count = State.Marks.size(); + while(l->tk==op) { + State.Marks.push_back(pushToken(State.Tokens)); + op_n ? tokenizeLogic(State, Flags, op_n, 0) : tokenizeSubExpression(State, Flags); + } + while(State.Marks.size()>marks_count) setTokenSkip(State); + State.LeftHand = false; } } -void CScriptTokenizer::tokenizeAssignment(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { - int State=0; - tokenizeAssignment(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags, State); -} -void CScriptTokenizer::tokenizeAssignment(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags, int &State) { - State &= ~TOKENIZE_STATE_leftHand; - tokenizeCondition(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags, State); + +void CScriptTokenizer::tokenizeCondition(ScriptTokenState &State, int Flags) { + tokenizeLogic(State, Flags); + if(l->tk == '?') { + Flags &= ~(TOKENIZE_FLAGS_noIn | TOKENIZE_FLAGS_canLabel); + State.Marks.push_back(pushToken(State.Tokens)); + tokenizeAssignment(State, Flags); + setTokenSkip(State); + State.Marks.push_back(pushToken(State.Tokens, ':')); + tokenizeAssignment(State, Flags); + setTokenSkip(State); + State.LeftHand = false; + } +} +void CScriptTokenizer::tokenizeAssignment(ScriptTokenState &State, int Flags) { + tokenizeCondition(State, Flags); if (l->tk=='=' || (l->tk>=LEX_ASSIGNMENTS_BEGIN && l->tk<=LEX_ASSIGNMENTS_END) ) { - State &= ~TOKENIZE_STATE_Destructuring; - if( !(State & TOKENIZE_STATE_leftHand) ) { + if(!State.LeftHand) throw new CScriptException(ReferenceError, "invalid assignment left-hand side", l->currentFile, l->currentLine(), l->currentColumn()); - } - pushToken(Tokens); - tokenizeAssignment(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + pushToken(State.Tokens); + tokenizeAssignment(State, Flags); + State.LeftHand = false; } } -void CScriptTokenizer::tokenizeExpression(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { - tokenizeAssignment(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); +void CScriptTokenizer::tokenizeExpression(ScriptTokenState &State, int Flags) { + tokenizeAssignment(State, Flags); while(l->tk == ',') { - pushToken(Tokens); - tokenizeAssignment(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + pushToken(State.Tokens); + tokenizeAssignment(State, Flags); + State.LeftHand = false; } } -void CScriptTokenizer::tokenizeBlock(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { +void CScriptTokenizer::tokenizeBlock(ScriptTokenState &State, int Flags) { bool addBlockStart = (Flags&TOKENIZE_FLAGS_noBlockStart)==0; - Flags&=~TOKENIZE_FLAGS_noBlockStart; - Marks.push_back(pushToken(Tokens, '{')); // push Token & push BeginIdx - if(addBlockStart) pushForwarder(Tokens, BlockStart); + Flags&=~(TOKENIZE_FLAGS_noBlockStart); + State.Marks.push_back(pushToken(State.Tokens, '{')); // push Token & push BeginIdx + if(addBlockStart) pushForwarder(State); while(l->tk != '}' && l->tk != LEX_EOF) - tokenizeStatement(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); - pushToken(Tokens, '}'); + tokenizeStatement(State, Flags); + pushToken(State.Tokens, '}'); - if(addBlockStart) removeEmptyForwarder(Tokens, BlockStart, Marks); // clean-up BlockStarts + if(addBlockStart) removeEmptyForwarder(State); // clean-up BlockStarts - setTokenSkip(Tokens, Marks); + setTokenSkip(State); } - -void CScriptTokenizer::tokenizeStatement(TOKEN_VECT &Tokens, vector &BlockStart, vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags) { +void CScriptTokenizer::tokenizeStatementNoLet(ScriptTokenState &State, int Flags) { + if(l->tk == LEX_R_LET) + tokenizeLet(State, Flags | TOKENIZE_FLAGS_asStatement, true); + else if(l->tk==LEX_R_FUNCTION) + tokenizeFunction(State, Flags | TOKENIZE_FLAGS_asStatement, true); + else + tokenizeStatement(State, Flags); +} +void CScriptTokenizer::tokenizeStatement(ScriptTokenState &State, int Flags) { + int tk = l->tk; switch(l->tk) { - case '{': tokenizeBlock(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); break; - case ';': pushToken(Tokens); break; - case LEX_R_VAR: tokenizeVar(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); break; - case LEX_R_LET: tokenizeLet(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags | TOKENIZE_FLAGS_asStatement); break; - case LEX_R_WITH: tokenizeWith(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); break; - case LEX_R_IF: tokenizeIf(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); break; - case LEX_R_SWITCH: tokenizeSwitch(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); break; - case LEX_R_DO: tokenizeDo(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); break; - case LEX_R_WHILE: tokenizeWhile(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); break; - case LEX_R_FOR: tokenizeFor(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); break; - case LEX_R_FUNCTION: tokenizeFunction(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags | TOKENIZE_FLAGS_asStatement); break; - case LEX_R_TRY: tokenizeTry(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); break; - case LEX_R_RETURN: + case '{': tokenizeBlock(State, Flags); break; + case ';': pushToken(State.Tokens); break; + case LEX_R_CONST: + case LEX_R_VAR: tokenizeVarAndConst(State, Flags); break; + case LEX_R_LET: tokenizeLet(State, Flags | TOKENIZE_FLAGS_asStatement); break; + case LEX_R_WITH: tokenizeWith(State, Flags); break; + case LEX_R_IF: tokenizeIf(State, Flags); break; + case LEX_R_SWITCH: tokenizeSwitch(State, Flags); break; + case LEX_R_DO: + case LEX_R_WHILE: tokenizeWhileAndDo(State, Flags); break; + case LEX_R_FOR: tokenizeFor(State, Flags); break; + case LEX_R_FUNCTION: tokenizeFunction(State, Flags | TOKENIZE_FLAGS_asStatement); break; + case LEX_R_TRY: tokenizeTry(State, Flags); break; + case LEX_R_RETURN: if( (Flags & TOKENIZE_FLAGS_canReturn)==0) throw new CScriptException(SyntaxError, "'return' statement, but not in a function.", l->currentFile, l->currentLine(), l->currentColumn()); case LEX_R_THROW: - Marks.push_back(pushToken(Tokens)); // push Token & push BeginIdx - if(l->tk != ';' && !l->lineBreakBeforeToken) { - tokenizeExpression(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); + State.Marks.push_back(pushToken(State.Tokens)); // push Token & push BeginIdx + if(l->tk != ';' && l->tk != '}' && !l->lineBreakBeforeToken) { + if(tk==LEX_R_RETURN) State.HaveReturnValue = true; + tokenizeExpression(State, Flags); } - pushToken(Tokens, ';'); // push ';' - setTokenSkip(Tokens, Marks); + pushToken(State.Tokens, ';'); // push ';' + setTokenSkip(State); break; case LEX_R_BREAK: case LEX_R_CONTINUE: { bool isBreak = l->tk == LEX_R_BREAK; - Marks.push_back(pushToken(Tokens)); // push Token & push BeginIdx + State.Marks.push_back(pushToken(State.Tokens)); // push Token + if(l->tk != ';' && !l->lineBreakBeforeToken) { l->check(LEX_ID); - STRING_VECTOR_t &L = isBreak ? Labels : LoopLabels; + STRING_VECTOR_t &L = isBreak ? State.Labels : State.LoopLabels; if(find(L.begin(), L.end(), l->tkStr) == L.end()) throw new CScriptException(SyntaxError, "label '"+l->tkStr+"' not found", l->currentFile, l->currentLine(), l->currentColumn()); - pushToken(Tokens); // push 'Label' + pushToken(State.Tokens); // push 'Label' } else if((Flags & (isBreak ? TOKENIZE_FLAGS_canBreak : TOKENIZE_FLAGS_canContinue) )==0) throw new CScriptException(SyntaxError, - isBreak ? "'break' must be inside loop or switch" : "'continue' must be inside loop", + isBreak ? "'break' must be inside loop, switch or labeled statement" : "'continue' must be inside loop", l->currentFile, l->currentLine(), l->currentColumn()); - pushToken(Tokens, ';'); // push ';' - setTokenSkip(Tokens, Marks); + pushToken(State.Tokens, ';'); // push ';' + setTokenSkip(State); } break; case LEX_ID: { - STRING_VECTOR_t::size_type label_count = Labels.size(); - tokenizeExpression(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags | TOKENIZE_FLAGS_canLabel); - if(label_count < Labels.size() && l->tk == ':') { - pushToken(Tokens); // push ':' - tokenizeStatement(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); - Labels.pop_back(); - } else - pushToken(Tokens, ';'); + State.Marks.push_back(pushToken(State.Tokens, CScriptToken(LEX_T_SKIP))); // push skip & skiperBeginIdx + STRING_VECTOR_t::size_type label_count = State.Labels.size(); + tokenizeExpression(State, Flags | TOKENIZE_FLAGS_canLabel); + if(label_count < State.Labels.size() && l->tk == ':') { + State.Tokens.erase(State.Tokens.begin()+State.Marks.back()); // remove skip + State.Marks.pop_back(); + pushToken(State.Tokens); // push ':' + tokenizeStatement(State, Flags); + State.Labels.pop_back(); + } else { + pushToken(State.Tokens, ';'); + setTokenSkip(State); + } } break; - default: tokenizeExpression(Tokens, BlockStart, Marks, Labels, LoopLabels, Flags); pushToken(Tokens, ';'); break; + case 0: + break; + default: + State.Marks.push_back(pushToken(State.Tokens, CScriptToken(LEX_T_SKIP))); // push skip & skiperBeginIdx + tokenizeExpression(State, Flags); + pushToken(State.Tokens, ';'); + setTokenSkip(State); + break; } + } int CScriptTokenizer::pushToken(TOKEN_VECT &Tokens, int Match, int Alternate) { @@ -2025,33 +2148,39 @@ int CScriptTokenizer::pushToken(TOKEN_VECT &Tokens, const CScriptToken &Token) { Tokens.push_back(Token); return ret; } -CScriptTokenDataForwards &CScriptTokenizer::pushForwarder(TOKEN_VECT &Tokens, std::vector &BlockStarts) { - BlockStarts.push_back(Tokens.size()); - Tokens.push_back(CScriptToken(LEX_T_FORWARD)); - return Tokens.back().Forwarder(); +void CScriptTokenizer::pushForwarder(TOKEN_VECT &Tokens, FORWARDER_VECTOR_t &Forwarders, vector &Marks) { + Marks.push_back(Tokens.size()); + CScriptToken token(LEX_T_FORWARD); + Tokens.push_back(token); + Forwarders.push_back(token.Forwarder()); } -void CScriptTokenizer::throwTokenNotExpected() { - throw new CScriptException(SyntaxError, "'"+CScriptToken::getTokenStr(l->tk)+"' was not expected", l->currentFile, l->currentLine(), l->currentColumn()); +void CScriptTokenizer::pushForwarder(ScriptTokenState &State, bool noMarks/*=false*/) { + if(!noMarks) State.Marks.push_back(State.Tokens.size()); + CScriptToken token(LEX_T_FORWARD); + State.Tokens.push_back(token); + State.Forwarders.push_back(token.Forwarder()); } - -void CScriptTokenizer::removeEmptyForwarder( TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks ) +void CScriptTokenizer::removeEmptyForwarder(ScriptTokenState &State) { - CScriptTokenDataForwards &forwarder = Tokens[BlockStart.back()].Forwarder(); - forwarder.vars_in_letscope.clear(); - if(forwarder.vars.empty() && forwarder.lets.empty() && forwarder.functions.empty()) { - Tokens.erase(Tokens.begin()+BlockStart.back()); - fix_BlockStarts_Marks(BlockStart, Marks, BlockStart.back(), -1); - } - BlockStart.pop_back(); + CScriptTokenDataForwardsPtr &forwarder = State.Forwarders.back(); + forwarder->vars_in_letscope.clear(); + if(forwarder->empty()) + State.Tokens.erase(State.Tokens.begin()+State.Marks.back()); + State.Forwarders.pop_back(); + State.Marks.pop_back(); } -CScriptTokenDataForwards &CScriptTokenizer::__getForwarder( TOKEN_VECT &Tokens, int Pos, vector &BlockStart, vector &Marks ) +void CScriptTokenizer::removeEmptyForwarder( TOKEN_VECT &Tokens, FORWARDER_VECTOR_t &Forwarders, vector &Marks ) { - if( (Tokens.begin()+Pos)->token != LEX_T_FORWARD) { - Tokens.insert(Tokens.begin()+Pos, CScriptToken(LEX_T_FORWARD)); - fix_BlockStarts_Marks(BlockStart, Marks, Pos+1, 1); - } - return (Tokens.begin()+Pos)->Forwarder(); + CScriptTokenDataForwardsPtr &forwarder = Forwarders.back(); + forwarder->vars_in_letscope.clear(); + if(forwarder->empty()) + Tokens.erase(Tokens.begin()+Marks.back()); + Forwarders.pop_back(); + Marks.pop_back(); +} +void CScriptTokenizer::throwTokenNotExpected() { + throw new CScriptException(SyntaxError, "'"+CScriptToken::getTokenStr(l->tk)+"' was not expected", l->currentFile, l->currentLine(), l->currentColumn()); } @@ -2062,7 +2191,7 @@ CScriptTokenDataForwards &CScriptTokenizer::__getForwarder( TOKEN_VECT &Tokens, CScriptVar::CScriptVar(CTinyJS *Context, const CScriptVarPtr &Prototype) { extensible = true; context = Context; - temporaryID = 0; + memset(temporaryMark, 0, sizeof(temporaryMark)); if(context->first) { next = context->first; next->prev = this; @@ -2081,7 +2210,7 @@ CScriptVar::CScriptVar(CTinyJS *Context, const CScriptVarPtr &Prototype) { CScriptVar::CScriptVar(const CScriptVar &Copy) { extensible = Copy.extensible; context = Copy.context; - temporaryID = 0; + memset(temporaryMark, 0, sizeof(temporaryMark)); if(context->first) { next = context->first; next->prev = this; @@ -2136,6 +2265,8 @@ bool CScriptVar::isPrimitive() {return false;} bool CScriptVar::isFunction() {return false;} bool CScriptVar::isNative() {return false;} bool CScriptVar::isBounded() {return false;} +bool CScriptVar::isIterator() {return false;} +bool CScriptVar::isGenerator() {return false;} ////////////////////////////////////////////////////////////////////////// /// Value @@ -2148,24 +2279,24 @@ CScriptVarPrimitivePtr CScriptVar::toPrimitive() { return toPrimitive_hintNumber(); } -CScriptVarPrimitivePtr CScriptVar::toPrimitive(bool &execute) { +CScriptVarPrimitivePtr CScriptVar::toPrimitive(CScriptResult &execute) { return toPrimitive_hintNumber(execute); } CScriptVarPrimitivePtr CScriptVar::toPrimitive_hintString(int32_t radix) { - bool execute=true; + CScriptResult execute; CScriptVarPrimitivePtr var = toPrimitive_hintString(execute, radix); - if(!execute) throw (CScriptVarPtr)context->removeExeptionVar(); + execute.cThrow(); return var; } -CScriptVarPrimitivePtr CScriptVar::toPrimitive_hintString(bool &execute, int32_t radix) { +CScriptVarPrimitivePtr CScriptVar::toPrimitive_hintString(CScriptResult &execute, int32_t radix) { if(execute) { if(!isPrimitive()) { CScriptVarPtr ret = callJS_toString(execute, radix); if(execute && !ret->isPrimitive()) { ret = callJS_valueOf(execute); if(execute && !ret->isPrimitive()) - context->throwError(execute, TypeError, "can't convert b to primitive type"); + context->throwError(execute, TypeError, "can't convert to primitive type"); } return ret; } @@ -2174,12 +2305,12 @@ CScriptVarPrimitivePtr CScriptVar::toPrimitive_hintString(bool &execute, int32_t return constScriptVar(Undefined); } CScriptVarPrimitivePtr CScriptVar::toPrimitive_hintNumber() { - bool execute=true; + CScriptResult execute; CScriptVarPrimitivePtr var = toPrimitive_hintNumber(execute); - if(!execute) throw (CScriptVarPtr)context->removeExeptionVar(); + execute.cThrow(); return var; } -CScriptVarPrimitivePtr CScriptVar::toPrimitive_hintNumber(bool &execute) { +CScriptVarPrimitivePtr CScriptVar::toPrimitive_hintNumber(CScriptResult &execute) { if(execute) { if(!isPrimitive()) { CScriptVarPtr ret = callJS_valueOf(execute); @@ -2195,10 +2326,10 @@ CScriptVarPrimitivePtr CScriptVar::toPrimitive_hintNumber(bool &execute) { return constScriptVar(Undefined); } -CScriptVarPtr CScriptVar::callJS_valueOf(bool &execute) { +CScriptVarPtr CScriptVar::callJS_valueOf(CScriptResult &execute) { if(execute) { CScriptVarPtr FncValueOf = findChildWithPrototypeChain("valueOf").getter(execute); - if(FncValueOf != context->objectPrototype_valueOf) { // custom valueOf in JavaScript + if(FncValueOf && FncValueOf != context->objectPrototype_valueOf) { // custom valueOf in JavaScript if(FncValueOf->isFunction()) { // no Error if toString not callable vector Params; return context->callFunction(execute, FncValueOf, Params, this); @@ -2212,10 +2343,10 @@ CScriptVarPtr CScriptVar::valueOf_CallBack() { return this; } -CScriptVarPtr CScriptVar::callJS_toString(bool &execute, int radix/*=0*/) { +CScriptVarPtr CScriptVar::callJS_toString(CScriptResult &execute, int radix/*=0*/) { if(execute) { CScriptVarPtr FncToString = findChildWithPrototypeChain("toString").getter(execute); - if(FncToString != context->objectPrototype_toString) { // custom valueOf in JavaScript + if(FncToString && FncToString != context->objectPrototype_toString) { // custom valueOf in JavaScript if(FncToString->isFunction()) { // no Error if toString not callable vector Params; Params.push_back(newScriptVar(radix)); @@ -2226,15 +2357,15 @@ CScriptVarPtr CScriptVar::callJS_toString(bool &execute, int radix/*=0*/) { } return this; } -CScriptVarPtr CScriptVar::toString_CallBack(bool &execute, int radix/*=0*/) { +CScriptVarPtr CScriptVar::toString_CallBack(CScriptResult &execute, int radix/*=0*/) { return this; } CNumber CScriptVar::toNumber() { return toPrimitive_hintNumber()->toNumber_Callback(); }; -CNumber CScriptVar::toNumber(bool &execute) { return toPrimitive_hintNumber(execute)->toNumber_Callback(); }; +CNumber CScriptVar::toNumber(CScriptResult &execute) { return toPrimitive_hintNumber(execute)->toNumber_Callback(); }; bool CScriptVar::toBoolean() { return true; } string CScriptVar::toString(int32_t radix) { return toPrimitive_hintString(radix)->toCString(radix); } -string CScriptVar::toString(bool &execute, int32_t radix) { return toPrimitive_hintString(execute, radix)->toCString(radix); } +string CScriptVar::toString(CScriptResult &execute, int32_t radix/*=0*/) { return toPrimitive_hintString(execute, radix)->toCString(radix); } int CScriptVar::getInt() { return toNumber().toInt32(); } double CScriptVar::getDouble() { return toNumber().toDouble(); } @@ -2243,9 +2374,32 @@ string CScriptVar::getString() { return toPrimitive_hintString()->toCString(); } CScriptTokenDataFnc *CScriptVar::getFunctionData() { return 0; } +CScriptVarPtr CScriptVar::toIterator(int Mode/*=3*/) { + CScriptResult execute; + CScriptVarPtr var = toIterator(execute, Mode); + execute.cThrow(); + return var; +} +CScriptVarPtr CScriptVar::toIterator(CScriptResult &execute, int Mode/*=3*/) { + if(!execute) return constScriptVar(Undefined); + if(isIterator()) return this; + CScriptVarFunctionPtr Generator(findChildWithPrototypeChain("__iterator__").getter(execute)); + vector args; + if(Generator) return context->callFunction(execute, Generator, args, this); + return newScriptVarDefaultIterator(context, this, Mode); +} + +string CScriptVar::getParsableString() { + uint32_t UniqueID = context->allocUniqueID(); + bool hasRecursion=false; + string ret = getParsableString("", " ", UniqueID, hasRecursion); + context->freeUniqueID(); + return ret; +} + string CScriptVar::getParsableString(const string &indentString, const string &indent, uint32_t uniqueID, bool &hasRecursion) { getParsableStringRecursionsCheck(); - return toString(); + return indentString+toString(); } CScriptVarPtr CScriptVar::getNumericVar() { return newScriptVar(toNumber()); } @@ -2379,9 +2533,13 @@ CScriptVarLinkWorkPtr CScriptVar::findChildWithStringChars(const string &childNa if(child) return child; CScriptVarStringPtr strVar = getRawPrimitive(); uint32_t Idx; - if (strVar && (Idx=isArrayIndex(childName))!=uint32_t(-1) && IdxstringLength()) { - int Char = strVar->getChar(Idx); - child(newScriptVar(string(1, (char)Char)), childName, SCRIPTVARLINK_ENUMERABLE); + if (strVar && (Idx=isArrayIndex(childName))!=uint32_t(-1)) { + if (IdxstringLength()) { + int Char = strVar->getChar(Idx); + child(newScriptVar(string(1, (char)Char)), childName, SCRIPTVARLINK_ENUMERABLE); + } else { + child(constScriptVar(Undefined), childName, SCRIPTVARLINK_ENUMERABLE); + } child.setReferencedOwner(this); // fake referenced Owner return child; } @@ -2389,16 +2547,20 @@ CScriptVarLinkWorkPtr CScriptVar::findChildWithStringChars(const string &childNa } CScriptVarLinkPtr CScriptVar::findChildInPrototypeChain(const string &childName) { - unsigned int uniqueID = context->getUniqueID(); + unsigned int uniqueID = context->allocUniqueID(); // Look for links to actual parent classes CScriptVarPtr object = this; CScriptVarLinkPtr __proto__; - while( object->getTempraryID() != uniqueID && (__proto__ = object->findChild(TINYJS___PROTO___VAR)) ) { + while( object->getTemporaryMark() != uniqueID && (__proto__ = object->findChild(TINYJS___PROTO___VAR)) ) { CScriptVarLinkPtr implementation = __proto__->getVarPtr()->findChild(childName); - if (implementation) return implementation; - object->setTemporaryID(uniqueID); // prevents recursions + if (implementation) { + context->freeUniqueID(); + return implementation; + } + object->setTemporaryMark(uniqueID); // prevents recursions object = __proto__; } + context->freeUniqueID(); return 0; } @@ -2441,13 +2603,19 @@ CScriptVarLinkPtr CScriptVar::findChildOrCreateByPath(const string &path) { void CScriptVar::keys(set &Keys, bool OnlyEnumerable/*=true*/, uint32_t ID/*=0*/) { - setTemporaryID(ID); + if(ID) setTemporaryMark(ID); for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it) { if(!OnlyEnumerable || (*it)->isEnumerable()) Keys.insert((*it)->getName()); } + CScriptVarStringPtr isStringObj = this->getRawPrimitive(); + if(isStringObj) { + uint32_t length = isStringObj->stringLength(); + for(uint32_t i=0; igetVarPtr()->getTempraryID() != ID ) + if( ID && (__proto__ = findChild(TINYJS___PROTO___VAR)) && __proto__->getVarPtr()->getTemporaryMark() != ID ) __proto__->getVarPtr()->keys(Keys, OnlyEnumerable, ID); } @@ -2523,19 +2691,20 @@ uint32_t CScriptVar::getArrayLength() { } CScriptVarPtr CScriptVar::mathsOp(const CScriptVarPtr &b, int op) { - bool execute = true; + CScriptResult execute; return context->mathsOp(execute, this, b, op); } void CScriptVar::trace(const string &name) { string indentStr; - uint32_t uniqueID = context->getUniqueID(); + uint32_t uniqueID = context->allocUniqueID(); trace(indentStr, uniqueID, name); + context->freeUniqueID(); } void CScriptVar::trace(string &indentStr, uint32_t uniqueID, const string &name) { string indent = " "; const char *extra=""; - if(temporaryID == uniqueID) + if(getTemporaryMark() == uniqueID) extra = " recursion detected"; TRACE("%s'%s' = '%s' %s%s\n", indentStr.c_str(), @@ -2543,8 +2712,8 @@ void CScriptVar::trace(string &indentStr, uint32_t uniqueID, const string &name) toString().c_str(), getFlagsAsString().c_str(), extra); - if(temporaryID != uniqueID) { - temporaryID = uniqueID; + if(getTemporaryMark() != uniqueID) { + setTemporaryMark(uniqueID); indentStr+=indent; for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it) { if((*it)->isEnumerable()) @@ -2585,11 +2754,12 @@ int CScriptVar::getRefs() { return refs; } -void CScriptVar::setTemporaryID_recursive(uint32_t ID) { - if(temporaryID != ID) { - temporaryID = ID; +void CScriptVar::setTemporaryMark_recursive(uint32_t ID) +{ + if(getTemporaryMark() != ID) { + setTemporaryMark(ID); for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it) { - (*it)->getVarPtr()->setTemporaryID_recursive(ID); + (*it)->getVarPtr()->setTemporaryMark_recursive(ID); } } } @@ -2646,7 +2816,7 @@ CScriptVarLinkWorkPtr CScriptVarLinkPtr::getter() { return CScriptVarLinkWorkPtr(*this).getter(); } -CScriptVarLinkWorkPtr CScriptVarLinkPtr::getter( bool &execute ) { +CScriptVarLinkWorkPtr CScriptVarLinkPtr::getter( CScriptResult &execute ) { return CScriptVarLinkWorkPtr(*this).getter(execute); } @@ -2654,7 +2824,7 @@ CScriptVarLinkWorkPtr CScriptVarLinkPtr::setter( const CScriptVarPtr &Var ) { return CScriptVarLinkWorkPtr(*this).setter(Var); } -CScriptVarLinkWorkPtr CScriptVarLinkPtr::setter( bool &execute, const CScriptVarPtr &Var ) { +CScriptVarLinkWorkPtr CScriptVarLinkPtr::setter( CScriptResult &execute, const CScriptVarPtr &Var ) { return CScriptVarLinkWorkPtr(*this).setter(execute, Var); } @@ -2678,14 +2848,14 @@ bool CScriptVarLinkPtr::operator <(const string &rhs) const { CScriptVarLinkWorkPtr CScriptVarLinkWorkPtr::getter() { if(link && link->getVarPtr()) { - bool execute=true; + CScriptResult execute; CScriptVarPtr ret = getter(execute); - if(!execute) throw link->getVarPtr()->getContext()->removeExeptionVar(); + execute.cThrow(); return ret; } return *this; } -CScriptVarLinkWorkPtr CScriptVarLinkWorkPtr::getter(bool &execute) { +CScriptVarLinkWorkPtr CScriptVarLinkWorkPtr::getter(CScriptResult &execute) { if(execute && link && link->getVarPtr() && link->getVarPtr()->isAccessor()) { const CScriptVarPtr &var = link->getVarPtr(); CScriptVarLinkPtr getter = var->findChild(TINYJS_ACCESSOR_GET_VAR); @@ -2700,27 +2870,29 @@ CScriptVarLinkWorkPtr CScriptVarLinkWorkPtr::getter(bool &execute) { } CScriptVarLinkWorkPtr CScriptVarLinkWorkPtr::setter( const CScriptVarPtr &Var ) { if(link && link->getVarPtr()) { - bool execute=true; + CScriptResult execute; CScriptVarPtr ret = setter(execute, Var); - if(!execute) throw link->getVarPtr()->getContext()->removeExeptionVar(); + execute.cThrow(); return ret; } return *this; } -CScriptVarLinkWorkPtr CScriptVarLinkWorkPtr::setter( bool &execute, const CScriptVarPtr &Var ) { +CScriptVarLinkWorkPtr CScriptVarLinkWorkPtr::setter( CScriptResult &execute, const CScriptVarPtr &Var ) { if(execute) { - if(link && link->getVarPtr() && link->getVarPtr()->isAccessor()) { - const CScriptVarPtr &var = link->getVarPtr(); - CScriptVarLinkPtr setter = var->findChild(TINYJS_ACCESSOR_SET_VAR); - if(setter) { - vector Params; - Params.push_back(Var); - ASSERT(getReferencedOwner()); - setter->getVarPtr()->getContext()->callFunction(execute, setter->getVarPtr(), Params, getReferencedOwner()); - } - } else if(link->isWritable()) - link->setVarPtr(Var); + if(link) { + if(link->getVarPtr() && link->getVarPtr()->isAccessor()) { + const CScriptVarPtr &var = link->getVarPtr(); + CScriptVarLinkPtr setter = var->findChild(TINYJS_ACCESSOR_SET_VAR); + if(setter) { + vector Params; + Params.push_back(Var); + ASSERT(getReferencedOwner()); + setter->getVarPtr()->getContext()->callFunction(execute, setter->getVarPtr(), Params, getReferencedOwner()); + } + } else + link->setVarPtr(Var); + } } return *this; } @@ -2736,7 +2908,7 @@ bool CScriptVarPrimitive::isPrimitive() { return true; } CScriptVarPrimitivePtr CScriptVarPrimitive::getRawPrimitive() { return this; } bool CScriptVarPrimitive::toBoolean() { return false; } CScriptVarPtr CScriptVarPrimitive::toObject() { return this; } -CScriptVarPtr CScriptVarPrimitive::toString_CallBack( bool &execute, int radix/*=0*/ ) { +CScriptVarPtr CScriptVarPrimitive::toString_CallBack( CScriptResult &execute, int radix/*=0*/ ) { return newScriptVar(toCString(radix)); } @@ -2792,7 +2964,7 @@ bool CScriptVarString::toBoolean() { return data.length()!=0; } CNumber CScriptVarString::toNumber_Callback() { return data.c_str(); } string CScriptVarString::toCString(int radix/*=0*/) { return data; } -string CScriptVarString::getParsableString(const string &indentString, const string &indent, uint32_t uniqueID, bool &hasRecursion) { return getJSString(data); } +string CScriptVarString::getParsableString(const string &indentString, const string &indent, uint32_t uniqueID, bool &hasRecursion) { return indentString+getJSString(data); } string CScriptVarString::getVarType() { return "string"; } CScriptVarPtr CScriptVarString::toObject() { @@ -2801,7 +2973,7 @@ CScriptVarPtr CScriptVarString::toObject() { return ret; } -CScriptVarPtr CScriptVarString::toString_CallBack( bool &execute, int radix/*=0*/ ) { +CScriptVarPtr CScriptVarString::toString_CallBack( CScriptResult &execute, int radix/*=0*/ ) { return this; } @@ -2865,41 +3037,6 @@ CNumber &CNumber::operator=(const char *str) { if(*endptr != '\0') type=tNaN, Int32=0; return *this; - -#if 0 - double d; - bool sig=false; - while(isWhitespace(*str)) str++; - if(*str == '-') sig=true, str++; - char *ptr=(char*)str; - while(*ptr=='0' && ptr[1]>'0' && ptr[1]<'9') ptr++; // skip leading '0' to prevent parse as octal - char *endptr; - long i = strtol(ptr,&endptr,0); - if(ptr != endptr) { - ptr = endptr; - while(isWhitespace(*endptr)) endptr++; - if(*endptr == '\0') { - if(sig && i==0) - return operator=(negativeZero); - else - return operator=(int32_t(sig?-i:i)); - } - } - if(*ptr=='.' || *ptr=='e' || *ptr=='E') { - d = strtod(str,&endptr); - if(str!=endptr) { - while(isWhitespace(*endptr)) endptr++; - if(*endptr == '\0') { - if(sig && d==0) - return operator=(negativeZero); - else - return operator=(sig?-d:d); - } - } - } - type=tNaN, Int32=0; - return *this; -#endif } int32_t CNumber::parseInt(const char * str, int32_t radix/*=0*/, const char **endptr/*=0*/) { type=tInt32, Int32=0; @@ -3065,7 +3202,7 @@ CNumber CNumber::modulo( const CNumber &Value ) const { if(type==tNaN || type==tInfinity || Value.type==tNaN || Value.isZero()) return CNumber(tNaN); if(Value.type==tInfinity) return CNumber(*this); if(isZero()) return CNumber(0); - if(type==tDouble || Value.type==tDouble) { + if(type==tDouble || Value.type==tDouble || (Int32==numeric_limits::min() && Value == -1) /* use double to prevent integer overflow */ ) { double n = toDouble(), d = Value.toDouble(), q; modf(n/d, &q); return CNumber(n - (d * q)); @@ -3404,6 +3541,13 @@ declare_dummy_t(Object); CScriptVarObject::CScriptVarObject(CTinyJS *Context) : CScriptVar(Context, Context->objectPrototype) { } CScriptVarObject::~CScriptVarObject() {} CScriptVarPtr CScriptVarObject::clone() { return new CScriptVarObject(*this); } + +void CScriptVarObject::removeAllChildren() +{ + CScriptVar::removeAllChildren(); + value.clear(); +} + CScriptVarPrimitivePtr CScriptVarObject::getRawPrimitive() { return value; } bool CScriptVarObject::isObject() { return true; } @@ -3429,6 +3573,7 @@ string CScriptVarObject::getParsableString(const string &indentString, const str return destination; } string CScriptVarObject::getVarType() { return "object"; } +string CScriptVarObject::getVarTypeTagName() { return "Object"; } CScriptVarPtr CScriptVarObject::toObject() { return this; } @@ -3437,18 +3582,28 @@ CScriptVarPtr CScriptVarObject::valueOf_CallBack() { return value->valueOf_CallBack(); return CScriptVar::valueOf_CallBack(); } -CScriptVarPtr CScriptVarObject::toString_CallBack(bool &execute, int radix) { +CScriptVarPtr CScriptVarObject::toString_CallBack(CScriptResult &execute, int radix) { if(value) return value->toString_CallBack(execute, radix); - return newScriptVar("[object Object]"); + return newScriptVar("[object "+getVarTypeTagName()+"]"); }; -void CScriptVarObject::setTemporaryID_recursive( uint32_t ID ) { - CScriptVar::setTemporaryID_recursive(ID); - if(value) value->setTemporaryID_recursive(ID); +void CScriptVarObject::setTemporaryMark_recursive( uint32_t ID) { + CScriptVar::setTemporaryMark_recursive(ID); + if(value) value->setTemporaryMark_recursive(ID); } +////////////////////////////////////////////////////////////////////////// +/// CScriptVarObjectTyped (simple Object with Typename +////////////////////////////////////////////////////////////////////////// + +declare_dummy_t(StopIteration); +CScriptVarObjectTypeTagged::~CScriptVarObjectTypeTagged() {} +CScriptVarPtr CScriptVarObjectTypeTagged::clone() { return new CScriptVarObjectTypeTagged(*this); } +std::string CScriptVarObjectTypeTagged::getVarTypeTagName() { return typeTagName; } + + ////////////////////////////////////////////////////////////////////////// /// CScriptVarError ////////////////////////////////////////////////////////////////////////// @@ -3466,12 +3621,11 @@ CScriptVarError::~CScriptVarError() {} CScriptVarPtr CScriptVarError::clone() { return new CScriptVarError(*this); } bool CScriptVarError::isError() { return true; } -CScriptVarPtr CScriptVarError::toString_CallBack(bool &execute, int radix) { +CScriptVarPtr CScriptVarError::toString_CallBack(CScriptResult &execute, int radix) { CScriptVarLinkPtr link; string name = ERROR_NAME[Error]; link = findChildWithPrototypeChain("name"); if(link) name = link->toString(execute); - string message; link = findChildWithPrototypeChain("message"); if(link) - message = link->toString(execute); + string message; link = findChildWithPrototypeChain("message"); if(link) message = link->toString(execute); string fileName; link = findChildWithPrototypeChain("fileName"); if(link) fileName = link->toString(execute); int lineNumber=-1; link = findChildWithPrototypeChain("lineNumber"); if(link) lineNumber = link->toNumber().toInt32(); int column=-1; link = findChildWithPrototypeChain("column"); if(link) column = link->toNumber().toInt32(); @@ -3483,13 +3637,29 @@ CScriptVarPtr CScriptVarError::toString_CallBack(bool &execute, int radix) { return newScriptVar(msg.str()); } +CScriptException *CScriptVarError::toCScriptException() +{ + CScriptVarLinkPtr link; + string name = ERROR_NAME[Error]; + link = findChildWithPrototypeChain("name"); if(link) name = link->toString(); + int ErrorCode; + for(ErrorCode=(sizeof(ERROR_NAME)/sizeof(ERROR_NAME[0]))-1; ErrorCode>0; ErrorCode--) { + if(name == ERROR_NAME[ErrorCode]) break; + } + string message; link = findChildWithPrototypeChain("message"); if(link) message = link->toString(); + string fileName; link = findChildWithPrototypeChain("fileName"); if(link) fileName = link->toString(); + int lineNumber=-1; link = findChildWithPrototypeChain("lineNumber"); if(link) lineNumber = link->toNumber().toInt32()-1; + int column=-1; link = findChildWithPrototypeChain("column"); if(link) column = link->toNumber().toInt32()-1; + return new CScriptException((enum ERROR_TYPES)ErrorCode, message, fileName, lineNumber, column); +} + ////////////////////////////////////////////////////////////////////////// // CScriptVarArray ////////////////////////////////////////////////////////////////////////// declare_dummy_t(Array); -CScriptVarArray::CScriptVarArray(CTinyJS *Context) : CScriptVarObject(Context, Context->arrayPrototype) { +CScriptVarArray::CScriptVarArray(CTinyJS *Context) : CScriptVarObject(Context, Context->arrayPrototype), toStringRecursion(false) { CScriptVarLinkPtr acc = addChild("length", newScriptVar(Accessor), 0); CScriptVarFunctionPtr getter(::newScriptVar(Context, this, &CScriptVarArray::native_Length, 0)); getter->setFunctionData(new CScriptTokenDataFnc); @@ -3517,13 +3687,23 @@ string CScriptVarArray::getParsableString(const string &indentString, const stri destination.append("]"); return destination; } -CScriptVarPtr CScriptVarArray::toString_CallBack( bool &execute, int radix/*=0*/ ) { +CScriptVarPtr CScriptVarArray::toString_CallBack( CScriptResult &execute, int radix/*=0*/ ) { ostringstream destination; - int len = getArrayLength(); - for (int i=0;itoString(execute); - if (itoString(execute); + if (iiteratorPrototype), mode(Mode), object(Object) { + object->keys(keys, true); + pos = keys.begin(); + addChild("next", ::newScriptVar(context, this, &CScriptVarDefaultIterator::native_next, 0)); +} +CScriptVarDefaultIterator::~CScriptVarDefaultIterator() {} +CScriptVarPtr CScriptVarDefaultIterator::clone() { return new CScriptVarDefaultIterator(*this); } +bool CScriptVarDefaultIterator::isIterator() {return true;} +void CScriptVarDefaultIterator::native_next(const CFunctionsScopePtr &c, void *data) { + if(pos==keys.end()) throw constScriptVar(StopIteration); + CScriptVarPtr ret, ret0, ret1; + if(mode&1) ret0 = newScriptVar(*pos); + if(mode&2) ret1 = object->findChildWithStringChars(*pos); + pos++; + if(mode==3) { + ret = newScriptVar(Array); + ret->setArrayIndex(0, ret0); + ret->setArrayIndex(1, ret1); + } else if(mode==1) + ret = ret0; + else + ret = ret1; + c->setReturnVar(ret); +} + + +#ifndef NO_GENERATORS +////////////////////////////////////////////////////////////////////////// +/// CScriptVarGenerator +////////////////////////////////////////////////////////////////////////// + +//declare_dummy_t(Generator); +CScriptVarGenerator::CScriptVarGenerator(CTinyJS *Context, const CScriptVarPtr &FunctionRoot, const CScriptVarFunctionPtr &Function) + : CScriptVarObject(Context, Context->generatorPrototype), functionRoot(FunctionRoot), function(Function), + closed(false), yieldVarIsException(false), coroutine(this) { +// addChild("next", ::newScriptVar(context, this, &CScriptVarGenerator::native_send, 0, "Generator.next")); + // addChild("send", ::newScriptVar(context, this, &CScriptVarGenerator::native_send, (void*)1, "Generator.send")); + //addChild("close", ::newScriptVar(context, this, &CScriptVarGenerator::native_throw, (void*)0, "Generator.close")); + //addChild("throw", ::newScriptVar(context, this, &CScriptVarGenerator::native_throw, (void*)1, "Generator.throw")); +} +CScriptVarGenerator::~CScriptVarGenerator() { + if(coroutine.isStarted() && coroutine.isRunning()) { + coroutine.Stop(false); + coroutine.next(); + coroutine.Stop(); + } +} +CScriptVarPtr CScriptVarGenerator::clone() { return new CScriptVarGenerator(*this); } +bool CScriptVarGenerator::isIterator() {return true;} +bool CScriptVarGenerator::isGenerator() {return true;} + +string CScriptVarGenerator::getVarType() { return "generator"; } +string CScriptVarGenerator::getVarTypeTagName() { return "Generator"; } + +void CScriptVarGenerator::setTemporaryMark_recursive( uint32_t ID ) { + CScriptVarObject::setTemporaryMark_recursive(ID); + functionRoot->setTemporaryMark_recursive(ID); + function->setTemporaryMark_recursive(ID); + if(yieldVar) yieldVar->setTemporaryMark_recursive(ID); + for(std::vector::iterator it=generatorScopes.begin(); it != generatorScopes.end(); ++it) + (*it)->setTemporaryMark_recursive(ID); +} +void CScriptVarGenerator::native_send(const CFunctionsScopePtr &c, void *data) { + // data == 0 ==> next() + // data != 0 ==> send(...) + if(closed) + throw constScriptVar(StopIteration); + + yieldVar = data ? c->getArgument(0) : constScriptVar(Undefined); + yieldVarIsException = false; + + if(!coroutine.isStarted() && data && !yieldVar->isUndefined()) + c->throwError(TypeError, "attempt to send value to newborn generator"); + if(coroutine.next()) { + c->setReturnVar(yieldVar); + return; + } + closed = true; + throw yieldVar; +} +void CScriptVarGenerator::native_throw(const CFunctionsScopePtr &c, void *data) { + // data == 0 ==> close() + // data != 0 ==> throw(...) + if(closed || !coroutine.isStarted()) { + closed = true; + if(data) + throw c->getArgument(0); + else + return; + } + yieldVar = data ? c->getArgument(0) : CScriptVarPtr(); + yieldVarIsException = true; + closed = data==0; + if(coroutine.next()) { + c->setReturnVar(yieldVar); + return; + } + closed = true; + /* + * from http://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators + * Generators have a close() method that forces the generator to close itself. The effects of closing a generator are: + * - Any finally clauses active in the generator function are run. + * - If a finally clause throws any exception other than StopIteration, the exception is propagated to the caller of the close() method. + * - The generator terminates. + * + * but in Firefox is also "StopIteration" propagated to the caller + * define GENERATOR_CLOSE_LIKE_IN_FIREFOX to enable this behavior + */ +//#define GENERATOR_CLOSE_LIKE_IN_FIREFOX +#ifdef GENERATOR_CLOSE_LIKE_IN_FIREFOX + if(data || yieldVar) + throw yieldVar; +#else + if(data || (yieldVar && yieldVar != constScriptVar(StopIteration))) + throw yieldVar; +#endif +} + +int CScriptVarGenerator::Coroutine() +{ + context->generator_start(this); + return 0; +} + +CScriptVarPtr CScriptVarGenerator::yield( CScriptResult &execute, CScriptVar *YieldIn ) +{ + yieldVar = YieldIn; + coroutine.yield(); + if(yieldVarIsException) { + execute.set(CScriptResult::Throw, yieldVar); + return constScriptVar(Undefined); + } + return yieldVar; +} + +#endif /*NO_GENERATORS*/ + ////////////////////////////////////////////////////////////////////////// // CScriptVarFunction ////////////////////////////////////////////////////////////////////////// @@ -3671,9 +3994,9 @@ string CScriptVarFunction::getParsableString(const string &indentString, const s return destination; } -CScriptVarPtr CScriptVarFunction::toString_CallBack(bool &execute, int radix){ +CScriptVarPtr CScriptVarFunction::toString_CallBack(CScriptResult &execute, int radix){ bool hasRecursion; - return newScriptVar(getParsableString("", "", 0, hasRecursion)); + return newScriptVar(getParsableString("", " ", 0, hasRecursion)); } CScriptTokenDataFnc *CScriptVarFunction::getFunctionData() { return data; } @@ -3704,14 +4027,14 @@ CScriptVarFunctionBounded::CScriptVarFunctionBounded(CScriptVarFunctionPtr Bound CScriptVarFunctionBounded::~CScriptVarFunctionBounded(){} CScriptVarPtr CScriptVarFunctionBounded::clone() { return new CScriptVarFunctionBounded(*this); } bool CScriptVarFunctionBounded::isBounded() { return true; } -void CScriptVarFunctionBounded::setTemporaryID_recursive( uint32_t ID ) { - CScriptVarFunction::setTemporaryID_recursive(ID); - boundedThis->setTemporaryID_recursive(ID); +void CScriptVarFunctionBounded::setTemporaryMark_recursive(uint32_t ID) { + CScriptVarFunction::setTemporaryMark_recursive(ID); + boundedThis->setTemporaryMark_recursive(ID); for(vector::iterator it=boundedArguments.begin(); it!=boundedArguments.end(); ++it) - (*it)->setTemporaryID_recursive(ID); + (*it)->setTemporaryMark_recursive(ID); } -CScriptVarPtr CScriptVarFunctionBounded::callFunction( bool &execute, vector &Arguments, const CScriptVarPtr &This, CScriptVarPtr *newThis/*=0*/ ) +CScriptVarPtr CScriptVarFunctionBounded::callFunction( CScriptResult &execute, vector &Arguments, const CScriptVarPtr &This, CScriptVarPtr *newThis/*=0*/ ) { vector newArgs=boundedArguments; newArgs.insert(newArgs.end(), Arguments.begin(), Arguments.end()); @@ -3890,13 +4213,14 @@ extern "C" void _registerFunctions(CTinyJS *tinyJS); extern "C" void _registerStringFunctions(CTinyJS *tinyJS); extern "C" void _registerMathFunctions(CTinyJS *tinyJS); -bool CTinyJS::noexecute = false; CTinyJS::CTinyJS() { CScriptVarPtr var; t = 0; - runtimeFlags = 0; + haveTry = false; first = 0; uniqueID = 0; + currentMarkSlot = -1; + stackBase = 0; ////////////////////////////////////////////////////////////////////////// @@ -3924,6 +4248,7 @@ CTinyJS::CTinyJS() { // Object var = addNative("function Object()", this, &CTinyJS::native_Object, 0, SCRIPTVARLINK_CONSTANT); objectPrototype = var->findChild(TINYJS_PROTOTYPE_CLASS); + objectPrototype->addChild(TINYJS_CONSTRUCTOR_VAR, var, SCRIPTVARLINK_BUILDINDEFAULT); addNative("function Object.getPrototypeOf(obj)", this, &CTinyJS::native_Object_getPrototypeOf); addNative("function Object.preventExtensions(obj)", this, &CTinyJS::native_Object_setObjectSecure); addNative("function Object.isExtensible(obj)", this, &CTinyJS::native_Object_isSecureObject); @@ -3949,17 +4274,20 @@ CTinyJS::CTinyJS() { // Array var = addNative("function Array()", this, &CTinyJS::native_Array, 0, SCRIPTVARLINK_CONSTANT); arrayPrototype = var->findChild(TINYJS_PROTOTYPE_CLASS); - arrayPrototype->addChild("valueOf", objectPrototype_valueOf); - arrayPrototype->addChild("toString", objectPrototype_toString); + arrayPrototype->addChild(TINYJS_CONSTRUCTOR_VAR, var, SCRIPTVARLINK_BUILDINDEFAULT); + arrayPrototype->addChild("valueOf", objectPrototype_valueOf, SCRIPTVARLINK_BUILDINDEFAULT); + arrayPrototype->addChild("toString", objectPrototype_toString, SCRIPTVARLINK_BUILDINDEFAULT); pseudo_refered.push_back(&arrayPrototype); var = addNative("function Array.__constructor__()", this, &CTinyJS::native_Array, (void*)1, SCRIPTVARLINK_CONSTANT); var->getFunctionData()->name = "Array"; + ////////////////////////////////////////////////////////////////////////// // String var = addNative("function String()", this, &CTinyJS::native_String, 0, SCRIPTVARLINK_CONSTANT); stringPrototype = var->findChild(TINYJS_PROTOTYPE_CLASS); - stringPrototype->addChild("valueOf", objectPrototype_valueOf); - stringPrototype->addChild("toString", objectPrototype_toString); + stringPrototype->addChild(TINYJS_CONSTRUCTOR_VAR, var, SCRIPTVARLINK_BUILDINDEFAULT); + stringPrototype->addChild("valueOf", objectPrototype_valueOf, SCRIPTVARLINK_BUILDINDEFAULT); + stringPrototype->addChild("toString", objectPrototype_toString, SCRIPTVARLINK_BUILDINDEFAULT); pseudo_refered.push_back(&stringPrototype); var = addNative("function String.__constructor__()", this, &CTinyJS::native_String, (void*)1, SCRIPTVARLINK_CONSTANT); var->getFunctionData()->name = "String"; @@ -3969,8 +4297,9 @@ CTinyJS::CTinyJS() { #ifndef NO_REGEXP var = addNative("function RegExp()", this, &CTinyJS::native_RegExp, 0, SCRIPTVARLINK_CONSTANT); regexpPrototype = var->findChild(TINYJS_PROTOTYPE_CLASS); - regexpPrototype->addChild("valueOf", objectPrototype_valueOf); - regexpPrototype->addChild("toString", objectPrototype_toString); + regexpPrototype->addChild(TINYJS_CONSTRUCTOR_VAR, var, SCRIPTVARLINK_BUILDINDEFAULT); + regexpPrototype->addChild("valueOf", objectPrototype_valueOf, SCRIPTVARLINK_BUILDINDEFAULT); + regexpPrototype->addChild("toString", objectPrototype_toString, SCRIPTVARLINK_BUILDINDEFAULT); pseudo_refered.push_back(®expPrototype); #endif /* NO_REGEXP */ @@ -3978,13 +4307,14 @@ CTinyJS::CTinyJS() { // Number var = addNative("function Number()", this, &CTinyJS::native_Number, 0, SCRIPTVARLINK_CONSTANT); var->addChild("NaN", constNaN = newScriptVarNumber(this, NaN), SCRIPTVARLINK_CONSTANT); - var->addChild("MAX_VALUE", constInfinityPositive = newScriptVarNumber(this, numeric_limits::max()), SCRIPTVARLINK_CONSTANT); - var->addChild("MIN_VALUE", constInfinityPositive = newScriptVarNumber(this, numeric_limits::min()), SCRIPTVARLINK_CONSTANT); + var->addChild("MAX_VALUE", newScriptVarNumber(this, numeric_limits::max()), SCRIPTVARLINK_CONSTANT); + var->addChild("MIN_VALUE", newScriptVarNumber(this, numeric_limits::min()), SCRIPTVARLINK_CONSTANT); var->addChild("POSITIVE_INFINITY", constInfinityPositive = newScriptVarNumber(this, InfinityPositive), SCRIPTVARLINK_CONSTANT); var->addChild("NEGATIVE_INFINITY", constInfinityNegative = newScriptVarNumber(this, InfinityNegative), SCRIPTVARLINK_CONSTANT); numberPrototype = var->findChild(TINYJS_PROTOTYPE_CLASS); - numberPrototype->addChild("valueOf", objectPrototype_valueOf); - numberPrototype->addChild("toString", objectPrototype_toString); + numberPrototype->addChild(TINYJS_CONSTRUCTOR_VAR, var, SCRIPTVARLINK_BUILDINDEFAULT); + numberPrototype->addChild("valueOf", objectPrototype_valueOf, SCRIPTVARLINK_BUILDINDEFAULT); + numberPrototype->addChild("toString", objectPrototype_toString, SCRIPTVARLINK_BUILDINDEFAULT); pseudo_refered.push_back(&numberPrototype); pseudo_refered.push_back(&constNaN); pseudo_refered.push_back(&constInfinityPositive); @@ -3996,27 +4326,49 @@ CTinyJS::CTinyJS() { // Boolean var = addNative("function Boolean()", this, &CTinyJS::native_Boolean, 0, SCRIPTVARLINK_CONSTANT); booleanPrototype = var->findChild(TINYJS_PROTOTYPE_CLASS); - booleanPrototype->addChild("valueOf", objectPrototype_valueOf); - booleanPrototype->addChild("toString", objectPrototype_toString); + booleanPrototype->addChild(TINYJS_CONSTRUCTOR_VAR, var, SCRIPTVARLINK_BUILDINDEFAULT); + booleanPrototype->addChild("valueOf", objectPrototype_valueOf, SCRIPTVARLINK_BUILDINDEFAULT); + booleanPrototype->addChild("toString", objectPrototype_toString, SCRIPTVARLINK_BUILDINDEFAULT); pseudo_refered.push_back(&booleanPrototype); var = addNative("function Boolean.__constructor__()", this, &CTinyJS::native_Boolean, (void*)1, SCRIPTVARLINK_CONSTANT); var->getFunctionData()->name = "Boolean"; + ////////////////////////////////////////////////////////////////////////// + // Iterator + var = addNative("function Iterator(obj,mode)", this, &CTinyJS::native_Iterator, 0, SCRIPTVARLINK_CONSTANT); + iteratorPrototype = var->findChild(TINYJS_PROTOTYPE_CLASS); + iteratorPrototype->addChild(TINYJS_CONSTRUCTOR_VAR, var, SCRIPTVARLINK_BUILDINDEFAULT); + pseudo_refered.push_back(&iteratorPrototype); + + ////////////////////////////////////////////////////////////////////////// + // Generator +// var = addNative("function Iterator(obj,mode)", this, &CTinyJS::native_Iterator, 0, SCRIPTVARLINK_CONSTANT); +#ifndef NO_GENERATORS + generatorPrototype = newScriptVar(Object); + generatorPrototype->addChild("next", ::newScriptVar(this, this, &CTinyJS::native_Generator_prototype_next, (void*)0, "Generator.next"), SCRIPTVARLINK_BUILDINDEFAULT); + generatorPrototype->addChild("send", ::newScriptVar(this, this, &CTinyJS::native_Generator_prototype_next, (void*)1, "Generator.send"), SCRIPTVARLINK_BUILDINDEFAULT); + generatorPrototype->addChild("close", ::newScriptVar(this, this, &CTinyJS::native_Generator_prototype_next, (void*)2, "Generator.close"), SCRIPTVARLINK_BUILDINDEFAULT); + generatorPrototype->addChild("throw", ::newScriptVar(this, this, &CTinyJS::native_Generator_prototype_next, (void*)3, "Generator.throw"), SCRIPTVARLINK_BUILDINDEFAULT); + pseudo_refered.push_back(&generatorPrototype); +#endif /*NO_GENERATORS*/ + ////////////////////////////////////////////////////////////////////////// // Function var = addNative("function Function(params, body)", this, &CTinyJS::native_Function, 0, SCRIPTVARLINK_CONSTANT); var->addChildOrReplace(TINYJS_PROTOTYPE_CLASS, functionPrototype); + functionPrototype->addChild(TINYJS_CONSTRUCTOR_VAR, var, SCRIPTVARLINK_BUILDINDEFAULT); addNative("function Function.prototype.call(objc)", this, &CTinyJS::native_Function_prototype_call); addNative("function Function.prototype.apply(objc, args)", this, &CTinyJS::native_Function_prototype_apply); addNative("function Function.prototype.bind(objc, args)", this, &CTinyJS::native_Function_prototype_bind); - functionPrototype->addChild("valueOf", objectPrototype_valueOf); - functionPrototype->addChild("toString", objectPrototype_toString); + functionPrototype->addChild("valueOf", objectPrototype_valueOf, SCRIPTVARLINK_BUILDINDEFAULT); + functionPrototype->addChild("toString", objectPrototype_toString, SCRIPTVARLINK_BUILDINDEFAULT); pseudo_refered.push_back(&functionPrototype); ////////////////////////////////////////////////////////////////////////// // Error var = addNative("function Error(message, fileName, lineNumber, column)", this, &CTinyJS::native_Error, 0, SCRIPTVARLINK_CONSTANT); errorPrototypes[Error] = var->findChild(TINYJS_PROTOTYPE_CLASS); + errorPrototypes[Error]->addChild(TINYJS_CONSTRUCTOR_VAR, var, SCRIPTVARLINK_BUILDINDEFAULT); errorPrototypes[Error]->addChild("message", newScriptVar("")); errorPrototypes[Error]->addChild("name", newScriptVar("Error")); errorPrototypes[Error]->addChild("fileName", newScriptVar("")); @@ -4025,29 +4377,39 @@ CTinyJS::CTinyJS() { var = addNative("function EvalError(message, fileName, lineNumber, column)", this, &CTinyJS::native_EvalError, 0, SCRIPTVARLINK_CONSTANT); errorPrototypes[EvalError] = var->findChild(TINYJS_PROTOTYPE_CLASS); + errorPrototypes[EvalError]->addChild(TINYJS_CONSTRUCTOR_VAR, var, SCRIPTVARLINK_BUILDINDEFAULT); errorPrototypes[EvalError]->addChildOrReplace(TINYJS___PROTO___VAR, errorPrototypes[Error], SCRIPTVARLINK_WRITABLE); errorPrototypes[EvalError]->addChild("name", newScriptVar("EvalError")); var = addNative("function RangeError(message, fileName, lineNumber, column)", this, &CTinyJS::native_RangeError, 0, SCRIPTVARLINK_CONSTANT); errorPrototypes[RangeError] = var->findChild(TINYJS_PROTOTYPE_CLASS); + errorPrototypes[RangeError]->addChild(TINYJS_CONSTRUCTOR_VAR, var, SCRIPTVARLINK_BUILDINDEFAULT); errorPrototypes[RangeError]->addChildOrReplace(TINYJS___PROTO___VAR, errorPrototypes[Error], SCRIPTVARLINK_WRITABLE); errorPrototypes[RangeError]->addChild("name", newScriptVar("RangeError")); var = addNative("function ReferenceError(message, fileName, lineNumber, column)", this, &CTinyJS::native_ReferenceError, 0, SCRIPTVARLINK_CONSTANT); errorPrototypes[ReferenceError] = var->findChild(TINYJS_PROTOTYPE_CLASS); + errorPrototypes[ReferenceError]->addChild(TINYJS_CONSTRUCTOR_VAR, var, SCRIPTVARLINK_BUILDINDEFAULT); errorPrototypes[ReferenceError]->addChildOrReplace(TINYJS___PROTO___VAR, errorPrototypes[Error], SCRIPTVARLINK_WRITABLE); errorPrototypes[ReferenceError]->addChild("name", newScriptVar("ReferenceError")); var = addNative("function SyntaxError(message, fileName, lineNumber, column)", this, &CTinyJS::native_SyntaxError, 0, SCRIPTVARLINK_CONSTANT); errorPrototypes[SyntaxError] = var->findChild(TINYJS_PROTOTYPE_CLASS); + errorPrototypes[SyntaxError]->addChild(TINYJS_CONSTRUCTOR_VAR, var, SCRIPTVARLINK_BUILDINDEFAULT); errorPrototypes[SyntaxError]->addChildOrReplace(TINYJS___PROTO___VAR, errorPrototypes[Error], SCRIPTVARLINK_WRITABLE); errorPrototypes[SyntaxError]->addChild("name", newScriptVar("SyntaxError")); var = addNative("function TypeError(message, fileName, lineNumber, column)", this, &CTinyJS::native_TypeError, 0, SCRIPTVARLINK_CONSTANT); errorPrototypes[TypeError] = var->findChild(TINYJS_PROTOTYPE_CLASS); + errorPrototypes[TypeError]->addChild(TINYJS_CONSTRUCTOR_VAR, var, SCRIPTVARLINK_BUILDINDEFAULT); errorPrototypes[TypeError]->addChildOrReplace(TINYJS___PROTO___VAR, errorPrototypes[Error], SCRIPTVARLINK_WRITABLE); errorPrototypes[TypeError]->addChild("name", newScriptVar("TypeError")); + + + + + ////////////////////////////////////////////////////////////////////////// // add global built-in vars & constants root->addChild("undefined", constUndefined = newScriptVarUndefined(this), SCRIPTVARLINK_CONSTANT); @@ -4055,18 +4417,23 @@ CTinyJS::CTinyJS() { constNull = newScriptVarNull(this); pseudo_refered.push_back(&constNull); root->addChild("NaN", constNaN, SCRIPTVARLINK_CONSTANT); root->addChild("Infinity", constInfinityPositive, SCRIPTVARLINK_CONSTANT); + root->addChild("StopIteration", constStopIteration=newScriptVar(Object, var=newScriptVar(Object), "StopIteration"), SCRIPTVARLINK_CONSTANT); + constStopIteration->addChild(TINYJS_PROTOTYPE_CLASS, var, SCRIPTVARLINK_CONSTANT); pseudo_refered.push_back(&constStopIteration); constNegativZero = newScriptVarNumber(this, NegativeZero); pseudo_refered.push_back(&constNegativZero); constFalse = newScriptVarBool(this, false); pseudo_refered.push_back(&constFalse); constTrue = newScriptVarBool(this, true); pseudo_refered.push_back(&constTrue); + ////////////////////////////////////////////////////////////////////////// // add global functions addNative("function eval(jsCode)", this, &CTinyJS::native_eval); + native_require_read = 0; + addNative("function require(jsFile)", this, &CTinyJS::native_require); addNative("function isNaN(objc)", this, &CTinyJS::native_isNAN); addNative("function isFinite(objc)", this, &CTinyJS::native_isFinite); addNative("function parseInt(string, radix)", this, &CTinyJS::native_parseInt); addNative("function parseFloat(string)", this, &CTinyJS::native_parseFloat); - + root->addChild("JSON", newScriptVar(Object), SCRIPTVARLINK_BUILDINDEFAULT); addNative("function JSON.parse(text, reviver)", this, &CTinyJS::native_JSON_parse); _registerFunctions(this); @@ -4097,11 +4464,9 @@ CTinyJS::~CTinyJS() { /// throws an Error & Exception ////////////////////////////////////////////////////////////////////////// -void CTinyJS::throwError(bool &execute, ERROR_TYPES ErrorType, const string &message ) { - if(execute && (runtimeFlags & RUNTIME_CAN_THROW)) { - exceptionVar = newScriptVarError(this, ErrorType, message.c_str(), t->currentFile.c_str(), t->currentLine(), t->currentColumn()); - runtimeFlags |= RUNTIME_THROW; - execute = false; +void CTinyJS::throwError(CScriptResult &execute, ERROR_TYPES ErrorType, const string &message ) { + if(execute && haveTry) { + execute.set(CScriptResult::Throw, newScriptVarError(this, ErrorType, message.c_str(), t->currentFile.c_str(), t->currentLine(), t->currentColumn())); return; } throw new CScriptException(ErrorType, message, t->currentFile, t->currentLine(), t->currentColumn()); @@ -4110,11 +4475,9 @@ void CTinyJS::throwException(ERROR_TYPES ErrorType, const string &message ) { throw new CScriptException(ErrorType, message, t->currentFile, t->currentLine(), t->currentColumn()); } -void CTinyJS::throwError(bool &execute, ERROR_TYPES ErrorType, const string &message, CScriptTokenizer::ScriptTokenPosition &Pos ){ - if(execute && (runtimeFlags & RUNTIME_CAN_THROW)) { - exceptionVar = newScriptVarError(this, ErrorType, message.c_str(), t->currentFile.c_str(), Pos.currentLine(), Pos.currentColumn()); - runtimeFlags |= RUNTIME_THROW; - execute = false; +void CTinyJS::throwError(CScriptResult &execute, ERROR_TYPES ErrorType, const string &message, CScriptTokenizer::ScriptTokenPosition &Pos ){ + if(execute && haveTry) { + execute.set(CScriptResult::Throw, newScriptVarError(this, ErrorType, message.c_str(), t->currentFile.c_str(), Pos.currentLine(), Pos.currentColumn())); return; } throw new CScriptException(ErrorType, message, t->currentFile, Pos.currentLine(), Pos.currentColumn()); @@ -4142,35 +4505,33 @@ void CTinyJS::execute(const string &Code, const string &File, int Line, int Colu } CScriptVarLinkPtr CTinyJS::evaluateComplex(CScriptTokenizer &Tokenizer) { - CScriptVarLinkPtr v; t = &Tokenizer; + CScriptResult execute; try { - bool execute = true; do { - v = execute_statement(execute); + execute_statement(execute); while (t->tk==';') t->match(';'); // skip empty statements } while (t->tk!=LEX_EOF); } catch (...) { - runtimeFlags = 0; // clean up runtimeFlags + haveTry = false; t=0; // clean up Tokenizer throw; // } t=0; - - ClearUnreferedVars(v); + ClearUnreferedVars(execute.value); - uint32_t UniqueID = getUniqueID(); + uint32_t UniqueID = allocUniqueID(); setTemporaryID_recursive(UniqueID); - if(v) v->getVarPtr()->setTemporaryID_recursive(UniqueID); + if(execute.value) execute.value->setTemporaryMark_recursive(UniqueID); for(CScriptVar *p = first; p; p=p->next) { - if(p->temporaryID != UniqueID) - printf("%p\n", p); + if(p->getTemporaryMark() != UniqueID) + printf("%s %p\n", p->getVarType().c_str(), p); } + freeUniqueID(); - if (v) { - return CScriptVarLinkPtr(v->getVarPtr()); - } + if (execute.value) + return CScriptVarLinkPtr(execute.value); // return undefined... return CScriptVarLinkPtr(constScriptVar(Undefined)); } @@ -4238,7 +4599,7 @@ CScriptVarLinkWorkPtr CTinyJS::parseFunctionDefinition(const CScriptToken &FncTo CScriptVarLinkWorkPtr funcVar(newScriptVar((CScriptTokenDataFnc*)&Fnc), Fnc.name); if(scope() != root) funcVar->getVarPtr()->addChild(TINYJS_FUNCTION_CLOSURE_VAR, scope(), 0); - funcVar->getVarPtr()->addChild(TINYJS_PROTOTYPE_CLASS, newScriptVar(Object), SCRIPTVARLINK_WRITABLE); + funcVar->getVarPtr()->addChild(TINYJS_PROTOTYPE_CLASS, newScriptVar(Object), SCRIPTVARLINK_WRITABLE)->getVarPtr()->addChild(TINYJS_CONSTRUCTOR_VAR, funcVar->getVarPtr(), SCRIPTVARLINK_WRITABLE); return funcVar; } @@ -4248,13 +4609,13 @@ CScriptVarLinkWorkPtr CTinyJS::parseFunctionsBodyFromString(const string &Argume return parseFunctionDefinition(tokenizer.getToken()); } CScriptVarPtr CTinyJS::callFunction(const CScriptVarFunctionPtr &Function, vector &Arguments, const CScriptVarPtr &This, CScriptVarPtr *newThis) { - bool execute=true; + CScriptResult execute; CScriptVarPtr retVar = callFunction(execute, Function, Arguments, This, newThis); - if(!execute) throw exceptionVar; + execute.cThrow(); return retVar; } -CScriptVarPtr CTinyJS::callFunction(bool &execute, const CScriptVarFunctionPtr &Function, vector &Arguments, const CScriptVarPtr &This, CScriptVarPtr *newThis) { +CScriptVarPtr CTinyJS::callFunction(CScriptResult &execute, const CScriptVarFunctionPtr &Function, vector &Arguments, const CScriptVarPtr &This, CScriptVarPtr *newThis) { ASSERT(Function && Function->isFunction()); if(Function->isBounded()) return CScriptVarFunctionBoundedPtr(Function)->callFunction(execute, Arguments, This, newThis); @@ -4265,6 +4626,7 @@ CScriptVarPtr CTinyJS::callFunction(bool &execute, const CScriptVarFunctionPtr & functionRoot->addChild("this", This); CScriptVarPtr arguments = functionRoot->addChild(TINYJS_ARGUMENTS_VAR, newScriptVar(Object)); + CScriptResult function_execute; int length_proto = Fnc->arguments.size(); int length_arguments = Arguments.size(); int length = max(length_proto, length_arguments); @@ -4280,29 +4642,17 @@ CScriptVarPtr CTinyJS::callFunction(bool &execute, const CScriptVarFunctionPtr & CScriptToken &FncArguments = Fnc->arguments[arguments_idx]; if(FncArguments.token == LEX_ID) functionRoot->addChildOrReplace(FncArguments.String(), value); - else { - ASSERT(FncArguments.DestructuringVar().vars.size()>1); - vector Path(1, value); - for(DESTRUCTURING_VARS_it it=FncArguments.DestructuringVar().vars.begin()+1; it!=FncArguments.DestructuringVar().vars.end(); ++it) { - if(it->second == "}" || it->second == "]") - Path.pop_back(); - else { - if(it->second.empty()) continue; // skip empty entries - CScriptVarLinkWorkPtr var = Path.back()->findChild(it->first); - if(var) var = var.getter(execute); else var = constUndefined; - if(!execute) return constUndefined; - if(it->second == "{" || it->second == "[") { - Path.push_back(var); - } else - functionRoot->addChildOrReplace(it->second, var); - } - } - } + else + assign_destructuring_var(functionRoot, FncArguments.DestructuringVar(), value, function_execute); } } arguments->addChild("length", newScriptVar(length_arguments)); - CScriptVarLinkWorkPtr returnVar; +#ifndef NO_GENERATORS + if(Fnc->isGenerator) { + return ::newScriptVarCScriptVarGenerator(this, functionRoot, Function); + } +#endif /*NO_GENERATORS*/ // execute function! // add the function's execute space to the symbol table so we can recurse CScopeControl ScopeControl(this); @@ -4310,15 +4660,18 @@ CScriptVarPtr CTinyJS::callFunction(bool &execute, const CScriptVarFunctionPtr & if (Function->isNative()) { try { CScriptVarFunctionNativePtr(Function)->callFunction(functionRoot); - if(runtimeFlags & RUNTIME_THROW) - execute = false; + CScriptVarLinkPtr ret = functionRoot->findChild(TINYJS_RETURN_VAR); + function_execute.set(CScriptResult::Return, ret ? CScriptVarPtr(ret) : constUndefined); } catch (CScriptVarPtr v) { - if(runtimeFlags & RUNTIME_CAN_THROW) { - runtimeFlags |= RUNTIME_THROW; - execute = false; - exceptionVar = v; - } else - throw new CScriptException(SyntaxError, v->toString(execute)+"' in: native function '"+Function->getFunctionData()->name+"'"); + if(haveTry) { + function_execute.setThrow(v, "native function '"+Fnc->name+"'"); + } else if(v->isError()) { + CScriptException *err = CScriptVarErrorPtr(v)->toCScriptException(); + if(err->fileName.empty()) err->fileName = "native function '"+Fnc->name+"'"; + throw err; + } + else + throw new CScriptException(Error, "uncaught exception: '"+v->toString(function_execute)+"' in native function '"+Fnc->name+"'"); } } else { /* we just want to execute the block, but something could @@ -4328,26 +4681,195 @@ CScriptVarPtr CTinyJS::callFunction(bool &execute, const CScriptVarFunctionPtr & t->currentFile = Fnc->file; t->pushTokenScope(Fnc->body); if(Fnc->body.front().token == '{') - execute_block(execute, false); - else - functionRoot->addChildOrReplace(TINYJS_RETURN_VAR, execute_base(execute)); + execute_block(function_execute); + else { + CScriptVarPtr ret = execute_base(function_execute); + if(function_execute) function_execute.set(CScriptResult::Return, ret); + } t->currentFile = oldFile; // because return will probably have called this, and set execute to false - if(!(runtimeFlags & RUNTIME_THROW)) { - execute = true; + } + if(function_execute.isReturnNormal()) { + if(newThis) *newThis = functionRoot->findChild("this"); + if(function_execute.isReturn()) { + CScriptVarPtr ret = function_execute.value; + return ret; } + } else + execute = function_execute; + return constScriptVar(Undefined); +} +#ifndef NO_GENERATORS +void CTinyJS::generator_start(CScriptVarGenerator *Generator) +{ + // push current Generator + generatorStack.push_back(Generator); + + // safe callers stackBase & set generators one + Generator->callersStackBase = stackBase; + stackBase = 0; + + // safe callers ScopeSize + Generator->callersScopeSize = scopes.size(); + + // safe callers Tokenizer & set generators one + Generator->callersTokenizer = t; + CScriptTokenizer generatorTokenizer; + t = &generatorTokenizer; + + // safe callers haveTry + Generator->callersHaveTry = haveTry; + haveTry = true; + + // push generator's FunctionRoot + CScopeControl ScopeControl(this); + ScopeControl.addFncScope(Generator->getFunctionRoot()); + + // call generator-function + CScriptTokenDataFnc *Fnc = Generator->getFunction()->getFunctionData(); + CScriptResult function_execute; + TOKEN_VECT eof(1, CScriptToken()); + t->pushTokenScope(eof); + t->pushTokenScope(Fnc->body); + t->currentFile = Fnc->file; + try { + if(Fnc->body.front().token == '{') + execute_block(function_execute); + else { + execute_base(function_execute); + } + if(function_execute.isThrow()) + Generator->setException(function_execute.value); + else + Generator->setException(constStopIteration); +// } catch(CScriptVarPtr &e) { +// Generator->setException(e); + } catch(CScriptCoroutine::StopIteration_t &) { + Generator->setException(CScriptVarPtr()); +// } catch(CScriptException *e) { +// Generator->setException(newScriptVarError(this, *e)); + } catch(...) { + // pop current Generator + generatorStack.pop_back(); + + // restore callers stackBase + stackBase = Generator->callersStackBase; + + // restore callers Scope (restored by ScopeControl + // scopes.erase(scopes.begin()+Generator->callersScopeSize, scopes.end()); + + // restore callers Tokenizer + t = Generator->callersTokenizer; + + // restore callers haveTry + haveTry = Generator->callersHaveTry; + + Generator->setException(constStopIteration); + // re-throw + throw; } - if(execute && newThis) - *newThis = functionRoot->findChild("this"); - /* get the real return var before we remove it from our function */ - if(execute && (returnVar = functionRoot->findChild(TINYJS_RETURN_VAR))) - return returnVar; - else - return constScriptVar(Undefined); + // pop current Generator + generatorStack.pop_back(); + + // restore callers stackBase + stackBase = Generator->callersStackBase; + + // restore callers Scope (restored by ScopeControl + // scopes.erase(scopes.begin()+Generator->callersScopeSize, scopes.end()); + + // restore callers Tokenizer + t = Generator->callersTokenizer; + + // restore callers haveTry + haveTry = Generator->callersHaveTry; } +CScriptVarPtr CTinyJS::generator_yield(CScriptResult &execute, CScriptVar *YieldIn) +{ + if(!execute) return constUndefined; + CScriptVarGenerator *Generator = generatorStack.back(); + if(Generator->isClosed()) { + throwError(execute, TypeError, "yield from closing generator function"); + return constUndefined; + } + + // pop current Generator + generatorStack.pop_back(); + + // safe generators and restore callers stackBase + void *generatorStckBase = stackBase; + stackBase = Generator->callersStackBase; + + // safe generators and restore callers scopes + Generator->generatorScopes.assign(scopes.begin()+Generator->callersScopeSize, scopes.end()); + scopes.erase(scopes.begin()+Generator->callersScopeSize, scopes.end()); + + // safe generators and restore callers Tokenizer + CScriptTokenizer *generatorTokenizer = t; + t = Generator->callersTokenizer; + + // safe generators and restore callers haveTry + bool generatorsHaveTry = haveTry; + haveTry = Generator->callersHaveTry; + + CScriptVarPtr ret; + try { + ret = Generator->yield(execute, YieldIn); + } catch(...) { + // normaly catch(CScriptCoroutine::CScriptCoroutineFinish_t &) + // force StopIteration with call CScriptCoroutine::Stop() before CScriptCoroutine::next() + // but catch(...) is for paranoia + + // push current Generator + generatorStack.push_back(Generator); + + // safe callers and restore generators stackBase + Generator->callersStackBase = stackBase; + stackBase = generatorStckBase; + + // safe callers and restore generator Scopes + Generator->callersScopeSize = scopes.size(); + scopes.insert(scopes.end(), Generator->generatorScopes.begin(), Generator->generatorScopes.end()); + Generator->generatorScopes.clear(); + + // safe callers and restore generator Tokenizer + Generator->callersTokenizer = t; + t = generatorTokenizer; + + // safe callers and restore generator haveTry + Generator->callersHaveTry = haveTry; + haveTry = generatorsHaveTry; -CScriptVarPtr CTinyJS::mathsOp(bool &execute, const CScriptVarPtr &A, const CScriptVarPtr &B, int op) { + // re-throw + throw; + } + // push current Generator + generatorStack.push_back(Generator); + + // safe callers and restore generators stackBase + Generator->callersStackBase = stackBase; + stackBase = generatorStckBase; + + // safe callers and restore generator Scopes + Generator->callersScopeSize = scopes.size(); + scopes.insert(scopes.end(), Generator->generatorScopes.begin(), Generator->generatorScopes.end()); + Generator->generatorScopes.clear(); + + // safe callers and restore generator Tokenizer + Generator->callersTokenizer = t; + t = generatorTokenizer; + + // safe callers and restore generator haveTry + Generator->callersHaveTry = haveTry; + haveTry = generatorsHaveTry; + + return ret; +} +#endif /*NO_GENERATORS*/ + + + +CScriptVarPtr CTinyJS::mathsOp(CScriptResult &execute, const CScriptVarPtr &A, const CScriptVarPtr &B, int op) { if(!execute) return constUndefined; if (op == LEX_TYPEEQUAL || op == LEX_NTYPEEQUAL) { // check type first @@ -4374,7 +4896,13 @@ CScriptVarPtr CTinyJS::mathsOp(bool &execute, const CScriptVarPtr &A, const CScr string da = a->isNull() ? "" : a->toString(execute); string db = b->isNull() ? "" : b->toString(execute); switch (op) { - case '+': return newScriptVar(da+db); + case '+': + try{ + return newScriptVar(da+db); + } catch(exception& e) { + throwError(execute, Error, e.what()); + return constUndefined; + } case LEX_EQUAL: return constScriptVar(da==db); case LEX_NEQUAL: return constScriptVar(da!=db); case '<': return constScriptVar(daaddChildOrReplace(Objc.vars.front().second, Val); + else { + CScriptVarLinkWorkPtr v(findInScopes(Objc.vars.front().second)); + ASSERT(v==true); + if(v) v->setVarPtr(Val); + } + } else { + vector Path(1, Val); + for(DESTRUCTURING_VARS_cit it=Objc.vars.begin()+1; it!=Objc.vars.end(); ++it) { + if(it->second == "}" || it->second == "]") + Path.pop_back(); + else { + if(it->second.empty()) continue; // skip empty entries + CScriptVarLinkWorkPtr var = Path.back()->findChildWithStringChars(it->first); + if(var) var = var.getter(execute); else var = constUndefined; + if(!execute) return; + if(it->second == "{" || it->second == "[") { + Path.push_back(var); + } else if(Scope) + Scope->addChildOrReplace(it->second, var); + else { + CScriptVarLinkWorkPtr v(findInScopes(it->second)); + ASSERT(v==true); + if(v) v->setVarPtr(var); + } + } + } + } +} + +void CTinyJS::execute_var_init( bool hideLetScope, CScriptResult &execute ) { for(;;) { - if(t->tk == LEX_T_DESTRUCTURING_VAR) { - DESTRUCTURING_VARS_t &vars = t->getToken().DestructuringVar().vars; - t->match(LEX_T_DESTRUCTURING_VAR); + t->check(LEX_T_DESTRUCTURING_VAR); + CScriptTokenDataDestructuringVar &Objc = t->getToken().DestructuringVar(); + t->match(LEX_T_DESTRUCTURING_VAR); + if(t->tk == '=') { t->match('='); if(hideLetScope) CScriptVarScopeLetPtr(scope())->setletExpressionInitMode(true); - vector Path(1, execute_assignment(execute)); + CScriptVarPtr Val = execute_assignment(execute); if(hideLetScope) CScriptVarScopeLetPtr(scope())->setletExpressionInitMode(false); - for(DESTRUCTURING_VARS_it it=vars.begin()+1; execute && it!=vars.end(); ++it) { - if(it->second == "}" || it->second == "]") - Path.pop_back(); - else { - if(it->second.empty()) continue; // skip empty entries - CScriptVarLinkWorkPtr var = Path.back()->findChild(it->first); - if(var) var = var.getter(execute); else var = constUndefined; - if(!execute) break; - if(it->second == "{" || it->second == "[") { - Path.push_back(var); - } else { - CScriptVarLinkWorkPtr v(findInScopes(it->second)); - ASSERT(v==true); - if(v) v.setter(execute, var); - } - } - } - } else { - string name = t->tkStr(); - t->match(LEX_ID); - CScriptVarPtr var; - if (t->tk == '=') { - t->match('='); - CScriptVarLinkWorkPtr v(findInScopes(name)); - ASSERT(v==true); - if(hideLetScope) CScriptVarScopeLetPtr(scope())->setletExpressionInitMode(true); - if(v) v.setter(execute, execute_assignment(execute)); - if(hideLetScope) CScriptVarScopeLetPtr(scope())->setletExpressionInitMode(false); - } + assign_destructuring_var(0, Objc, Val, execute); } if (t->tk == ',') t->match(','); @@ -4465,30 +5000,33 @@ void CTinyJS::execute_var_init( bool hideLetScope, bool &execute ) break; } } -void CTinyJS::execute_destructuring(CScriptTokenDataObjectLiteral &Objc, const CScriptVarPtr &Val, bool &execute) { +void CTinyJS::execute_destructuring(CScriptTokenDataObjectLiteral &Objc, const CScriptVarPtr &Val, CScriptResult &execute) { for(vector::iterator it=Objc.elements.begin(); execute && it!=Objc.elements.end(); ++it) { if(it->value.empty()) continue; - CScriptVarPtr rhs = Val->findChild(it->id).getter(execute); + CScriptVarPtr rhs = Val->findChildWithStringChars(it->id).getter(execute); + if(!rhs) rhs=constUndefined; if(it->value.front().token == LEX_T_OBJECT_LITERAL && it->value.front().Object().destructuring) { execute_destructuring(it->value.front().Object(), rhs, execute); } else { t->pushTokenScope(it->value); CScriptVarLinkWorkPtr lhs = execute_condition(execute); - if (!lhs->isOwned()) { - CScriptVarPtr Owner = lhs.getReferencedOwner(); - if(Owner) { - if(!lhs.getReferencedOwner()->isExtensible()) - continue; - lhs = Owner->addChildOrReplace(lhs->getName(), lhs); - } else - lhs = root->addChildOrReplace(lhs->getName(), lhs); + if(lhs->isWritable()) { + if (!lhs->isOwned()) { + CScriptVarPtr fakedOwner = lhs.getReferencedOwner(); + if(fakedOwner) { + if(!fakedOwner->isExtensible()) + continue; + lhs = fakedOwner->addChildOrReplace(lhs->getName(), lhs); + } else + lhs = root->addChildOrReplace(lhs->getName(), lhs); + } + lhs.setter(execute, rhs); } - lhs.setter(execute, rhs); } } } -CScriptVarLinkWorkPtr CTinyJS::execute_literals(bool &execute) { +CScriptVarLinkWorkPtr CTinyJS::execute_literals(CScriptResult &execute) { switch(t->tk) { case LEX_ID: if(execute) { @@ -4586,10 +5124,10 @@ CScriptVarLinkWorkPtr CTinyJS::execute_literals(bool &execute) { t->match('('); t->check(LEX_T_FORWARD); ScopeControl.addLetScope(); - execute_statement(execute); + execute_statement(execute); // execute forwarder execute_var_init(true, execute); t->match(')'); - return execute_base(execute); + return execute_assignment(execute); } else { t->skip(t->getToken().Int()); } @@ -4602,22 +5140,43 @@ CScriptVarLinkWorkPtr CTinyJS::execute_literals(bool &execute) { } t->match(LEX_T_FUNCTION_OPERATOR); break; +#ifndef NO_GENERATORS + case LEX_R_YIELD: + if (execute) { + t->match(LEX_R_YIELD); + CScriptVarPtr result = constUndefined; + if (t->tk != ';') + result = execute_base(execute); + if(execute) + return generator_yield(execute, result.getVar()); + else + return constUndefined; + } else + t->skip(t->getToken().Int()); + break; +#endif /*NO_GENERATORS*/ case LEX_R_NEW: // new -> create a new object if (execute) { t->match(LEX_R_NEW); CScriptVarLinkWorkPtr parent = execute_literals(execute); CScriptVarLinkWorkPtr objClass = execute_member(parent, execute).getter(execute); if (execute) { - if(objClass->getVarPtr()->isFunction()) { + CScriptVarPtr Constructor = objClass->getVarPtr(); + if(Constructor->isFunction()) { CScriptVarPtr obj(newScriptVar(Object)); - CScriptVarLinkPtr prototype = objClass->getVarPtr()->findChild(TINYJS_PROTOTYPE_CLASS); + CScriptVarLinkPtr prototype = Constructor->findChild(TINYJS_PROTOTYPE_CLASS); if(!prototype || prototype->getVarPtr()->isUndefined() || prototype->getVarPtr()->isNull()) { - prototype = objClass->getVarPtr()->addChild(TINYJS_PROTOTYPE_CLASS, newScriptVar(Object), SCRIPTVARLINK_WRITABLE); + prototype = Constructor->addChild(TINYJS_PROTOTYPE_CLASS, newScriptVar(Object), SCRIPTVARLINK_WRITABLE); obj->addChildOrReplace(TINYJS___PROTO___VAR, prototype, SCRIPTVARLINK_WRITABLE); } - CScriptVarLinkPtr constructor = objClass->getVarPtr()->findChild("__constructor__"); - if(constructor && constructor->getVarPtr()->isFunction()) - objClass = constructor; + CScriptVarLinkPtr __constructor__ = Constructor->findChild("__constructor__"); + if(__constructor__ && __constructor__->getVarPtr()->isFunction()) + Constructor = __constructor__; + if (stackBase) { + int dummy; + if(&dummy < stackBase) + throwError(execute, Error, "too much recursion"); + } vector arguments; if (t->tk == '(') { t->match('('); @@ -4630,7 +5189,7 @@ CScriptVarLinkWorkPtr CTinyJS::execute_literals(bool &execute) { t->match(')'); } if(execute) { - CScriptVarPtr returnVar = callFunction(execute, objClass->getVarPtr(), arguments, obj, &obj); + CScriptVarPtr returnVar = callFunction(execute, Constructor, arguments, obj, &obj); if(returnVar->isObject()) return CScriptVarLinkWorkPtr(returnVar); return CScriptVarLinkWorkPtr(obj); @@ -4660,6 +5219,10 @@ CScriptVarLinkWorkPtr CTinyJS::execute_literals(bool &execute) { } else t->skip(t->getToken().Int()); break; + case LEX_T_EXCEPTION_VAR: + t->match(LEX_T_EXCEPTION_VAR); + if(execute.value) return execute.value; + break; default: t->match(LEX_EOF); break; @@ -4667,7 +5230,7 @@ CScriptVarLinkWorkPtr CTinyJS::execute_literals(bool &execute) { return constScriptVar(Undefined); } -CScriptVarLinkWorkPtr CTinyJS::execute_member(CScriptVarLinkWorkPtr &parent, bool &execute) { +CScriptVarLinkWorkPtr CTinyJS::execute_member(CScriptVarLinkWorkPtr &parent, CScriptResult &execute) { CScriptVarLinkWorkPtr a; parent.swap(a); if(t->tk == '.' || t->tk == '[') { @@ -4685,7 +5248,7 @@ CScriptVarLinkWorkPtr CTinyJS::execute_member(CScriptVarLinkWorkPtr &parent, boo } else { if(execute) { t->match('['); - name = execute_expression(execute)->toString(execute); + name = execute_base(execute)->toString(execute); t->match(']'); } else t->skip(t->getToken().Int()); @@ -4703,16 +5266,22 @@ CScriptVarLinkWorkPtr CTinyJS::execute_member(CScriptVarLinkWorkPtr &parent, boo return a; } -CScriptVarLinkWorkPtr CTinyJS::execute_function_call(bool &execute) { +CScriptVarLinkWorkPtr CTinyJS::execute_function_call(CScriptResult &execute) { CScriptVarLinkWorkPtr parent = execute_literals(execute); CScriptVarLinkWorkPtr a = execute_member(parent, execute); while (t->tk == '(') { if (execute) { if(a->getVarPtr()->isUndefined() || a->getVarPtr()->isNull()) throwError(execute, ReferenceError, a->getName() + " is " + a->toString(execute)); - CScriptVarLinkWorkPtr fnc = a.getter(execute); - if (!fnc->getVarPtr()->isFunction()) + CScriptVarPtr fnc = a.getter(execute)->getVarPtr(); + if (!fnc->isFunction()) throwError(execute, TypeError, a->getName() + " is not a function"); + if (stackBase) { + int dummy; + if(&dummy < stackBase) + throwError(execute, Error, "too much recursion"); + } + t->match('('); // path += '('; // grab in all parameters @@ -4733,7 +5302,7 @@ CScriptVarLinkWorkPtr CTinyJS::execute_function_call(bool &execute) { parent = findInScopes("this"); // if no parent use the root-scope CScriptVarPtr This(parent ? parent->getVarPtr() : (CScriptVarPtr )root); - a = callFunction(execute, a->getVarPtr(), arguments, This); + a = callFunction(execute, fnc, arguments, This); } } else { // function, but not executing - just parse args and be done @@ -4750,13 +5319,13 @@ CScriptVarLinkWorkPtr CTinyJS::execute_function_call(bool &execute) { } // R->L: Precedence 3 (in-/decrement) ++ -- // R<-L: Precedence 4 (unary) ! ~ + - typeof void delete -bool CTinyJS::execute_unary_rhs(bool &execute, CScriptVarLinkWorkPtr& a) { +bool CTinyJS::execute_unary_rhs(CScriptResult &execute, CScriptVarLinkWorkPtr& a) { t->match(t->tk); a = execute_unary(execute).getter(execute); if(execute) CheckRightHandVar(execute, a); return execute; }; -CScriptVarLinkWorkPtr CTinyJS::execute_unary(bool &execute) { +CScriptVarLinkWorkPtr CTinyJS::execute_unary(CScriptResult &execute) { CScriptVarLinkWorkPtr a; switch(t->tk) { case '-': @@ -4788,7 +5357,7 @@ CScriptVarLinkWorkPtr CTinyJS::execute_unary(bool &execute) { a = execute_unary(execute); // no getter - delete can remove the accessor if (execute) { // !!! no right-hand-check by delete - if(a->isOwned() && a->isConfigurable()) { + if(a->isOwned() && a->isConfigurable() && a->getName() != "this") { a->getOwner()->removeLink(a); // removes the link from owner a = constScriptVar(true); } @@ -4847,7 +5416,7 @@ CScriptVarLinkWorkPtr CTinyJS::execute_unary(bool &execute) { } // L->R: Precedence 5 (term) * / % -CScriptVarLinkWorkPtr CTinyJS::execute_term(bool &execute) { +CScriptVarLinkWorkPtr CTinyJS::execute_term(CScriptResult &execute) { CScriptVarLinkWorkPtr a = execute_unary(execute); if (t->tk=='*' || t->tk=='/' || t->tk=='%') { CheckRightHandVar(execute, a); @@ -4865,7 +5434,7 @@ CScriptVarLinkWorkPtr CTinyJS::execute_term(bool &execute) { } // L->R: Precedence 6 (addition/subtraction) + - -CScriptVarLinkWorkPtr CTinyJS::execute_expression(bool &execute) { +CScriptVarLinkWorkPtr CTinyJS::execute_expression(CScriptResult &execute) { CScriptVarLinkWorkPtr a = execute_term(execute); if (t->tk=='+' || t->tk=='-') { CheckRightHandVar(execute, a); @@ -4883,7 +5452,7 @@ CScriptVarLinkWorkPtr CTinyJS::execute_expression(bool &execute) { } // L->R: Precedence 7 (bitwise shift) << >> >>> -CScriptVarLinkWorkPtr CTinyJS::execute_binary_shift(bool &execute) { +CScriptVarLinkWorkPtr CTinyJS::execute_binary_shift(CScriptResult &execute) { CScriptVarLinkWorkPtr a = execute_expression(execute); if (t->tk==LEX_LSHIFT || t->tk==LEX_RSHIFT || t->tk==LEX_RSHIFTU) { CheckRightHandVar(execute, a); @@ -4903,7 +5472,7 @@ CScriptVarLinkWorkPtr CTinyJS::execute_binary_shift(bool &execute) { } // L->R: Precedence 8 (relational) < <= > <= in instanceof // L->R: Precedence 9 (equality) == != === !=== -CScriptVarLinkWorkPtr CTinyJS::execute_relation(bool &execute, int set, int set_n) { +CScriptVarLinkWorkPtr CTinyJS::execute_relation(CScriptResult &execute, int set, int set_n) { CScriptVarLinkWorkPtr a = set_n ? execute_relation(execute, set_n, 0) : execute_binary_shift(execute); if ((set==LEX_EQUAL && t->tk>=LEX_RELATIONS_1_BEGIN && t->tk<=LEX_RELATIONS_1_END) || (set=='<' && (t->tk==LEX_LEQUAL || t->tk==LEX_GEQUAL || t->tk=='<' || t->tk=='>' || t->tk == LEX_R_IN || t->tk == LEX_R_INSTANCEOF))) { @@ -4919,18 +5488,21 @@ CScriptVarLinkWorkPtr CTinyJS::execute_relation(bool &execute, int set, int set_ string nameOf_b = b->getName(); b = b.getter(execute); if(op == LEX_R_IN) { + if(!b->getVarPtr()->isObject()) + throwError(execute, TypeError, "invalid 'in' operand "+nameOf_b); a(constScriptVar( (bool)b->getVarPtr()->findChildWithPrototypeChain(a->toString(execute)))); } else if(op == LEX_R_INSTANCEOF) { CScriptVarLinkPtr prototype = b->getVarPtr()->findChild(TINYJS_PROTOTYPE_CLASS); if(!prototype) throwError(execute, TypeError, "invalid 'instanceof' operand "+nameOf_b); else { - unsigned int uniqueID = getUniqueID(); + unsigned int uniqueID = allocUniqueID(); CScriptVarPtr object = a->getVarPtr()->findChild(TINYJS___PROTO___VAR); - while( object && object!=prototype->getVarPtr() && object->getTempraryID() != uniqueID) { - object->setTemporaryID(uniqueID); // prevents recursions + while( object && object!=prototype->getVarPtr() && object->getTemporaryMark() != uniqueID) { + object->setTemporaryMark(uniqueID); // prevents recursions object = object->findChild(TINYJS___PROTO___VAR); } + freeUniqueID(); a(constScriptVar(object && object==prototype->getVarPtr())); } } else @@ -4944,7 +5516,7 @@ CScriptVarLinkWorkPtr CTinyJS::execute_relation(bool &execute, int set, int set_ // L->R: Precedence 10 (bitwise-and) & // L->R: Precedence 11 (bitwise-xor) ^ // L->R: Precedence 12 (bitwise-or) | -CScriptVarLinkWorkPtr CTinyJS::execute_binary_logic(bool &execute, int op, int op_n1, int op_n2) { +CScriptVarLinkWorkPtr CTinyJS::execute_binary_logic(CScriptResult &execute, int op, int op_n1, int op_n2) { CScriptVarLinkWorkPtr a = op_n1 ? execute_binary_logic(execute, op_n1, op_n2, 0) : execute_relation(execute); if (t->tk==op) { CheckRightHandVar(execute, a); @@ -4962,47 +5534,51 @@ CScriptVarLinkWorkPtr CTinyJS::execute_binary_logic(bool &execute, int op, int o } // L->R: Precedence 13 ==> (logical-and) && // L->R: Precedence 14 ==> (logical-or) || -CScriptVarLinkWorkPtr CTinyJS::execute_logic(bool &execute, int op /*= LEX_OROR*/, int op_n /*= LEX_ANDAND*/) { +CScriptVarLinkWorkPtr CTinyJS::execute_logic(CScriptResult &execute, int op /*= LEX_OROR*/, int op_n /*= LEX_ANDAND*/) { CScriptVarLinkWorkPtr a = op_n ? execute_logic(execute, op_n, 0) : execute_binary_logic(execute); if (t->tk==op) { if(execute) { + CScriptVarLinkWorkPtr b; CheckRightHandVar(execute, a); a(a.getter(execute)); // rebuild a - CScriptVarLinkWorkPtr b; - bool result_bool = a->toBoolean(); - bool shortCircuit = false; - while (t->tk==op) { - t->match(t->tk); - shortCircuit = (op==LEX_ANDAND) ? !result_bool : result_bool; - b = op_n ? execute_logic(shortCircuit ? noexecute : execute, op_n, 0) : execute_binary_logic(shortCircuit ? noexecute : execute); // L->R - if (execute && !shortCircuit) { - CheckRightHandVar(execute, b); - a(b.getter(execute)); // rebuild a - result_bool = a->toBoolean(); - } - } - return a; + do { + if(execute && (op==LEX_ANDAND ? a->toBoolean() : !a->toBoolean())) { + t->match(t->tk); + b = op_n ? execute_logic(execute, op_n, 0) : execute_binary_logic(execute); + CheckRightHandVar(execute, b); a(b.getter(execute)); // rebuild a + } else + t->skip(t->getToken().Int()); + } while(t->tk==op); } else - op_n ? execute_logic(execute, op_n, 0) : execute_binary_logic(execute); // L->R - } - return a; -} + t->skip(t->getToken().Int()); + } + return a; +} // L<-R: Precedence 15 (condition) ?: -CScriptVarLinkWorkPtr CTinyJS::execute_condition(bool &execute) -{ +CScriptVarLinkWorkPtr CTinyJS::execute_condition(CScriptResult &execute) { CScriptVarLinkWorkPtr a = execute_logic(execute); - if (t->tk=='?') - { + if (t->tk=='?') { CheckRightHandVar(execute, a); - t->match('?'); bool cond = execute && a.getter(execute)->toBoolean(); - CScriptVarLinkWorkPtr b; - a = execute_condition(cond ? execute : noexecute ); // L<-R - t->match(':'); - b = execute_condition(cond ? noexecute : execute); // R-L - if(!cond) - return b; + if(execute) { + if(cond) { + t->match('?'); +// a = execute_condition(execute); + a = execute_assignment(execute); + t->check(':'); + t->skip(t->getToken().Int()); + } else { + CScriptVarLinkWorkPtr b; + t->skip(t->getToken().Int()); + t->match(':'); + return execute_assignment(execute); +// return execute_condition(execute); + } + } else { + t->skip(t->getToken().Int()); + t->skip(t->getToken().Int()); + } } return a; } @@ -5010,10 +5586,10 @@ CScriptVarLinkWorkPtr CTinyJS::execute_condition(bool &execute) // L<-R: Precedence 16 (assignment) = += -= *= /= %= <<= >>= >>>= &= |= ^= // now we can return CScriptVarLinkPtr execute_assignment returns always no setters/getters // force life of the Owner is no more needed -CScriptVarLinkPtr CTinyJS::execute_assignment(bool &execute) { +CScriptVarLinkPtr CTinyJS::execute_assignment(CScriptResult &execute) { return execute_assignment(execute_condition(execute), execute); } -CScriptVarLinkPtr CTinyJS::execute_assignment(CScriptVarLinkWorkPtr lhs, bool &execute) { +CScriptVarLinkPtr CTinyJS::execute_assignment(CScriptVarLinkWorkPtr lhs, CScriptResult &execute) { if (t->tk=='=' || (t->tk>=LEX_ASSIGNMENTS_BEGIN && t->tk<=LEX_ASSIGNMENTS_END) ) { int op = t->tk; CScriptTokenizer::ScriptTokenPosition leftHandPos = t->getPos(); @@ -5056,7 +5632,7 @@ CScriptVarLinkPtr CTinyJS::execute_assignment(CScriptVarLinkWorkPtr lhs, bool &e return lhs.getter(execute); } // L->R: Precedence 17 (comma) , -CScriptVarLinkPtr CTinyJS::execute_base(bool &execute) { +CScriptVarLinkPtr CTinyJS::execute_base(CScriptResult &execute) { CScriptVarLinkPtr a; for(;;) { @@ -5068,11 +5644,11 @@ CScriptVarLinkPtr CTinyJS::execute_base(bool &execute) { } return a; } -void CTinyJS::execute_block(bool &execute, bool createLetScope /*=true*/) { +void CTinyJS::execute_block(CScriptResult &execute) { if(execute) { t->match('{'); CScopeControl ScopeControl(this); - if(createLetScope && t->tk==LEX_T_FORWARD && t->getToken().Forwarder().lets.size()) // add a LetScope only if needed + if(t->tk==LEX_T_FORWARD) // add a LetScope only if needed ScopeControl.addLetScope(); while (t->tk && t->tk!='}') execute_statement(execute); @@ -5082,8 +5658,7 @@ void CTinyJS::execute_block(bool &execute, bool createLetScope /*=true*/) { else t->skip(t->getToken().Int()); } -CScriptVarLinkPtr CTinyJS::execute_statement(bool &execute) { - CScriptVarLinkPtr ret; +void CTinyJS::execute_statement(CScriptResult &execute) { switch(t->tk) { case '{': /* A block of code */ execute_block(execute); @@ -5091,21 +5666,16 @@ CScriptVarLinkPtr CTinyJS::execute_statement(bool &execute) { case ';': /* Empty statement - to allow things like ;;; */ t->match(';'); break; - case LEX_ID: - ret = execute_base(execute); - t->match(';'); - break; case LEX_T_FORWARD: { CScriptVarPtr in_scope = scope()->scopeLet(); - STRING_SET_t &vars = t->getToken().Forwarder().lets; - for(int i=0; i<2; ++i) { - for(STRING_SET_it it=vars.begin(); it!=vars.end(); ++it) { + STRING_SET_t *varNames = t->getToken().Forwarder().varNames; + for(int i=0; ifindChild(*it); - if(!a) in_scope->addChild(*it, constScriptVar(Undefined), SCRIPTVARLINK_VARDEFAULT); + if(!a) in_scope->addChild(*it, constScriptVar(Undefined), i==CScriptTokenDataForwards::CONSTS ? SCRIPTVARLINK_CONSTDEFAULT : SCRIPTVARLINK_VARDEFAULT); } - in_scope = scope()->scopeVar(); - vars = t->getToken().Forwarder().vars; + in_scope = scope()->scopeVar(); } CScriptTokenDataForwards::FNC_SET_t &functions = t->getToken().Forwarder().functions; for(CScriptTokenDataForwards::FNC_SET_it it=functions.begin(); it!=functions.end(); ++it) { @@ -5117,25 +5687,23 @@ CScriptVarLinkPtr CTinyJS::execute_statement(bool &execute) { break; case LEX_R_VAR: case LEX_R_LET: + case LEX_R_CONST: if(execute) { CScopeControl ScopeControl(this); - bool let = t->tk==LEX_R_LET, let_ext=false; + bool isLet = t->tk==LEX_R_LET, let_ext=false; t->match(t->tk); - if(let && t->tk=='(') { + if(isLet && t->tk=='(') { let_ext = true; t->match('('); t->check(LEX_T_FORWARD); ScopeControl.addLetScope(); - execute_statement(execute); + execute_statement(execute); // forwarder } execute_var_init(let_ext, execute); if(let_ext) { t->match(')'); - if(t->tk == '{') - execute_block(execute, false); - else - execute_statement(execute); + execute_statement(execute); } else t->match(';'); } else @@ -5149,7 +5717,7 @@ CScriptVarLinkPtr CTinyJS::execute_statement(bool &execute) { t->match(')'); CScopeControl ScopeControl(this); ScopeControl.addWithScope(var); - ret = execute_statement(execute); + execute_statement(execute); } else t->skip(t->getToken().Int()); break; @@ -5177,259 +5745,136 @@ CScriptVarLinkPtr CTinyJS::execute_statement(bool &execute) { } else t->skip(t->getToken().Int()); break; - case LEX_R_DO: - if(execute) { - t->match(LEX_R_DO); - STRING_VECTOR_t myLabels; - while(t->tk == LEX_T_LOOP_LABEL) { - myLabels.push_back(t->tkStr()); - t->match(LEX_T_LOOP_LABEL); - } - CScriptTokenizer::ScriptTokenPosition loopStart = t->getPos(); - bool loopCond = true; - while (loopCond && execute) { - t->setPos(loopStart); - execute_statement(execute); - if(!execute) - { - bool Continue = false; - if(runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE) - && - (label.empty() || find(myLabels.begin(), myLabels.end(), label) != myLabels.end())) { - label.clear(); - execute = true; - Continue = (runtimeFlags & RUNTIME_CONTINUE) != 0; - runtimeFlags &= ~(RUNTIME_BREAK | RUNTIME_CONTINUE); - } - if(!Continue) { - t->skip(t->getToken().Int()); - break; - } - } - t->match(LEX_R_WHILE); - t->match('('); - loopCond = execute_base(execute)->toBoolean(); - t->match(')'); - t->match(';'); - } - } else - t->skip(t->getToken().Int()); - break; - case LEX_R_WHILE: - if(execute) { - t->match(LEX_R_WHILE); - STRING_VECTOR_t myLabels; - while(t->tk == LEX_T_LOOP_LABEL) { - myLabels.push_back(t->tkStr()); - t->match(LEX_T_LOOP_LABEL); - } - bool loopCond; - t->match('('); - CScriptTokenizer::ScriptTokenPosition condStart = t->getPos(); - loopCond = execute_base(execute)->toBoolean(); - t->match(')'); - if(loopCond && execute) { - t->match(LEX_T_SKIP); - CScriptTokenizer::ScriptTokenPosition loopStart = t->getPos(); - CScriptTokenizer::ScriptTokenPosition loopEnd = loopStart; - while (loopCond && execute) { - t->setPos(loopStart); - execute_statement(execute); - if(loopEnd == loopStart) // first loop-pass - loopEnd = t->getPos(); - if(!execute) { - bool Continue = false; - if(runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE) - && - (label.empty() || find(myLabels.begin(), myLabels.end(), label) != myLabels.end())) { - label.clear(); - execute = true; - Continue = (runtimeFlags & RUNTIME_CONTINUE) != 0; - runtimeFlags &= ~(RUNTIME_BREAK | RUNTIME_CONTINUE); - } - if(!Continue) break; - } - if(execute) { - t->setPos(condStart); - loopCond = execute_base(execute)->toBoolean(); - } - } - t->setPos(loopEnd); - } else { - t->check(LEX_T_SKIP); - t->skip(t->getToken().Int()); - } - } else - t->skip(t->getToken().Int()); - break; case LEX_T_FOR_IN: - case LEX_T_FOR_EACH_IN: - if(execute) { - bool for_each = t->tk == LEX_T_FOR_EACH_IN; - t->match(t->tk); - STRING_VECTOR_t myLabels; - while(t->tk == LEX_T_LOOP_LABEL) { - myLabels.push_back(t->tkStr()); - t->match(LEX_T_LOOP_LABEL); - } - t->match('('); - CScriptVarLinkWorkPtr for_var; - CScriptVarLinkWorkPtr for_in_var; + { + CScriptTokenDataLoop &LoopData = t->getToken().Loop(); + t->match(LEX_T_FOR_IN); + if(!execute) break; CScopeControl ScopeControl(this); - ScopeControl.addLetScope(); - - if(t->tk == LEX_R_LET) { - t->match(LEX_R_LET); - string var = t->tkStr(); - t->match(LEX_ID); - for_var = scope()->scopeLet()->findChildOrCreate(var); - } - else { - if(t->tk == LEX_R_VAR) t->match(LEX_R_VAR); // skip var - for_var = execute_function_call(execute); + if(LoopData.init.size()) { + t->pushTokenScope(LoopData.init); + ScopeControl.addLetScope(); + execute_statement(execute); // forwarder } - - if(!for_each) for_each = t->tk==LEX_T_OF; - t->match(LEX_R_IN, LEX_T_OF); - - for_in_var = execute_function_call(execute); - CheckRightHandVar(execute, for_in_var); - t->match(')'); - STRING_SET_t keys; - for_in_var->getVarPtr()->keys(keys, true, 0); - if( keys.size() ) { - if(!for_var->isOwned()) { - if(for_var.hasReferencedOwner()) - for_var = for_var.getReferencedOwner()->addChildOrReplace(for_var->getName(), for_var); - else - for_var = root->addChildOrReplace(for_var->getName(), for_var); - } - - CScriptTokenizer::ScriptTokenPosition loopStart = t->getPos(); - - for(STRING_SET_it it = keys.begin(); execute && it != keys.end(); ++it) { - CScriptVarLinkPtr link = for_var; - if(link) { - if (for_each) - link->setVarPtr(for_in_var->getVarPtr()->findChild(*it)); + if(!execute) break; + + t->pushTokenScope(LoopData.iter); + CScriptVarPtr for_in_var = execute_base(execute); + + if(!execute) break; + + CScriptVarPtr Iterator(for_in_var->toIterator(execute, LoopData.type!=CScriptTokenDataLoop::FOR_IN ? 2:1)); + CScriptVarFunctionPtr Iterator_next(Iterator->findChildWithPrototypeChain("next").getter(execute)); + if(execute && !Iterator_next) throwError(execute, TypeError, "'" + for_in_var->toString(execute) + "' is not iterable", t->getPrevPos()); + if(!execute) break; + CScriptResult tmp_execute; + for(;;) { + bool old_haveTry = haveTry; + haveTry = true; + tmp_execute.set(CScriptResult::Normal, Iterator); + t->pushTokenScope(LoopData.condition); + execute_statement(tmp_execute); + haveTry = old_haveTry; + if(tmp_execute.isThrow()){ + if(tmp_execute.value != constStopIteration) { + if(!haveTry) + throw new CScriptException("uncaught exception: '"+tmp_execute.value->toString(CScriptResult())+"'", t->currentFile, t->currentLine(), t->currentColumn()); else - link->setVarPtr(newScriptVar(*it)); - } else ASSERT(0); - t->setPos(loopStart); - execute_statement(execute); - if(!execute) { - bool Continue = false; - if(runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE) - && - (label.empty() || find(myLabels.begin(), myLabels.end(), label) != myLabels.end())) { - label.clear(); - execute = true; - Continue = (runtimeFlags & RUNTIME_CONTINUE)!=0; - runtimeFlags &= ~(RUNTIME_BREAK | RUNTIME_CONTINUE); - } - if(!Continue) break; + execute = tmp_execute; } + break; } - } else - execute_statement(noexecute); - } else - t->skip(t->getToken().Int()); + t->pushTokenScope(LoopData.body); + execute_statement(execute); + if(!execute) { + bool Continue = false; + if(execute.isBreakContinue() + && + (execute.target.empty() || find(LoopData.labels.begin(), LoopData.labels.end(), execute.target) != LoopData.labels.end())) { + Continue = execute.isContinue(); + execute.set(CScriptResult::Normal, false); + } + if(!Continue) break; + } + } + } break; - case LEX_R_FOR: - if(execute) + case LEX_T_LOOP: { - t->match(LEX_R_FOR); - STRING_VECTOR_t myLabels; - while(t->tk == LEX_T_LOOP_LABEL) { - myLabels.push_back(t->tkStr()); - t->match(LEX_T_LOOP_LABEL); - } - t->match('('); + CScriptTokenDataLoop &LoopData = t->getToken().Loop(); + t->match(LEX_T_LOOP); + if(!execute) break; + CScopeControl ScopeControl(this); - if(t->tk == LEX_T_FORWARD) { - ScopeControl.addLetScope(); - execute_statement(execute); // forwarder - } - //if(t->tk == LEX_R_LET) - execute_statement(execute); // initialisation - CScriptTokenizer::ScriptTokenPosition conditionStart = t->getPos(); - bool cond_empty = true; - bool loopCond = execute; // Empty Condition -->always true - if(t->tk != ';') { - cond_empty = false; - loopCond = execute && execute_base(execute)->toBoolean(); - } - t->match(';'); - CScriptTokenizer::ScriptTokenPosition iterStart = t->getPos(); - bool iter_empty = true; - if(t->tk != ')') - { - iter_empty = false; - execute_base(noexecute); // iterator + if(LoopData.type == CScriptTokenDataLoop::FOR) { + CScriptResult tmp_execute; + t->pushTokenScope(LoopData.init); + if(t->tk == LEX_T_FORWARD) { + ScopeControl.addLetScope(); + execute_statement(tmp_execute); // forwarder + } + if(t->tk==LEX_R_VAR || t->tk==LEX_R_LET) + execute_statement(tmp_execute); // initialisation + else if (t->tk != ';') + execute_base(tmp_execute); // initialisation + if(!execute(tmp_execute)) break; } - t->match(')'); - if(loopCond) { // when execute==false then loopCond always false to - CScriptTokenizer::ScriptTokenPosition loopStart = t->getPos(); - CScriptTokenizer::ScriptTokenPosition loopEnd = t->getPos(); - while (loopCond && execute) { - t->setPos(loopStart); - execute_statement(execute); - if(loopEnd == loopStart) // first loop-pass - loopEnd = t->getPos(); - if(!execute) { - bool Continue; - if(runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE) - && - (label.empty() || find(myLabels.begin(), myLabels.end(), label) != myLabels.end())) { - label.clear(); - execute = true; - Continue = (runtimeFlags & RUNTIME_CONTINUE)!=0; - runtimeFlags &= ~(RUNTIME_BREAK | RUNTIME_CONTINUE); - } - if(!Continue) break; - } - if(execute) { - if(!iter_empty) { - t->setPos(iterStart);; - execute_base(execute); - } - if(!cond_empty) { - t->setPos(conditionStart); - loopCond = execute_base(execute)->toBoolean(); - } + bool loopCond = true; // Empty Condition -->always true + if(LoopData.type != CScriptTokenDataLoop::DO && LoopData.condition.size()) { + t->pushTokenScope(LoopData.condition); + loopCond = execute_base(execute)->toBoolean(); + if(!execute) break; + } + while (loopCond && execute) { + t->pushTokenScope(LoopData.body); + execute_statement(execute); + if(!execute) { + bool Continue = false; + if(execute.isBreakContinue() + && + (execute.target.empty() || find(LoopData.labels.begin(), LoopData.labels.end(), execute.target) != LoopData.labels.end())) { + Continue = execute.isContinue(); + execute.set(CScriptResult::Normal, false); } + if(!Continue) break; } - t->setPos(loopEnd); - } else - execute_statement(noexecute); - } else - t->skip(t->getToken().Int()); + if(LoopData.type == CScriptTokenDataLoop::FOR && execute && LoopData.iter.size()) { + t->pushTokenScope(LoopData.iter); + execute_base(execute); + } + if(execute && LoopData.condition.size()) { + t->pushTokenScope(LoopData.condition); + loopCond = execute_base(execute)->toBoolean(); + } + } + } break; case LEX_R_BREAK: case LEX_R_CONTINUE: - if(execute) { - runtimeFlags |= t->tk==LEX_R_BREAK ? RUNTIME_BREAK : RUNTIME_CONTINUE; - execute = false; + if (execute) + { + CScriptResult::TYPE type = t->tk==LEX_R_BREAK ? CScriptResult::Break : CScriptResult::Continue; + string label; t->match(t->tk); if(t->tk == LEX_ID) { - if(execute) label = t->tkStr(); + label = t->tkStr(); t->match(LEX_ID); } t->match(';'); + execute.set(type, label); } else t->skip(t->getToken().Int()); break; case LEX_R_RETURN: if (execute) { t->match(LEX_R_RETURN); - CScriptVarPtr result; + CScriptVarPtr result = constUndefined; if (t->tk != ';') result = execute_base(execute); t->match(';'); - if(result) scope()->scopeVar()->addChildOrReplace(TINYJS_RETURN_VAR, result); - execute = false; + execute.set(CScriptResult::Return, result); } else t->skip(t->getToken().Int()); break; @@ -5438,75 +5883,61 @@ CScriptVarLinkPtr CTinyJS::execute_statement(bool &execute) { CScriptVarLinkWorkPtr funcVar = parseFunctionDefinition(t->getToken()); scope()->scopeVar()->addChildOrReplace(funcVar->getName(), funcVar, SCRIPTVARLINK_VARDEFAULT); } - case LEX_R_FUNCTION_PLACEHOLDER: + case LEX_T_FUNCTION_PLACEHOLDER: t->match(t->tk); break; - case LEX_R_TRY: + case LEX_T_TRY: if(execute) { - t->match(LEX_R_TRY); - STRING_VECTOR_t myLabels; - while(t->tk == LEX_T_LOOP_LABEL) { - myLabels.push_back(t->tkStr()); - t->match(LEX_T_LOOP_LABEL); - } - // save runtimeFlags - int old_throw_runtimeFlags = runtimeFlags & RUNTIME_THROW_MASK; - // set runtimeFlags - runtimeFlags = runtimeFlags | RUNTIME_CAN_THROW; + CScriptTokenDataTry &TryData = t->getToken().Try(); + + bool old_haveTry = haveTry; + haveTry = true; + // execute try-block + t->pushTokenScope(TryData.tryBlock); execute_block(execute); - CScriptVarPtr exceptionVar = this->exceptionVar; // remember exceptionVar - this->exceptionVar = CScriptVarPtr(); // clear exeptionVar; - - bool isThrow = (runtimeFlags & RUNTIME_THROW) != 0; - if(isThrow) execute = true; - if(runtimeFlags & RUNTIME_BREAK && find(myLabels.begin(), myLabels.end(), label) != myLabels.end()) { - label.clear(); - execute = true; - runtimeFlags &= ~RUNTIME_BREAK; - } - // restore runtimeFlags - runtimeFlags = (runtimeFlags & ~RUNTIME_THROW_MASK) | old_throw_runtimeFlags; - - while(t->tk == LEX_R_CATCH) // expect catch, finally - { - if(execute && isThrow) { // when a catch-block is found or no throw thens skip all followed - t->match(LEX_R_CATCH); - t->match('('); - string exception_var_name = t->tkStr(); - t->match(LEX_ID); + + bool isThrow = execute.isThrow(); + + if(isThrow && execute.value) { + // execute catch-blocks only if value set (spezial case Generator.close() -> only finally-blocks are executed) + for(CScriptTokenDataTry::CatchBlock_it catchBlock = TryData.catchBlocks.begin(); catchBlock!=TryData.catchBlocks.end(); catchBlock++) { + CScriptResult catch_execute; CScopeControl ScopeControl(this); ScopeControl.addLetScope(); - scope()->scopeLet()->addChild(exception_var_name, exceptionVar); + t->pushTokenScope(catchBlock->condition); // condition; + execute_statement(catch_execute); // forwarder + assign_destructuring_var(0, *catchBlock->indentifiers, execute.value, catch_execute); bool condition = true; - if(t->tk == LEX_R_IF) { - t->match(LEX_R_IF); - condition = execute_base(execute)->toBoolean(); + if(catchBlock->condition.size()>1) + condition = execute_base(catch_execute)->toBoolean(); + if(!catch_execute) { + execute = catch_execute; + break; + } else if(condition) { + t->pushTokenScope(catchBlock->block); // condition; + execute_block(catch_execute); + execute = catch_execute; + break; } - t->match(')'); - if(execute && condition) { - isThrow = false; - execute_block(execute, false); - } else - execute_block(noexecute, false); - } else - t->skip(t->getToken().Int()); + } } - if(t->tk == LEX_R_FINALLY) { - t->match(LEX_R_FINALLY); - bool finally_execute = true; // alway execute finally-block + if(TryData.finallyBlock.size()) { + CScriptResult finally_execute; // alway execute finally-block + t->pushTokenScope(TryData.finallyBlock); // finally; execute_block(finally_execute); + execute(finally_execute); } - if(isThrow && (runtimeFlags & RUNTIME_THROW)==0) { // no catch-block found and no new exception - if(runtimeFlags & RUNTIME_CAN_THROW) { // throw again - runtimeFlags |= RUNTIME_THROW; - execute = false; - this->exceptionVar = exceptionVar; - } else - throw new CScriptException("uncaught exception: ", t->currentFile, t->currentLine(), t->currentColumn()); + // restore haveTry + haveTry = old_haveTry; + if(execute.isThrow() && !haveTry) { // (exception in catch or finally or no catch-clause found) and no parent try-block + if(execute.value->isError()) + throw CScriptVarErrorPtr(execute.value)->toCScriptException(); + throw new CScriptException("uncaught exception: '"+execute.value->toString()+"'", execute.throw_at_file, execute.throw_at_line, execute.throw_at_column); } - } else - t->skip(t->getToken().Int()); + + } + t->match(LEX_T_TRY); break; case LEX_R_THROW: if(execute) { @@ -5515,11 +5946,8 @@ CScriptVarLinkPtr CTinyJS::execute_statement(bool &execute) { t->match(LEX_R_THROW); CScriptVarPtr a = execute_base(execute); if(execute) { - if(runtimeFlags & RUNTIME_CAN_THROW) { - runtimeFlags |= RUNTIME_THROW; - execute = false; - exceptionVar = a; - } + if(haveTry) + execute.setThrow(a, t->currentFile, tokenPos.currentLine(), tokenPos.currentColumn()); else throw new CScriptException("uncaught exception: '"+a->toString(execute)+"'", t->currentFile, tokenPos.currentLine(), tokenPos.currentColumn()); } @@ -5554,7 +5982,7 @@ CScriptVarLinkPtr CTinyJS::execute_statement(bool &execute) { t->match(LEX_R_CASE); t->match(LEX_T_SKIP); // skip 'L_T_SKIP' CScriptVarLinkPtr CaseValue = execute_base(execute); - CaseValue = mathsOp(execute, CaseValue, SwitchValue, LEX_EQUAL); + CaseValue = mathsOp(execute, CaseValue, SwitchValue, LEX_TYPEEQUAL); if(execute) { found = CaseValue->toBoolean(); if(found) t->match(':'); // skip ':' and execute all after ':' @@ -5586,49 +6014,57 @@ CScriptVarLinkPtr CTinyJS::execute_statement(bool &execute) { goto end_while; // goto isn't fine but C supports no "break lable;" break; default: - execute_statement(found ? execute : noexecute); + ASSERT(found); + execute_statement(execute); break; } } end_while: t->match('}'); - if(!execute && (runtimeFlags & RUNTIME_BREAK) ) { - execute = true; - runtimeFlags &= ~RUNTIME_BREAK; + if(execute.isBreak() && execute.target.empty()) { + execute.set(CScriptResult::Normal); } } else t->skip(t->getToken().Int()); } else t->skip(t->getToken().Int()); break; + case LEX_T_DUMMY_LABEL: + t->match(LEX_T_DUMMY_LABEL); + t->match(':'); + break; case LEX_T_LABEL: { - string Label = t->tkStr(); - t->match(LEX_T_LABEL); - t->match(':'); + STRING_VECTOR_t Labels; + while(t->tk == LEX_T_LABEL) { + Labels.push_back(t->tkStr()); + t->match(LEX_T_LABEL); + t->match(':'); + } if(execute) { execute_statement(execute); - if(!execute) { - if(runtimeFlags & (RUNTIME_BREAK) && Label == label) { // break this label - runtimeFlags &= ~RUNTIME_BREAK; - execute = true; - } + if(execute.isBreak() && find(Labels.begin(), Labels.end(), execute.target) != Labels.end()) { // break this label + execute.set(CScriptResult::Normal, false); } } else - execute_statement(noexecute); + execute_statement(execute); } break; case LEX_EOF: t->match(LEX_EOF); break; default: - /* Execute a simple statement that only contains basic arithmetic... */ - ret = execute_base(execute); - t->match(';'); + if(t->tk!=LEX_T_SKIP || execute) { + if(t->tk==LEX_T_SKIP) t->match(LEX_T_SKIP); + /* Execute a simple statement that only contains basic arithmetic... */ + CScriptVarPtr ret = execute_base(execute); + if(execute) execute.set(CScriptResult::Normal, CScriptVarPtr(ret)); + t->match(';'); + } else + t->skip(t->getToken().Int()); break; } - return ret; } @@ -5689,15 +6125,6 @@ void CTinyJS::native_Object_keys(const CFunctionsScopePtr &c, void *data) { STRING_SET_t keys; obj->keys(keys, data==0); - uint32_t length = 0; - CScriptVarStringPtr isStringObj = obj->getRawPrimitive(); - if(isStringObj) - length = isStringObj->stringLength(); - else - length = obj->getArrayLength(); - for(uint32_t i=0; isetArrayIndex(idx++, newScriptVar(*it)); @@ -5770,7 +6197,7 @@ void CTinyJS::native_Object_prototype_valueOf(const CFunctionsScopePtr &c, void c->setReturnVar(c->getArgument("this")->valueOf_CallBack()); } void CTinyJS::native_Object_prototype_toString(const CFunctionsScopePtr &c, void *data) { - bool execute = true; + CScriptResult execute; int radix = 10; if(c->getArgumentsLength()>=1) radix = c->getArgument("radix")->toNumber().toInt32(); c->setReturnVar(c->getArgument("this")->toString_CallBack(execute, radix)); @@ -5874,6 +6301,34 @@ void CTinyJS::native_Boolean(const CFunctionsScopePtr &c, void *data) { c->setReturnVar(arg); } +////////////////////////////////////////////////////////////////////////// +/// Iterator +////////////////////////////////////////////////////////////////////////// + +void CTinyJS::native_Iterator(const CFunctionsScopePtr &c, void *data) { + if(c->getArgumentsLength()<1) c->throwError(TypeError, "missing argument 0 when calling function Iterator"); + c->setReturnVar(c->getArgument(0)->toIterator(c->getArgument(1)->toBoolean()?1:3)); +} + +////////////////////////////////////////////////////////////////////////// +/// Generator +////////////////////////////////////////////////////////////////////////// + +#ifndef NO_GENERATORS +void CTinyJS::native_Generator_prototype_next(const CFunctionsScopePtr &c, void *data) { + CScriptVarGeneratorPtr Generator(c->getArgument("this")); + if(!Generator) { + static const char *fnc[] = {"next","send","close","throw"}; + c->throwError(TypeError, string(fnc[(int)data])+" method called on incompatible Object"); + } + if((int)data >=2) + Generator->native_throw(c, (void*)(((int)data)-2)); + else + Generator->native_send(c, data); +} +#endif /*NO_GENERATORS*/ + + ////////////////////////////////////////////////////////////////////////// /// Function ////////////////////////////////////////////////////////////////////////// @@ -5967,20 +6422,19 @@ void CTinyJS::native_eval(const CFunctionsScopePtr &c, void *data) { string Code = c->getArgument("jsCode")->toString(); CScriptVarScopePtr scEvalScope = scopes.back(); // save scope scopes.pop_back(); // go back to the callers scope - CScriptVarLinkWorkPtr returnVar; + CScriptResult execute; CScriptTokenizer *oldTokenizer = t; t=0; try { CScriptTokenizer Tokenizer(Code.c_str(), "eval"); t = &Tokenizer; - bool execute = true; do { - returnVar = execute_statement(execute); + execute_statement(execute); while (t->tk==';') t->match(';'); // skip empty statements } while (t->tk!=LEX_EOF); } catch (CScriptException *e) { // script exceptions t = oldTokenizer; // restore tokenizer scopes.push_back(scEvalScope); // restore Scopes; - if(runtimeFlags & RUNTIME_CAN_THROW) { // an Error in eval is allways catchable + if(haveTry) { // an Error in eval is always catchable CScriptVarPtr E = newScriptVarError(this, e->errorType, e->message.c_str(), e->fileName.c_str(), e->lineNumber, e->column); delete e; throw E; @@ -5989,12 +6443,41 @@ void CTinyJS::native_eval(const CFunctionsScopePtr &c, void *data) { } catch (...) { // all other exceptions t = oldTokenizer; // restore tokenizer scopes.push_back(scEvalScope); // restore Scopes; - throw; -} + throw; // re-throw + } t = oldTokenizer; // restore tokenizer scopes.push_back(scEvalScope); // restore Scopes; - if(returnVar) - c->setReturnVar(returnVar->getVarPtr()); + if(execute.value) + c->setReturnVar(execute.value); +} + +static int _native_require_read(const string &Fname, std::string &Data) { + std::ifstream in(Fname.c_str(), std::ios::in | std::ios::binary); + if (in) { + in.seekg(0, std::ios::end); + Data.resize((string::size_type)in.tellg()); + in.seekg(0, std::ios::beg); + in.read(&Data[0], Data.size()); + in.close(); + return(0); + } + return errno; +} + +void CTinyJS::native_require(const CFunctionsScopePtr &c, void *data) { + string File = c->getArgument("jsFile")->toString(); + string Code; + int ErrorNo; + if(!native_require_read) + native_require_read = _native_require_read; // use builtin if no callback + + if((ErrorNo = native_require_read(File, Code))) { + ostringstream msg; + msg << "can't read \"" << File << "\" (Error=" << ErrorNo << ")"; + c->throwError(Error, msg.str()); + } + c->addChildOrReplace("jsCode", c->newScriptVar(Code)); + native_eval(c, data); } void CTinyJS::native_isNAN(const CFunctionsScopePtr &c, void *data) { @@ -6027,7 +6510,7 @@ void CTinyJS::native_JSON_parse(const CFunctionsScopePtr &c, void *data) { try { CScriptTokenizer Tokenizer(Code.c_str(), "JSON.parse", 0, -1); t = &Tokenizer; - bool execute = true; + CScriptResult execute; returnVar = execute_literals(execute); t->match(LEX_EOF); } catch (CScriptException *e) { @@ -6038,29 +6521,25 @@ void CTinyJS::native_JSON_parse(const CFunctionsScopePtr &c, void *data) { if(returnVar) c->setReturnVar(returnVar); - -// check of exceptions -// int exceptionState = runtimeFlags & (RUNTIME_BREAK | RUNTIME_CONTINUE); -// runtimeFlags &= ~RUNTIME_LOOP_MASK; -// if(exceptionState) throw new CScriptEvalException(exceptionState); } void CTinyJS::setTemporaryID_recursive(uint32_t ID) { for(vector::iterator it = pseudo_refered.begin(); it!=pseudo_refered.end(); ++it) - if(**it) (**it)->setTemporaryID_recursive(ID); + if(**it) (**it)->setTemporaryMark_recursive(ID); for(int i=Error; isetTemporaryID_recursive(ID); - root->setTemporaryID_recursive(ID); + if(errorPrototypes[i]) errorPrototypes[i]->setTemporaryMark_recursive(ID); + root->setTemporaryMark_recursive(ID); } void CTinyJS::ClearUnreferedVars(const CScriptVarPtr &extra/*=CScriptVarPtr()*/) { - uint32_t UniqueID = getUniqueID(); + uint32_t UniqueID = allocUniqueID(); setTemporaryID_recursive(UniqueID); - if(extra) extra->setTemporaryID_recursive(UniqueID); + if(extra) extra->setTemporaryMark_recursive(UniqueID); CScriptVar *p = first; + while(p) { - if(p->temporaryID != UniqueID) + if(p->getTemporaryMark() != UniqueID) { CScriptVarPtr var = p; var->removeAllChildren(); @@ -6069,5 +6548,6 @@ void CTinyJS::ClearUnreferedVars(const CScriptVarPtr &extra/*=CScriptVarPtr()*/) else p = p->next; } + freeUniqueID(); } diff --git a/TinyJS.h b/TinyJS.h index 8e2bac4..f03181a 100755 --- a/TinyJS.h +++ b/TinyJS.h @@ -14,7 +14,7 @@ * * Authored / Changed By Armin Diedering * - * Copyright (C) 2010-2012 ardisoft + * Copyright (C) 2010-2014 ardisoft * * * Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -44,17 +44,12 @@ #include #include #include +#include #include #include #include "config.h" -#if __cplusplus >= 201103L -# define MEMBER_DELETE =delete -#else -# define MEMBER_DELETE -#endif - #ifdef NO_POOL_ALLOCATOR template class fixed_size_object {}; @@ -119,27 +114,24 @@ enum LEX_TYPES { LEX_XOREQUAL, #define LEX_ASSIGNMENTS_END LEX_XOREQUAL +#define LEX_TOKEN_NONSIMPLE_1_BEGIN LEX_TOKEN_STRING_BEGIN #define LEX_TOKEN_STRING_BEGIN LEX_ID LEX_ID, LEX_STR, LEX_REGEXP, LEX_T_LABEL, LEX_T_DUMMY_LABEL, - LEX_T_LOOP_LABEL, -#define LEX_TOKEN_STRING_END LEX_T_LOOP_LABEL +#define LEX_TOKEN_STRING_END LEX_T_DUMMY_LABEL LEX_FLOAT, +#define LEX_TOKEN_NONSIMPLE_1_END LEX_FLOAT // reserved words LEX_R_IF, LEX_R_ELSE, -#define LEX_TOKEN_LOOP_BEGIN LEX_R_DO LEX_R_DO, LEX_R_WHILE, LEX_R_FOR, - LEX_T_FOR_IN, - LEX_T_FOR_EACH_IN, -#define LEX_TOKEN_LOOP_END LEX_T_FOR_EACH_IN LEX_R_IN, LEX_T_OF, LEX_R_BREAK, @@ -147,6 +139,7 @@ enum LEX_TYPES { LEX_R_RETURN, LEX_R_VAR, LEX_R_LET, + LEX_R_CONST, LEX_R_WITH, LEX_R_TRUE, LEX_R_FALSE, @@ -166,30 +159,40 @@ enum LEX_TYPES { // special token // LEX_T_FILE, -#define LEX_TOKEN_FUNCTION_BEGIN LEX_R_FUNCTION_PLACEHOLDER - LEX_R_FUNCTION_PLACEHOLDER, +#define LEX_TOKEN_NONSIMPLE_2_BEGIN LEX_TOKEN_FOR_BEGIN +#define LEX_TOKEN_FOR_BEGIN LEX_T_LOOP + LEX_T_LOOP, + LEX_T_FOR_IN, +#define LEX_TOKEN_FOR_END LEX_T_FOR_IN +#define LEX_TOKEN_FUNCTION_BEGIN LEX_R_FUNCTION LEX_R_FUNCTION, + LEX_T_FUNCTION_PLACEHOLDER, LEX_T_FUNCTION_OPERATOR, - LEX_T_FUNCTION_SHORT, - LEX_T_FUNCTION_OPERATOR_SHORT, LEX_T_GET, LEX_T_SET, #define LEX_TOKEN_FUNCTION_END LEX_T_SET + LEX_T_TRY, + LEX_T_OBJECT_LITERAL, + LEX_T_DESTRUCTURING_VAR, + LEX_T_FORWARD, +#define LEX_TOKEN_NONSIMPLE_2_END LEX_T_FORWARD + LEX_T_EXCEPTION_VAR, LEX_T_SKIP, - LEX_T_FORWARD, - LEX_T_DESTRUCTURING_VAR, - LEX_T_OBJECT_LITERAL, + LEX_R_YIELD, + }; #define LEX_TOKEN_DATA_STRING(tk) ((LEX_TOKEN_STRING_BEGIN<= tk && tk <= LEX_TOKEN_STRING_END)) #define LEX_TOKEN_DATA_FLOAT(tk) (tk==LEX_FLOAT) +#define LEX_TOKEN_DATA_LOOP(tk) (LEX_TOKEN_FOR_BEGIN <= tk && tk <= LEX_TOKEN_FOR_END) #define LEX_TOKEN_DATA_FUNCTION(tk) (LEX_TOKEN_FUNCTION_BEGIN <= tk && tk <= LEX_TOKEN_FUNCTION_END) +#define LEX_TOKEN_DATA_TRY(tk) (tk == LEX_T_TRY) #define LEX_TOKEN_DATA_OBJECT_LITERAL(tk) (tk==LEX_T_OBJECT_LITERAL) #define LEX_TOKEN_DATA_DESTRUCTURING_VAR(tk) (tk==LEX_T_DESTRUCTURING_VAR) #define LEX_TOKEN_DATA_FORWARDER(tk) (tk==LEX_T_FORWARD) -#define LEX_TOKEN_DATA_SIMPLE(tk) (!LEX_TOKEN_DATA_STRING(tk) && !LEX_TOKEN_DATA_FLOAT(tk) && !LEX_TOKEN_DATA_FUNCTION(tk) && !LEX_TOKEN_DATA_OBJECT_LITERAL(tk) && !LEX_TOKEN_DATA_DESTRUCTURING_VAR(tk) && !LEX_TOKEN_DATA_FORWARDER(tk)) +#define LEX_TOKEN_DATA_SIMPLE(tk) (!((LEX_TOKEN_NONSIMPLE_1_BEGIN <= tk && tk <= LEX_TOKEN_NONSIMPLE_1_END) || (LEX_TOKEN_NONSIMPLE_2_BEGIN <= tk && tk <= LEX_TOKEN_NONSIMPLE_2_END))) enum SCRIPTVARLINK_FLAGS { SCRIPTVARLINK_WRITABLE = 1<<0, @@ -197,18 +200,12 @@ enum SCRIPTVARLINK_FLAGS { SCRIPTVARLINK_ENUMERABLE = 1<<2, SCRIPTVARLINK_DEFAULT = SCRIPTVARLINK_WRITABLE | SCRIPTVARLINK_CONFIGURABLE | SCRIPTVARLINK_ENUMERABLE, SCRIPTVARLINK_VARDEFAULT = SCRIPTVARLINK_WRITABLE | SCRIPTVARLINK_ENUMERABLE, + SCRIPTVARLINK_CONSTDEFAULT = SCRIPTVARLINK_ENUMERABLE, SCRIPTVARLINK_BUILDINDEFAULT = SCRIPTVARLINK_WRITABLE | SCRIPTVARLINK_CONFIGURABLE, SCRIPTVARLINK_READONLY = SCRIPTVARLINK_CONFIGURABLE, SCRIPTVARLINK_READONLY_ENUM = SCRIPTVARLINK_CONFIGURABLE | SCRIPTVARLINK_ENUMERABLE, SCRIPTVARLINK_CONSTANT = 0, }; -enum RUNTIME_FLAGS { - RUNTIME_BREAK = 1<<0, - RUNTIME_CONTINUE = 1<<1, - RUNTIME_CAN_THROW = 1<<2, - RUNTIME_THROW = 1<<3, - RUNTIME_THROW_MASK = RUNTIME_CAN_THROW | RUNTIME_THROW, -}; enum ERROR_TYPES { Error = 0, @@ -222,6 +219,7 @@ enum ERROR_TYPES { #define ERROR_COUNT (ERROR_MAX+1) extern const char *ERROR_NAME[]; +#define TEMPORARY_MARK_SLOTS 5 #define TINYJS_RETURN_VAR "return" #define TINYJS_LOKALE_VAR "__locale__" @@ -234,10 +232,9 @@ extern const char *ERROR_NAME[]; #define TINYJS_SCOPE_WITH_VAR "__scope_with__" #define TINYJS_ACCESSOR_GET_VAR "__accessor_get__" #define TINYJS_ACCESSOR_SET_VAR "__accessor_set__" +#define TINYJS_CONSTRUCTOR_VAR "constructor" #define TINYJS_TEMP_NAME "" #define TINYJS_BLANK_DATA "" -#define TINYJS_NEGATIVE_INFINITY_DATA "-1" -#define TINYJS_POSITIVE_INFINITY_DATA "+1" typedef std::vector STRING_VECTOR_t; typedef STRING_VECTOR_t::iterator STRING_VECTOR_it; @@ -299,9 +296,10 @@ class CScriptLex const char *tokenStart; int currentLine; const char *currentLineStart; + int currentColumn() { return tokenStart-currentLineStart; } } pos; int currentLine() { return pos.currentLine; } - int currentColumn() { return pos.tokenStart-pos.currentLineStart; } + int currentColumn() { return pos.currentColumn(); } bool lineBreakBeforeToken; private: const char *data; @@ -335,6 +333,28 @@ class CScriptTokenData private: int refs; }; +template +class CScriptTokenDataPtr { +public: + CScriptTokenDataPtr() : ptr(0) {} + CScriptTokenDataPtr(const CScriptTokenDataPtr &Copy) : ptr(0) { *this=Copy; } + CScriptTokenDataPtr &operator=(const CScriptTokenDataPtr &Copy) { + if(ptr != Copy.ptr) { + if(ptr) ptr->unref(); + if((ptr = Copy.ptr)) ptr->ref(); + } + return *this; + } + CScriptTokenDataPtr(C &Init) { (ptr=&Init)->ref(); } + ~CScriptTokenDataPtr() { if(ptr) ptr->unref(); } + C *operator->() { return ptr; } + C &operator*() { return *ptr; } + operator bool() { return ptr!=0; } + bool operator==(const CScriptTokenDataPtr& rhs) { return ptr==rhs.ptr; } +private: + C *ptr; +}; + class CScriptTokenDataString : public fixed_size_object, public CScriptTokenData { public: CScriptTokenDataString(const std::string &String) : tokenStr(String) {} @@ -344,19 +364,26 @@ class CScriptTokenDataString : public fixed_size_object, class CScriptTokenDataFnc : public fixed_size_object, public CScriptTokenData { public: + CScriptTokenDataFnc() : line(0),isGenerator(false) {} std::string file; int line; std::string name; TOKEN_VECT arguments; TOKEN_VECT body; std::string getArgumentsString(); + bool isGenerator; }; class CScriptTokenDataForwards : public fixed_size_object, public CScriptTokenData { public: CScriptTokenDataForwards() {} - STRING_SET_t vars; - STRING_SET_t lets; + enum { + LETS = 0, + VARS, + CONSTS, + END + }; + STRING_SET_t varNames[END]; STRING_SET_t vars_in_letscope; class compare_fnc_token_by_name { public: @@ -367,14 +394,49 @@ class CScriptTokenDataForwards : public fixed_size_objectunref(); + if((ptr = Copy.ptr)) ptr->ref(); + } + return *this; + } + CScriptTokenDataForwardsPtr(CScriptTokenDataForwards &Init) { (ptr=&Init)->ref(); } + ~CScriptTokenDataForwardsPtr() { if(ptr) ptr->unref(); } + CScriptTokenDataForwards *operator->() { return ptr; } + operator bool() { return ptr!=0; } + bool operator==(const CScriptTokenDataForwardsPtr& rhs) { return ptr==rhs.ptr; } private: + CScriptTokenDataForwards *ptr; +}; +typedef std::vector FORWARDER_VECTOR_t; + +class CScriptTokenDataLoop : public fixed_size_object, public CScriptTokenData { +public: + CScriptTokenDataLoop() { type=FOR; } + enum {FOR_EACH=0, FOR_IN, FOR_OF, FOR, WHILE, DO} type; // do not change the order + STRING_VECTOR_t labels; + TOKEN_VECT init; + TOKEN_VECT condition; + TOKEN_VECT iter; + TOKEN_VECT body; + std::string getParsableString(const std::string &IndentString="", const std::string &Indent=""); }; typedef std::pair DESTRUCTURING_VAR_t; typedef std::vector DESTRUCTURING_VARS_t; typedef DESTRUCTURING_VARS_t::iterator DESTRUCTURING_VARS_it; +typedef DESTRUCTURING_VARS_t::const_iterator DESTRUCTURING_VARS_cit; class CScriptTokenDataDestructuringVar : public fixed_size_object, public CScriptTokenData { public: DESTRUCTURING_VARS_t vars; @@ -399,6 +461,20 @@ class CScriptTokenDataObjectLiteral : public fixed_size_object, public CScriptTokenData { +public: + TOKEN_VECT tryBlock; + struct CatchBlock { + CScriptTokenDataPtr indentifiers; + TOKEN_VECT condition; + TOKEN_VECT block; + }; + std::vector catchBlocks; + typedef std::vector::iterator CatchBlock_it; + TOKEN_VECT finallyBlock; + std::string getParsableString(const std::string &IndentString="", const std::string &Indent=""); +}; + ////////////////////////////////////////////////////////////////////////// /// CScriptToken @@ -433,7 +509,12 @@ class CScriptToken : public fixed_size_object const CScriptTokenDataFnc &Fnc() const { ASSERT(LEX_TOKEN_DATA_FUNCTION(token)); return *dynamic_cast(tokenData); } CScriptTokenDataObjectLiteral &Object() { ASSERT(LEX_TOKEN_DATA_OBJECT_LITERAL(token)); return *dynamic_cast(tokenData); } CScriptTokenDataDestructuringVar &DestructuringVar() { ASSERT(LEX_TOKEN_DATA_DESTRUCTURING_VAR(token)); return *dynamic_cast(tokenData); } + CScriptTokenDataLoop &Loop() { ASSERT(LEX_TOKEN_DATA_LOOP(token)); return *dynamic_cast(tokenData); } + CScriptTokenDataTry &Try() { ASSERT(LEX_TOKEN_DATA_TRY(token)); return *dynamic_cast(tokenData); } CScriptTokenDataForwards &Forwarder() { ASSERT(LEX_TOKEN_DATA_FORWARDER(token)); return *dynamic_cast(tokenData); } +#ifdef _DEBUG + std::string token_str; +#endif uint16_t line; uint16_t column; uint16_t token; @@ -466,7 +547,6 @@ class CScriptTokenizer bool operator ==(const ScriptTokenPosition &eq) { return pos == eq.pos; } ScriptTokenPosition &operator =(const ScriptTokenPosition ©) { tokens=copy.tokens; pos=copy.pos; -// currentLine=copy.currentLine; return *this; } TOKEN_VECT *tokens; @@ -474,6 +554,19 @@ class CScriptTokenizer int currentLine() { return pos->line; } int currentColumn() { return pos->column; } }; + struct ScriptTokenState { + TOKEN_VECT Tokens; + FORWARDER_VECTOR_t Forwarders; + std::vector Marks; + STRING_VECTOR_t Labels; + STRING_VECTOR_t LoopLabels; + bool LeftHand; + void pushLeftHandState() { States.push_back(LeftHand); } + void popLeftHandeState() { LeftHand = States.back(); States.pop_back(); } + std::vector States; + bool FunctionIsGenerator; + bool HaveReturnValue; + }; CScriptTokenizer(); CScriptTokenizer(CScriptLex &Lexer); CScriptTokenizer(const char *Code, const std::string &File="", int Line=0, int Column=0); @@ -494,41 +587,38 @@ class CScriptTokenizer int currentColumn() { return getPos().currentColumn();} const std::string &tkStr() { static std::string empty; return LEX_TOKEN_DATA_STRING(getToken().token)?getToken().String():empty; } private: - void tokenizeCatch(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); - void tokenizeTry(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); - void tokenizeSwitch(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); - void tokenizeWith(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); - void tokenizeWhile(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); - void tokenizeDo(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); - void tokenizeIf(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); - void tokenizeFor(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); - void _tokenizeDeconstructionVarObject(DESTRUCTURING_VARS_t &Vars, STRING_VECTOR_t &VarNames); - void _tokenizeDeconstructionVarArray(DESTRUCTURING_VARS_t &Vars, STRING_VECTOR_t &VarNames); - void _tokenizeDestructionVar(DESTRUCTURING_VARS_t &Vars, const std::string &Path, STRING_VECTOR_t &VarNames); - CScriptToken tokenizeDestructionVar(STRING_VECTOR_t &VarNames); - void tokenizeFunction(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); - void tokenizeLet(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); - void tokenizeVar(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); - void _tokenizeLiteralObject(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags, int &State); - void _tokenizeLiteralArray(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags, int &State); - void tokenizeObjectLiteral(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags, int &State); - - void tokenizeLiteral(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags, int &State); - void tokenizeMember(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags, int &State); - void tokenizeFunctionCall(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags, int &State); - void tokenizeSubExpression(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags, int &State); - void tokenizeCondition(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags, int &State); - void tokenizeAssignment(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); - void tokenizeAssignment(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags, int &State); - void tokenizeExpression(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); - void tokenizeBlock(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); - void tokenizeStatement(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks, STRING_VECTOR_t &Labels, STRING_VECTOR_t &LoopLabels, int Flags); + void tokenizeTry(ScriptTokenState &State, int Flags); + void tokenizeSwitch(ScriptTokenState &State, int Flags); + void tokenizeWith(ScriptTokenState &State, int Flags); + void tokenizeWhileAndDo(ScriptTokenState &State, int Flags); + void tokenizeIf(ScriptTokenState &State, int Flags); + void tokenizeFor(ScriptTokenState &State, int Flags); + CScriptToken tokenizeVarIdentifier(STRING_VECTOR_t *VarNames=0, bool *NeedAssignment=0); + void tokenizeFunction(ScriptTokenState &State, int Flags, bool noLetDef=false); + void tokenizeLet(ScriptTokenState &State, int Flags, bool noLetDef=false); + void tokenizeVarNoConst(ScriptTokenState &State, int Flags); + void tokenizeVarAndConst(ScriptTokenState &State, int Flags); + void _tokenizeLiteralObject(ScriptTokenState &State, int Flags); + void _tokenizeLiteralArray(ScriptTokenState &State, int Flags); + + void tokenizeLiteral(ScriptTokenState &State, int Flags); + void tokenizeMember(ScriptTokenState &State, int Flags); + void tokenizeFunctionCall(ScriptTokenState &State, int Flags); + void tokenizeSubExpression(ScriptTokenState &State, int Flags); + void tokenizeLogic(ScriptTokenState &State, int Flags, int op= LEX_OROR, int op_n=LEX_ANDAND); + void tokenizeCondition(ScriptTokenState &State, int Flags); + void tokenizeAssignment(ScriptTokenState &State, int Flags); + void tokenizeExpression(ScriptTokenState &State, int Flags); + void tokenizeBlock(ScriptTokenState &State, int Flags); + void tokenizeStatementNoLet(ScriptTokenState &State, int Flags); + void tokenizeStatement(ScriptTokenState &State, int Flags); int pushToken(TOKEN_VECT &Tokens, int Match=-1, int Alternate=-1); int pushToken(TOKEN_VECT &Tokens, const CScriptToken &Token); - CScriptTokenDataForwards &pushForwarder(TOKEN_VECT &Tokens, std::vector &BlockStart); - void removeEmptyForwarder(TOKEN_VECT &Tokens, std::vector &BlockStart, std::vector &Marks); - CScriptTokenDataForwards &__getForwarder(TOKEN_VECT &Tokens, int Pos, std::vector &BlockStart, std::vector &Marks); + void pushForwarder(ScriptTokenState &State, bool noMarks=false); + void removeEmptyForwarder(ScriptTokenState &State); + void pushForwarder(TOKEN_VECT &Tokens, FORWARDER_VECTOR_t &Forwarders, std::vector &Marks); + void removeEmptyForwarder(TOKEN_VECT &Tokens, FORWARDER_VECTOR_t &Forwarders, std::vector &Marks); void throwTokenNotExpected(); CScriptLex *l; TOKEN_VECT tokens; @@ -557,7 +647,7 @@ typedef CScriptVarPointer CFunctionsScopePtr; typedef void (*JSCallback)(const CFunctionsScopePtr &var, void *userdata); class CTinyJS; - +class CScriptResult; ////////////////////////////////////////////////////////////////////////// /// CScriptVar @@ -600,6 +690,9 @@ class CScriptVar : public fixed_size_object { virtual bool isNative(); ///< is CScriptVarFunctionNativeCallback / CScriptVarFunctionNativeClass virtual bool isBounded(); ///< is CScriptVarFunctionBounded + virtual bool isIterator(); + virtual bool isGenerator(); + bool isBasic() { return Childs.empty(); } ///< Is this *not* an array/object/etc @@ -609,22 +702,22 @@ class CScriptVar : public fixed_size_object { virtual CScriptVarPrimitivePtr getRawPrimitive()=0; ///< is Var==Primitive -> return this isObject return Value CScriptVarPrimitivePtr toPrimitive(); ///< by default call getDefaultValue_hintNumber by a Date-object calls getDefaultValue_hintString - virtual CScriptVarPrimitivePtr toPrimitive(bool &execute); ///< if the var an ObjectType gets the valueOf; if valueOf of an ObjectType gets toString / otherwise gets the Var itself + virtual CScriptVarPrimitivePtr toPrimitive(CScriptResult &execute); ///< if the var an ObjectType gets the valueOf; if valueOf of an ObjectType gets toString / otherwise gets the Var itself CScriptVarPrimitivePtr toPrimitive_hintString(int32_t radix=0); ///< if the var an ObjectType gets the valueOf; if valueOf of an ObjectType gets toString / otherwise gets the Var itself - CScriptVarPrimitivePtr toPrimitive_hintString(bool &execute, int32_t radix=0); ///< if the var an ObjectType gets the valueOf; if valueOf of an ObjectType gets toString / otherwise gets the Var itself + CScriptVarPrimitivePtr toPrimitive_hintString(CScriptResult &execute, int32_t radix=0); ///< if the var an ObjectType gets the valueOf; if valueOf of an ObjectType gets toString / otherwise gets the Var itself CScriptVarPrimitivePtr toPrimitive_hintNumber(); ///< if the var an ObjectType gets the valueOf; if valueOf of an ObjectType gets toString / otherwise gets the Var itself - CScriptVarPrimitivePtr toPrimitive_hintNumber(bool &execute); ///< if the var an ObjectType gets the valueOf; if valueOf of an ObjectType gets toString / otherwise gets the Var itself + CScriptVarPrimitivePtr toPrimitive_hintNumber(CScriptResult &execute); ///< if the var an ObjectType gets the valueOf; if valueOf of an ObjectType gets toString / otherwise gets the Var itself - CScriptVarPtr callJS_toString(bool &execute, int radix=0); - virtual CScriptVarPtr toString_CallBack(bool &execute, int radix=0); - CScriptVarPtr callJS_valueOf(bool &execute); + CScriptVarPtr callJS_toString(CScriptResult &execute, int radix=0); + virtual CScriptVarPtr toString_CallBack(CScriptResult &execute, int radix=0); + CScriptVarPtr callJS_valueOf(CScriptResult &execute); virtual CScriptVarPtr valueOf_CallBack(); CNumber toNumber(); - CNumber toNumber(bool &execute); + CNumber toNumber(CScriptResult &execute); virtual bool toBoolean(); std::string toString(int32_t radix=0); ///< shortcut for this->toPrimitive_hintString()->toCString(); - std::string toString(bool &execute, int32_t radix=0); ///< shortcut for this->toPrimitive_hintString(execute)->toCString(); + std::string toString(CScriptResult &execute, int32_t radix=0); ///< shortcut for this->toPrimitive_hintString(execute)->toCString(); #define WARN_DEPRECATED #ifdef WARN_DEPRECATED int DEPRECATED("getInt() is deprecated use toNumber().toInt32 instead") getInt(); @@ -641,11 +734,19 @@ class CScriptVar : public fixed_size_object { virtual CScriptVarPtr toObject()=0; + CScriptVarPtr toIterator(int Mode=3); + CScriptVarPtr toIterator(CScriptResult &execute, int Mode=3); + + // virtual std::string getParsableString(const std::string &indentString, const std::string &indent, bool &hasRecursion); ///< get Data as a parsable javascript string #define getParsableStringRecursionsCheck() do{ \ - if(uniqueID && uniqueID==temporaryID) { hasRecursion=true; return "recursion"; } \ - temporaryID = uniqueID; \ + if(uniqueID) { \ + if(uniqueID==getTemporaryMark()) { hasRecursion=true; return "recursion"; } \ + setTemporaryMark(uniqueID); \ + } \ } while(0) + + std::string getParsableString(); ///< get Data as a parsable javascript string virtual std::string getParsableString(const std::string &indentString, const std::string &indent, uint32_t uniqueID, bool &hasRecursion); ///< get Data as a parsable javascript string virtual std::string getVarType()=0; @@ -686,7 +787,7 @@ class CScriptVar : public fixed_size_object { CScriptVarLinkPtr DEPRECATED("addChildNoDup is deprecated use addChildOrReplace instead!") addChildNoDup(const std::string &childName, const CScriptVarPtr &child, int linkFlags = SCRIPTVARLINK_DEFAULT); CScriptVarLinkPtr addChildOrReplace(const std::string &childName, const CScriptVarPtr &child, int linkFlags = SCRIPTVARLINK_DEFAULT); ///< add a child overwriting any with the same name bool removeLink(CScriptVarLinkPtr &link); ///< Remove a specific link (this is faster than finding via a child) - void removeAllChildren(); + virtual void removeAllChildren(); /// ARRAY CScriptVarPtr getArrayIndex(uint32_t idx); ///< The the value at an array index @@ -720,9 +821,9 @@ class CScriptVar : public fixed_size_object { template CScriptVarPtr newScriptVar(T t); // { return ::newScriptVar(context, t); } template CScriptVarPtr newScriptVar(T1 t1, T2 t2); // { return ::newScriptVar(context, t); } template const CScriptVarPtr &constScriptVar(T t); // { return ::newScriptVar(context, t); } - void setTemporaryID(uint32_t ID) { temporaryID = ID; } - virtual void setTemporaryID_recursive(uint32_t ID); - uint32_t getTempraryID() { return temporaryID; } + void setTemporaryMark(uint32_t ID); // defined as inline at end of this file { temporaryMark[context->getCurrentMarkSlot()] = ID; } + virtual void setTemporaryMark_recursive(uint32_t ID); + uint32_t getTemporaryMark(); // defined as inline at end of this file { return temporaryMark[context->getCurrentMarkSlot()]; } protected: bool extensible; CTinyJS *context; @@ -730,7 +831,7 @@ class CScriptVar : public fixed_size_object { CScriptVar *prev; public: CScriptVar *next; - uint32_t temporaryID; + uint32_t temporaryMark[TEMPORARY_MARK_SLOTS]; friend class CScriptVarPtr; }; @@ -783,6 +884,7 @@ class CScriptVarPointer : public CScriptVarPtr { CScriptVarPointer() {} CScriptVarPointer(CScriptVar *Var) : CScriptVarPtr(dynamic_cast(Var)) {} CScriptVarPointer(const CScriptVarPtr &Copy) : CScriptVarPtr(dynamic_cast(Copy.getVar())) {} + CScriptVarPointer &operator=(const CScriptVarPtr &Copy) { CScriptVarPtr::operator=(dynamic_cast(Copy.getVar())); return *this; } C * operator ->() const { C *Var = dynamic_cast(var); ASSERT(var && Var); return Var; } }; @@ -822,23 +924,23 @@ class CScriptVarLink : public fixed_size_object CScriptVarPrimitivePtr toPrimitive() { ///< by default call getDefaultValue_hintNumber by a Date-object calls getDefaultValue_hintString return var->toPrimitive(); } - CScriptVarPrimitivePtr toPrimitive(bool &execute) { ///< if the var an ObjectType gets the valueOf; if valueOf of an ObjectType gets toString / otherwise gets the Var itself + CScriptVarPrimitivePtr toPrimitive(CScriptResult &execute) { ///< if the var an ObjectType gets the valueOf; if valueOf of an ObjectType gets toString / otherwise gets the Var itself return var->toPrimitive(execute); } CScriptVarPrimitivePtr toPrimitive_hintString(int32_t radix=0) { ///< if the var an ObjectType gets the valueOf; if valueOf of an ObjectType gets toString / otherwise gets the Var itself return var->toPrimitive_hintString(radix); } - CScriptVarPrimitivePtr toPrimitive_hintString(bool &execute, int32_t radix=0) { ///< if the var an ObjectType gets the valueOf; if valueOf of an ObjectType gets toString / otherwise gets the Var itself + CScriptVarPrimitivePtr toPrimitive_hintString(CScriptResult &execute, int32_t radix=0) { ///< if the var an ObjectType gets the valueOf; if valueOf of an ObjectType gets toString / otherwise gets the Var itself return var->toPrimitive_hintString(execute, radix); } CScriptVarPrimitivePtr toPrimitive_hintNumber() { ///< if the var an ObjectType gets the valueOf; if valueOf of an ObjectType gets toString / otherwise gets the Var itself return var->toPrimitive_hintNumber(); } - CScriptVarPrimitivePtr toPrimitive_hintNumber(bool &execute) { ///< if the var an ObjectType gets the valueOf; if valueOf of an ObjectType gets toString / otherwise gets the Var itself + CScriptVarPrimitivePtr toPrimitive_hintNumber(CScriptResult &execute) { ///< if the var an ObjectType gets the valueOf; if valueOf of an ObjectType gets toString / otherwise gets the Var itself return var->toPrimitive_hintNumber(execute); } CNumber toNumber(); // { return var->toNumber(); } - CNumber toNumber(bool &execute); // { return var->toNumber(execute); } + CNumber toNumber(CScriptResult &execute); // { return var->toNumber(execute); } bool toBoolean() { return var->toBoolean(); } std::string toString(int32_t radix=0) { ///< shortcut for this->toPrimitive_hintString()->toCString(); return var->toString(radix); } - std::string toString(bool &execute, int32_t radix=0) { ///< shortcut for this->toPrimitive_hintString(execute)->toCString(); + std::string toString(CScriptResult &execute, int32_t radix=0) { ///< shortcut for this->toPrimitive_hintString(execute)->toCString(); return var->toString(execute, radix); } CScriptVarPtr toObject() { return var->toObject(); }; @@ -887,9 +989,9 @@ class CScriptVarLinkPtr { // getter & setter CScriptVarLinkWorkPtr getter(); - CScriptVarLinkWorkPtr getter(bool &execute); + CScriptVarLinkWorkPtr getter(CScriptResult &execute); CScriptVarLinkWorkPtr setter(const CScriptVarPtr &Var); - CScriptVarLinkWorkPtr setter(bool &execute, const CScriptVarPtr &Var); + CScriptVarLinkWorkPtr setter(CScriptResult &execute, const CScriptVarPtr &Var); // if operator bool() const { return link!=0; } @@ -929,9 +1031,9 @@ class CScriptVarLinkWorkPtr : public CScriptVarLinkPtr { // getter & setter CScriptVarLinkWorkPtr getter(); - CScriptVarLinkWorkPtr getter(bool &execute); + CScriptVarLinkWorkPtr getter(CScriptResult &execute); CScriptVarLinkWorkPtr setter(const CScriptVarPtr &Var); - CScriptVarLinkWorkPtr setter(bool &execute, const CScriptVarPtr &Var); + CScriptVarLinkWorkPtr setter(CScriptResult &execute, const CScriptVarPtr &Var); void swap(CScriptVarLinkWorkPtr &Link) { @@ -978,7 +1080,7 @@ class CScriptVarPrimitive : public CScriptVar { virtual std::string toCString(int radix=0)=0; virtual CScriptVarPtr toObject(); - virtual CScriptVarPtr toString_CallBack(bool &execute, int radix=0); + virtual CScriptVarPtr toString_CallBack(CScriptResult &execute, int radix=0); protected: }; @@ -1060,7 +1162,7 @@ class CScriptVarString : public CScriptVarPrimitive { virtual std::string getVarType(); // { return "string"; } virtual CScriptVarPtr toObject(); - virtual CScriptVarPtr toString_CallBack(bool &execute, int radix=0); + virtual CScriptVarPtr toString_CallBack(CScriptResult &execute, int radix=0); uint32_t stringLength() { return data.size(); } int getChar(uint32_t Idx); @@ -1246,7 +1348,9 @@ class CScriptVarNumber : public CScriptVarPrimitive { define_newScriptVar_Fnc(Number, CTinyJS *Context, const CNumber &Obj); inline define_newScriptVar_NamedFnc(Number, CTinyJS *Context, const CNumber &Obj) { return new CScriptVarNumber(Context, Obj); } inline define_newScriptVar_Fnc(Number, CTinyJS *Context, unsigned int Obj) { return newScriptVarNumber(Context, CNumber(Obj)); } +inline define_newScriptVar_Fnc(Number, CTinyJS *Context, unsigned long Obj) { return newScriptVarNumber(Context, CNumber((uint32_t)Obj)); } inline define_newScriptVar_Fnc(Number, CTinyJS *Context, int Obj) { return newScriptVarNumber(Context, CNumber(Obj)); } +inline define_newScriptVar_Fnc(Number, CTinyJS *Context, long Obj) { return newScriptVarNumber(Context, CNumber((int32_t)Obj)); } inline define_newScriptVar_Fnc(Number, CTinyJS *Context, double Obj) { return newScriptVarNumber(Context, CNumber(Obj)); } inline define_DEPRECATED_newScriptVar_Fnc(NaN, CTinyJS *Context, NaN_t) { return newScriptVarNumber(Context, CNumber(NaN)); } inline define_DEPRECATED_newScriptVar_Fnc(Infinity, CTinyJS *Context, Infinity Obj) { return newScriptVarNumber(Context, CNumber(Obj)); } @@ -1300,16 +1404,19 @@ class CScriptVarObject : public CScriptVar { virtual ~CScriptVarObject(); virtual CScriptVarPtr clone(); + virtual void removeAllChildren(); + virtual CScriptVarPrimitivePtr getRawPrimitive(); virtual bool isObject(); // { return true; } virtual std::string getParsableString(const std::string &indentString, const std::string &indent, uint32_t uniqueID, bool &hasRecursion); virtual std::string getVarType(); ///< always "object" + virtual std::string getVarTypeTagName(); ///< always "Object" virtual CScriptVarPtr toObject(); virtual CScriptVarPtr valueOf_CallBack(); - virtual CScriptVarPtr toString_CallBack(bool &execute, int radix=0); - virtual void setTemporaryID_recursive(uint32_t ID); + virtual CScriptVarPtr toString_CallBack(CScriptResult &execute, int radix=0); + virtual void setTemporaryMark_recursive(uint32_t ID); protected: private: CScriptVarPrimitivePtr value; @@ -1324,6 +1431,32 @@ inline define_newScriptVar_Fnc(Object, CTinyJS *Context, const CScriptVarPtr &Pr inline define_newScriptVar_Fnc(Object, CTinyJS *Context, const CScriptVarPrimitivePtr &Value, const CScriptVarPtr &Prototype) { return new CScriptVarObject(Context, Value, Prototype); } +////////////////////////////////////////////////////////////////////////// +/// CScriptVarObjectTyped (simple Object with TypeTagName) +////////////////////////////////////////////////////////////////////////// + +define_dummy_t(StopIteration); + +class CScriptVarObjectTypeTagged : public CScriptVarObject { +protected: +// CScriptVarObjectTyped(CTinyJS *Context); + CScriptVarObjectTypeTagged(CTinyJS *Context, const CScriptVarPtr &Prototype, const std::string &TypeTagName) : CScriptVarObject(Context, Prototype), typeTagName(TypeTagName) {} + CScriptVarObjectTypeTagged(const CScriptVarObjectTypeTagged &Copy) : CScriptVarObject(Copy), typeTagName(Copy.typeTagName) {} ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarObjectTypeTagged(); + virtual CScriptVarPtr clone(); + + virtual std::string getVarTypeTagName(); +protected: +private: + std::string typeTagName; + friend define_newScriptVar_Fnc(Object, CTinyJS *Context, Object_t, const CScriptVarPtr &, const std::string &); + friend define_newScriptVar_Fnc(Object, CTinyJS *Context, const CScriptVarPtr &, const std::string &); +}; +inline define_newScriptVar_Fnc(Object, CTinyJS *Context, Object_t, const CScriptVarPtr &Prototype, const std::string &TypeTagName) { return new CScriptVarObjectTypeTagged(Context, Prototype, TypeTagName); } +inline define_newScriptVar_Fnc(Object, CTinyJS *Context, const CScriptVarPtr &Prototype, const std::string &TypeTagName) { return new CScriptVarObjectTypeTagged(Context, Prototype, TypeTagName); } + + ////////////////////////////////////////////////////////////////////////// /// CScriptVarError ////////////////////////////////////////////////////////////////////////// @@ -1341,11 +1474,14 @@ class CScriptVarError : public CScriptVarObject { // virtual std::string getParsableString(const std::string &indentString, const std::string &indent); ///< get Data as a parsable javascript string - virtual CScriptVarPtr toString_CallBack(bool &execute, int radix=0); + virtual CScriptVarPtr toString_CallBack(CScriptResult &execute, int radix=0); + CScriptException *toCScriptException(); private: friend define_newScriptVar_NamedFnc(Error, CTinyJS *Context, ERROR_TYPES type, const char *message, const char *file, int line, int column); + friend define_newScriptVar_NamedFnc(Error, CTinyJS *Context, const CScriptException &Exception); }; inline define_newScriptVar_NamedFnc(Error, CTinyJS *Context, ERROR_TYPES type, const char *message=0, const char *file=0, int line=-1, int column=-1) { return new CScriptVarError(Context, type, message, file, line, column); } +inline define_newScriptVar_NamedFnc(Error, CTinyJS *Context, const CScriptException &Exception) { return new CScriptVarError(Context, Exception.errorType, Exception.message.c_str(), Exception.fileName.c_str(), Exception.lineNumber, Exception.column); } ////////////////////////////////////////////////////////////////////////// /// CScriptVarArray @@ -1356,7 +1492,7 @@ define_ScriptVarPtr_Type(Array); class CScriptVarArray : public CScriptVarObject { protected: CScriptVarArray(CTinyJS *Context); - CScriptVarArray(const CScriptVarArray &Copy) : CScriptVarObject(Copy) {} ///< Copy protected -> use clone for public + CScriptVarArray(const CScriptVarArray &Copy) : CScriptVarObject(Copy), toStringRecursion(Copy.toStringRecursion) {} ///< Copy protected -> use clone for public public: virtual ~CScriptVarArray(); virtual CScriptVarPtr clone(); @@ -1364,11 +1500,12 @@ class CScriptVarArray : public CScriptVarObject { virtual std::string getParsableString(const std::string &indentString, const std::string &indent, uint32_t uniqueID, bool &hasRecursion); - virtual CScriptVarPtr toString_CallBack(bool &execute, int radix=0); + virtual CScriptVarPtr toString_CallBack(CScriptResult &execute, int radix=0); friend define_newScriptVar_Fnc(Array, CTinyJS *Context, Array_t); private: void native_Length(const CFunctionsScopePtr &c, void *data); + bool toStringRecursion; }; inline define_newScriptVar_Fnc(Array, CTinyJS *Context, Array_t) { return new CScriptVarArray(Context); } @@ -1387,7 +1524,7 @@ class CScriptVarRegExp : public CScriptVarObject { virtual ~CScriptVarRegExp(); virtual CScriptVarPtr clone(); virtual bool isRegExp(); // { return true; } - virtual CScriptVarPtr toString_CallBack(bool &execute, int radix=0); + virtual CScriptVarPtr toString_CallBack(CScriptResult &execute, int radix=0); CScriptVarPtr exec(const std::string &Input, bool Test=false); @@ -1436,7 +1573,7 @@ class CScriptVarFunction : public CScriptVarObject { virtual std::string getVarType(); // { return "function"; } virtual std::string getParsableString(const std::string &indentString, const std::string &indent, uint32_t uniqueID, bool &hasRecursion); - virtual CScriptVarPtr toString_CallBack(bool &execute, int radix=0); + virtual CScriptVarPtr toString_CallBack(CScriptResult &execute, int radix=0); virtual CScriptTokenDataFnc *getFunctionData(); void setFunctionData(CScriptTokenDataFnc *Data); private: @@ -1460,8 +1597,8 @@ class CScriptVarFunctionBounded : public CScriptVarFunction { virtual ~CScriptVarFunctionBounded(); virtual CScriptVarPtr clone(); virtual bool isBounded(); ///< is CScriptVarFunctionBounded - virtual void setTemporaryID_recursive(uint32_t ID); - CScriptVarPtr callFunction(bool &execute, std::vector &Arguments, const CScriptVarPtr &This, CScriptVarPtr *newThis=0); + virtual void setTemporaryMark_recursive(uint32_t ID); + CScriptVarPtr callFunction(CScriptResult &execute, std::vector &Arguments, const CScriptVarPtr &This, CScriptVarPtr *newThis=0); protected: private: CScriptVarFunctionPtr boundedFunction; @@ -1480,7 +1617,9 @@ inline define_newScriptVar_NamedFnc(FunctionBounded, CScriptVarFunctionPtr Bound define_ScriptVarPtr_Type(FunctionNative); class CScriptVarFunctionNative : public CScriptVarFunction { protected: - CScriptVarFunctionNative(CTinyJS *Context, void *Userdata) : CScriptVarFunction(Context, new CScriptTokenDataFnc), jsUserData(Userdata) { } + CScriptVarFunctionNative(CTinyJS *Context, void *Userdata, const char *Name) : CScriptVarFunction(Context, new CScriptTokenDataFnc), jsUserData(Userdata) { + if(Name) getFunctionData()->name = Name; + } CScriptVarFunctionNative(const CScriptVarFunctionNative &Copy) : CScriptVarFunction(Copy), jsUserData(Copy.jsUserData) { } ///< Copy protected -> use clone for public public: virtual ~CScriptVarFunctionNative(); @@ -1500,7 +1639,7 @@ class CScriptVarFunctionNative : public CScriptVarFunction { define_ScriptVarPtr_Type(FunctionNativeCallback); class CScriptVarFunctionNativeCallback : public CScriptVarFunctionNative { protected: - CScriptVarFunctionNativeCallback(CTinyJS *Context, JSCallback Callback, void *Userdata) : CScriptVarFunctionNative(Context, Userdata), jsCallback(Callback) { } + CScriptVarFunctionNativeCallback(CTinyJS *Context, JSCallback Callback, void *Userdata, const char *Name) : CScriptVarFunctionNative(Context, Userdata, Name), jsCallback(Callback) { } CScriptVarFunctionNativeCallback(const CScriptVarFunctionNativeCallback &Copy) : CScriptVarFunctionNative(Copy), jsCallback(Copy.jsCallback) { } ///< Copy protected -> use clone for public public: virtual ~CScriptVarFunctionNativeCallback(); @@ -1508,9 +1647,9 @@ class CScriptVarFunctionNativeCallback : public CScriptVarFunctionNative { virtual void callFunction(const CFunctionsScopePtr &c); private: JSCallback jsCallback; ///< Callback for native functions - friend define_newScriptVar_Fnc(FunctionNativeCallback, CTinyJS *Context, JSCallback Callback, void*); + friend define_newScriptVar_Fnc(FunctionNativeCallback, CTinyJS *Context, JSCallback Callback, void*, const char*); }; -inline define_newScriptVar_Fnc(FunctionNativeCallback, CTinyJS *Context, JSCallback Callback, void *Userdata) { return new CScriptVarFunctionNativeCallback(Context, Callback, Userdata); } +inline define_newScriptVar_Fnc(FunctionNativeCallback, CTinyJS *Context, JSCallback Callback, void *Userdata, const char *Name=0) { return new CScriptVarFunctionNativeCallback(Context, Callback, Userdata, Name); } ////////////////////////////////////////////////////////////////////////// @@ -1520,7 +1659,7 @@ inline define_newScriptVar_Fnc(FunctionNativeCallback, CTinyJS *Context, JSCallb template class CScriptVarFunctionNativeClass : public CScriptVarFunctionNative { protected: - CScriptVarFunctionNativeClass(CTinyJS *Context, native *ClassPtr, void (native::*ClassFnc)(const CFunctionsScopePtr &, void *), void *Userdata) : CScriptVarFunctionNative(Context, Userdata), classPtr(ClassPtr), classFnc(ClassFnc) { } + CScriptVarFunctionNativeClass(CTinyJS *Context, native *ClassPtr, void (native::*ClassFnc)(const CFunctionsScopePtr &, void *), void *Userdata, const char *Name) : CScriptVarFunctionNative(Context, Userdata, Name), classPtr(ClassPtr), classFnc(ClassFnc) { } CScriptVarFunctionNativeClass(const CScriptVarFunctionNativeClass &Copy) : CScriptVarFunctionNative(Copy), classPtr(Copy.classPtr), classFnc(Copy.classFnc) { } ///< Copy protected -> use clone for public public: virtual CScriptVarPtr clone() { return new CScriptVarFunctionNativeClass(*this); } @@ -1530,10 +1669,10 @@ class CScriptVarFunctionNativeClass : public CScriptVarFunctionNative { native *classPtr; void (native::*classFnc)(const CFunctionsScopePtr &c, void *userdata); template - friend define_newScriptVar_Fnc(FunctionNativeCallback, CTinyJS*, native2 *, void (native2::*)(const CFunctionsScopePtr &, void *), void *); + friend define_newScriptVar_Fnc(FunctionNativeCallback, CTinyJS*, native2 *, void (native2::*)(const CFunctionsScopePtr &, void *), void *, const char *); }; template -define_newScriptVar_Fnc(FunctionNativeCallback, CTinyJS *Context, native *ClassPtr, void (native::*ClassFnc)(const CFunctionsScopePtr &, void *), void *Userdata) { return new CScriptVarFunctionNativeClass(Context, ClassPtr, ClassFnc, Userdata); } +define_newScriptVar_Fnc(FunctionNativeCallback, CTinyJS *Context, native *ClassPtr, void (native::*ClassFnc)(const CFunctionsScopePtr &, void *), void *Userdata, const char *Name=0) { return new CScriptVarFunctionNativeClass(Context, ClassPtr, ClassFnc, Userdata, Name); } ////////////////////////////////////////////////////////////////////////// @@ -1578,6 +1717,19 @@ template define_newScriptVar_NamedFnc(Accessor, CTinyJS *Context, C *cl inline define_newScriptVar_NamedFnc(Accessor, CTinyJS *Context, const CScriptVarFunctionPtr &getter, const CScriptVarFunctionPtr &setter) { return new CScriptVarAccessor(Context, getter, setter); } +////////////////////////////////////////////////////////////////////////// +/// CScriptVarDestructuring +////////////////////////////////////////////////////////////////////////// + +class CScriptVarDestructuring : public CScriptVarObject { +protected: // only derived classes or friends can be created + CScriptVarDestructuring(CTinyJS *Context) // constructor for rootScope + : CScriptVarObject(Context) {} + virtual CScriptVarPtr clone(); +public: + virtual ~CScriptVarDestructuring(); +}; + ////////////////////////////////////////////////////////////////////////// /// CScriptVarScope ////////////////////////////////////////////////////////////////////////// @@ -1680,6 +1832,103 @@ class CScriptVarScopeWith : public CScriptVarScopeLet { inline define_newScriptVar_Fnc(ScopeWith, CTinyJS *, ScopeWith_t, const CScriptVarScopePtr &Parent, const CScriptVarPtr &With) { return new CScriptVarScopeWith(Parent, With); } +////////////////////////////////////////////////////////////////////////// +/// CScriptVarDefaultIterator +////////////////////////////////////////////////////////////////////////// + +define_dummy_t(DefaultIterator); +define_ScriptVarPtr_Type(DefaultIterator); + +class CScriptVarDefaultIterator : public CScriptVarObject { +protected: + CScriptVarDefaultIterator(CTinyJS *Context, const CScriptVarPtr &Object, int Mode); + CScriptVarDefaultIterator(const CScriptVarDefaultIterator &Copy) + : + CScriptVarObject(Copy), mode(Copy.mode), object(Copy.object), + keys(Copy.keys), pos(keys.begin()){} ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarDefaultIterator(); + virtual CScriptVarPtr clone(); + virtual bool isIterator(); + + void native_next(const CFunctionsScopePtr &c, void *data); +private: + int mode; + CScriptVarPtr object; + STRING_SET_t keys; + STRING_SET_it pos; + friend define_newScriptVar_NamedFnc(DefaultIterator, CTinyJS *, const CScriptVarPtr &, int); + +}; +inline define_newScriptVar_NamedFnc(DefaultIterator, CTinyJS *Context, const CScriptVarPtr &Object, int Mode) { return new CScriptVarDefaultIterator(Context, Object, Mode); } + + +////////////////////////////////////////////////////////////////////////// +/// CScriptVarGenerator +////////////////////////////////////////////////////////////////////////// + +#ifndef NO_GENERATORS + +define_dummy_t(Generator); +define_ScriptVarPtr_Type(Generator); + +class CScriptVarGenerator : public CScriptVarObject { +protected: + CScriptVarGenerator(CTinyJS *Context, const CScriptVarPtr &FunctionRoot, const CScriptVarFunctionPtr &Function); + CScriptVarGenerator(const CScriptVarGenerator &Copy) + : + CScriptVarObject(Copy), functionRoot(Copy.functionRoot), function(Copy.function), coroutine(this) {} ///< Copy protected -> use clone for public +public: + virtual ~CScriptVarGenerator(); + virtual CScriptVarPtr clone(); + virtual bool isIterator(); + virtual bool isGenerator(); + virtual std::string getVarType(); // { return "generator"; } + virtual std::string getVarTypeTagName(); // { return "Generator"; } + + CScriptVarPtr getFunctionRoot() { return functionRoot; } + CScriptVarFunctionPtr getFunction() { return function; } + + virtual void setTemporaryMark_recursive(uint32_t ID); + + void native_send(const CFunctionsScopePtr &c, void *data); + void native_throw(const CFunctionsScopePtr &c, void *data); + int Coroutine(); + void setException(const CScriptVarPtr &YieldVar) { + yieldVar = YieldVar; + yieldVarIsException = true; + } + bool isClosed() { return closed; } + CScriptVarPtr yield(CScriptResult &execute, CScriptVar *YieldIn); +private: + CScriptVarPtr functionRoot; + CScriptVarFunctionPtr function; + bool closed; + CScriptVarPtr yieldVar; + bool yieldVarIsException; + class CCooroutine : public CScriptCoroutine { + CCooroutine(CScriptVarGenerator* Parent) : parent(Parent) {} + int Coroutine() { return parent->Coroutine(); } + void yield() { CScriptCoroutine::yield(); } + CScriptVarGenerator* parent; + friend class CScriptVarGenerator; + } coroutine; + friend class CCooroutine; + +public: + void *callersStackBase; + int callersScopeSize; + CScriptTokenizer *callersTokenizer; + bool callersHaveTry; + std::vector generatorScopes; + + friend define_newScriptVar_NamedFnc(CScriptVarGenerator, CTinyJS *, const CScriptVarPtr &, const CScriptVarFunctionPtr &); + +}; +inline define_newScriptVar_NamedFnc(CScriptVarGenerator, CTinyJS *Context, const CScriptVarPtr &FunctionRoot, const CScriptVarFunctionPtr &Function) { return new CScriptVarGenerator(Context, FunctionRoot, Function); } + +#endif + ////////////////////////////////////////////////////////////////////////// template inline CScriptVarPtr CScriptVar::newScriptVar(T t) { return ::newScriptVar(context, t); } @@ -1688,10 +1937,57 @@ inline CScriptVarPtr CScriptVar::newScriptVar(T1 t1, T2 t2) { return ::newScript //inline CScriptVarPtr newScriptVar(const CNumber &t) { return ::newScriptVar(context, t); } ////////////////////////////////////////////////////////////////////////// + +class CScriptResult { +public: + enum TYPE { + Normal, + Break, + Continue, + Return, + Throw, + noncatchableThrow, + noExecute + }; + CScriptResult() : type(Normal), throw_at_line(-1), throw_at_column(-1) {} +// CScriptResult(TYPE Type) : type(Type), throw_at_line(-1), throw_at_column(-1) {} +// ~RESULT() { if(type==Throw) throw value; } + bool isNormal() { return type==Normal; } + bool isBreak() { return type==Break; } + bool isContinue() { return type==Continue; } + bool isBreakContinue() { return type==Break || type==Continue; } + bool isReturn() { return type==Return; } + bool isReturnNormal() { return type==Return || type==Normal; } + bool isThrow() { return type==Throw; } + + + operator bool() const { return type==Normal; } + void set(TYPE Type, bool Clear=true) { type=Type; if(Clear) value.clear(), target.clear(); } + void set(TYPE Type, const CScriptVarPtr &Value) { type=Type; value=Value; } + void set(TYPE Type, const std::string &Target) { type=Type; target=Target; } + void setThrow(const CScriptVarPtr &Value, const std::string &File, int Line=-1, int Column=-1) { type=Throw; value=Value; throw_at_file=File, throw_at_line=Line; throw_at_column=Column; } + + void cThrow() { if(type==Throw) throw value; } + + CScriptResult &operator()(const CScriptResult &rhs) { if(rhs.type!=Normal) *this=rhs; return *this; } + + enum TYPE type; + CScriptVarPtr value; + std::string target; + std::string throw_at_file; + int throw_at_line; + int throw_at_column; + +}; + + + ////////////////////////////////////////////////////////////////////////// /// CTinyJS ////////////////////////////////////////////////////////////////////////// +typedef int (*native_require_read_fnc)(const std::string &Fname, std::string &Data); + class CTinyJS { public: CTinyJS(); @@ -1728,6 +2024,12 @@ class CTinyJS { * 'undefined' */ std::string evaluate(const std::string &code, const std::string &File="", int Line=0, int Column=0); + native_require_read_fnc setRequireReadFnc(native_require_read_fnc fnc) { + native_require_read_fnc old = native_require_read; + native_require_read = fnc; + return old; + } + /// add a native function to be called from TinyJS /** example: \code @@ -1771,19 +2073,18 @@ class CTinyJS { //CScriptVarPtr newScriptVar(const CNumber &t) { return ::newScriptVar(this, t); } template CScriptVarPtr newScriptVar(T t) { return ::newScriptVar(this, t); } template CScriptVarPtr newScriptVar(T1 t1, T2 t2) { return ::newScriptVar(this, t1, t2); } + template CScriptVarPtr newScriptVar(T1 t1, T2 t2, T3 t3) { return ::newScriptVar(this, t1, t2, t3); } const CScriptVarPtr &constScriptVar(Undefined_t) { return constUndefined; } const CScriptVarPtr &constScriptVar(Null_t) { return constNull; } const CScriptVarPtr &constScriptVar(NaN_t) { return constNaN; } const CScriptVarPtr &constScriptVar(Infinity t) { return t.Sig()<0 ? constInfinityNegative : constInfinityPositive; } const CScriptVarPtr &constScriptVar(bool Val) { return Val?constTrue:constFalse; } const CScriptVarPtr &constScriptVar(NegativeZero_t) { return constNegativZero; } - + const CScriptVarPtr &constScriptVar(StopIteration_t) { return constStopIteration; } private: - static bool noexecute; CScriptTokenizer *t; /// current tokenizer - int runtimeFlags; - std::string label; + bool haveTry; std::vectorscopes; CScriptVarScopePtr root; const CScriptVarScopePtr &scope() { return scopes.back(); } @@ -1812,6 +2113,10 @@ class CTinyJS { CScriptVarPtr regexpPrototype; /// Built in string class CScriptVarPtr numberPrototype; /// Built in number class CScriptVarPtr booleanPrototype; /// Built in boolean class + CScriptVarPtr iteratorPrototype; /// Built in iterator class +#ifndef NO_GENERATORS + CScriptVarPtr generatorPrototype; /// Built in generator class +#endif /*NO_GENERATORS*/ CScriptVarPtr functionPrototype; /// Built in function class const CScriptVarPtr &getErrorPrototype(ERROR_TYPES Type) { return errorPrototypes[Type]; } private: @@ -1824,16 +2129,17 @@ class CTinyJS { CScriptVarPtr constNegativZero; CScriptVarPtr constTrue; CScriptVarPtr constFalse; + CScriptVarPtr constStopIteration; + std::vector pseudo_refered; - CScriptVarPtr exceptionVar; /// containing the exception var by (runtimeFlags&RUNTIME_THROW) == true; - void CheckRightHandVar(bool &execute, CScriptVarLinkWorkPtr &link) + void CheckRightHandVar(CScriptResult &execute, CScriptVarLinkWorkPtr &link) { if(execute && link && !link->isOwned() && !link.hasReferencedOwner() && !link->getName().empty()) throwError(execute, ReferenceError, link->getName() + " is not defined", t->getPrevPos()); } - void CheckRightHandVar(bool &execute, CScriptVarLinkWorkPtr &link, CScriptTokenizer::ScriptTokenPosition &Pos) + void CheckRightHandVar(CScriptResult &execute, CScriptVarLinkWorkPtr &link, CScriptTokenizer::ScriptTokenPosition &Pos) { if(execute && link && !link->isOwned() && !link.hasReferencedOwner() && !link->getName().empty()) throwError(execute, ReferenceError, link->getName() + " is not defined", Pos); @@ -1842,36 +2148,38 @@ class CTinyJS { public: // function call CScriptVarPtr callFunction(const CScriptVarFunctionPtr &Function, std::vector &Arguments, const CScriptVarPtr &This, CScriptVarPtr *newThis=0); - CScriptVarPtr callFunction(bool &execute, const CScriptVarFunctionPtr &Function, std::vector &Arguments, const CScriptVarPtr &This, CScriptVarPtr *newThis=0); - /// clears the RUNTIME_THROW flag - /// clears the exceptionVar - /// returns the old exceptionVar - CScriptVarPtr removeExeptionVar() { runtimeFlags &= ~RUNTIME_THROW; CScriptVarPtr ret = exceptionVar; exceptionVar.clear(); return ret; } - // - // parsing - in order of precedence + CScriptVarPtr callFunction(CScriptResult &execute, const CScriptVarFunctionPtr &Function, std::vector &Arguments, const CScriptVarPtr &This, CScriptVarPtr *newThis=0); + ////////////////////////////////////////////////////////////////////////// +#ifndef NO_GENERATORS + std::vector generatorStack; + void generator_start(CScriptVarGenerator *Generator); + CScriptVarPtr generator_yield(CScriptResult &execute, CScriptVar *YieldIn); +#endif + ////////////////////////////////////////////////////////////////////////// - CScriptVarPtr mathsOp(bool &execute, const CScriptVarPtr &a, const CScriptVarPtr &b, int op); + // parsing - in order of precedence + CScriptVarPtr mathsOp(CScriptResult &execute, const CScriptVarPtr &a, const CScriptVarPtr &b, int op); private: - - void execute_var_init(bool hideLetScope, bool &execute); - void execute_destructuring(CScriptTokenDataObjectLiteral &Objc, const CScriptVarPtr &Val, bool &execute); - CScriptVarLinkWorkPtr execute_literals(bool &execute); - CScriptVarLinkWorkPtr execute_member(CScriptVarLinkWorkPtr &parent, bool &execute); - CScriptVarLinkWorkPtr execute_function_call(bool &execute); - bool execute_unary_rhs(bool &execute, CScriptVarLinkWorkPtr& a); - CScriptVarLinkWorkPtr execute_unary(bool &execute); - CScriptVarLinkWorkPtr execute_term(bool &execute); - CScriptVarLinkWorkPtr execute_expression(bool &execute); - CScriptVarLinkWorkPtr execute_binary_shift(bool &execute); - CScriptVarLinkWorkPtr execute_relation(bool &execute, int set=LEX_EQUAL, int set_n='<'); - CScriptVarLinkWorkPtr execute_binary_logic(bool &execute, int op='|', int op_n1='^', int op_n2='&'); - CScriptVarLinkWorkPtr execute_logic(bool &execute, int op=LEX_OROR, int op_n=LEX_ANDAND); - CScriptVarLinkWorkPtr execute_condition(bool &execute); - CScriptVarLinkPtr execute_assignment(CScriptVarLinkWorkPtr Lhs, bool &execute); - CScriptVarLinkPtr execute_assignment(bool &execute); - CScriptVarLinkPtr execute_base(bool &execute); - void execute_block(bool &execute, bool createLetScope=true); - CScriptVarLinkPtr execute_statement(bool &execute); + void assign_destructuring_var(const CScriptVarPtr &Scope, const CScriptTokenDataDestructuringVar &Objc, const CScriptVarPtr &Val, CScriptResult &execute); + void execute_var_init(bool hideLetScope, CScriptResult &execute); + void execute_destructuring(CScriptTokenDataObjectLiteral &Objc, const CScriptVarPtr &Val, CScriptResult &execute); + CScriptVarLinkWorkPtr execute_literals(CScriptResult &execute); + CScriptVarLinkWorkPtr execute_member(CScriptVarLinkWorkPtr &parent, CScriptResult &execute); + CScriptVarLinkWorkPtr execute_function_call(CScriptResult &execute); + bool execute_unary_rhs(CScriptResult &execute, CScriptVarLinkWorkPtr& a); + CScriptVarLinkWorkPtr execute_unary(CScriptResult &execute); + CScriptVarLinkWorkPtr execute_term(CScriptResult &execute); + CScriptVarLinkWorkPtr execute_expression(CScriptResult &execute); + CScriptVarLinkWorkPtr execute_binary_shift(CScriptResult &execute); + CScriptVarLinkWorkPtr execute_relation(CScriptResult &execute, int set=LEX_EQUAL, int set_n='<'); + CScriptVarLinkWorkPtr execute_binary_logic(CScriptResult &execute, int op='|', int op_n1='^', int op_n2='&'); + CScriptVarLinkWorkPtr execute_logic(CScriptResult &execute, int op=LEX_OROR, int op_n=LEX_ANDAND); + CScriptVarLinkWorkPtr execute_condition(CScriptResult &execute); + CScriptVarLinkPtr execute_assignment(CScriptVarLinkWorkPtr Lhs, CScriptResult &execute); + CScriptVarLinkPtr execute_assignment(CScriptResult &execute); + CScriptVarLinkPtr execute_base(CScriptResult &execute); + void execute_block(CScriptResult &execute); + void execute_statement(CScriptResult &execute); // parsing utility functions CScriptVarLinkWorkPtr parseFunctionDefinition(const CScriptToken &FncToken); CScriptVarLinkWorkPtr parseFunctionsBodyFromString(const std::string &ArgumentList, const std::string &FncBody); @@ -1885,9 +2193,9 @@ class CTinyJS { ////////////////////////////////////////////////////////////////////////// /// throws an Error & Exception public: - void throwError(bool &execute, ERROR_TYPES ErrorType, const std::string &message); + void throwError(CScriptResult &execute, ERROR_TYPES ErrorType, const std::string &message); void throwException(ERROR_TYPES ErrorType, const std::string &message); - void throwError(bool &execute, ERROR_TYPES ErrorType, const std::string &message, CScriptTokenizer::ScriptTokenPosition &Pos); + void throwError(CScriptResult &execute, ERROR_TYPES ErrorType, const std::string &message, CScriptTokenizer::ScriptTokenPosition &Pos); void throwException(ERROR_TYPES ErrorType, const std::string &message, CScriptTokenizer::ScriptTokenPosition &Pos); private: ////////////////////////////////////////////////////////////////////////// @@ -1921,6 +2229,11 @@ class CTinyJS { void native_Boolean(const CFunctionsScopePtr &c, void *data); + void native_Iterator(const CFunctionsScopePtr &c, void *data); + +// void native_Generator(const CFunctionsScopePtr &c, void *data); + void native_Generator_prototype_next(const CFunctionsScopePtr &c, void *data); + void native_Function(const CFunctionsScopePtr &c, void *data); void native_Function_prototype_call(const CFunctionsScopePtr &c, void *data); void native_Function_prototype_apply(const CFunctionsScopePtr &c, void *data); @@ -1938,22 +2251,38 @@ class CTinyJS { /// global function void native_eval(const CFunctionsScopePtr &c, void *data); + void native_require(const CFunctionsScopePtr &c, void *data); + native_require_read_fnc native_require_read; void native_isNAN(const CFunctionsScopePtr &c, void *data); void native_isFinite(const CFunctionsScopePtr &c, void *data); void native_parseInt(const CFunctionsScopePtr &c, void *data); void native_parseFloat(const CFunctionsScopePtr &c, void *data); - - void native_JSON_parse(const CFunctionsScopePtr &c, void *data); uint32_t uniqueID; + int32_t currentMarkSlot; + void *stackBase; public: - uint32_t getUniqueID() { return ++uniqueID; } + int32_t getCurrentMarkSlot() { + ASSERT(currentMarkSlot >= 0); // UniqueID not allocated + return currentMarkSlot; + } + uint32_t allocUniqueID() { + ++currentMarkSlot; + ASSERT(currentMarkSlot= 0); // freeUniqueID without allocUniqueID + --currentMarkSlot; + } CScriptVar *first; void setTemporaryID_recursive(uint32_t ID); void ClearUnreferedVars(const CScriptVarPtr &extra=CScriptVarPtr()); + void setStackBase(void * StackBase) { stackBase = StackBase; } + void setStackBase(uint32_t StackSize) { char dummy; stackBase = StackSize ? &dummy-StackSize : 0; } }; @@ -1962,7 +2291,11 @@ template inline const CScriptVarPtr &CScriptVar::constScriptVar(T t) { return context->constScriptVar(t); } ////////////////////////////////////////////////////////////////////////// inline CNumber CScriptVarLink::toNumber() { return var->toNumber(); } -inline CNumber CScriptVarLink::toNumber(bool &execute) { return var->toNumber(execute); } +inline CNumber CScriptVarLink::toNumber(CScriptResult &execute) { return var->toNumber(execute); } + +inline void CScriptVar::setTemporaryMark(uint32_t ID) { temporaryMark[context->getCurrentMarkSlot()] = ID; } +inline uint32_t CScriptVar::getTemporaryMark() { return temporaryMark[context->getCurrentMarkSlot()]; } + #endif diff --git a/TinyJS_Functions.cpp b/TinyJS_Functions.cpp index 35205a2..df3d7c5 100755 --- a/TinyJS_Functions.cpp +++ b/TinyJS_Functions.cpp @@ -14,7 +14,7 @@ * * Authored / Changed By Armin Diedering * - * Copyright (C) 2010-2012 ardisoft + * Copyright (C) 2010-2014 ardisoft * * * Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -62,7 +62,7 @@ static void scObjectClone(const CFunctionsScopePtr &c, void *) { CScriptVarPtr obj = c->getArgument("this"); c->setReturnVar(obj->clone()); } - +/* static void scIntegerValueOf(const CFunctionsScopePtr &c, void *) { string str = c->getArgument("str")->toString(); @@ -71,11 +71,12 @@ static void scIntegerValueOf(const CFunctionsScopePtr &c, void *) { val = str.operator[](0); c->setReturnVar(c->newScriptVar(val)); } - +*/ static void scJSONStringify(const CFunctionsScopePtr &c, void *) { - uint32_t UniqueID = c->getContext()->getUniqueID(); + uint32_t UniqueID = c->getContext()->allocUniqueID(); bool hasRecursion=false; c->setReturnVar(c->newScriptVar(c->getArgument("obj")->getParsableString("", " ", UniqueID, hasRecursion))); + c->getContext()->freeUniqueID(); if(hasRecursion) c->throwError(TypeError, "cyclic object value"); } @@ -152,7 +153,7 @@ extern "C" void _registerFunctions(CTinyJS *tinyJS) { tinyJS->addNative("function Object.prototype.dump()", scObjectDump, 0, SCRIPTVARLINK_BUILDINDEFAULT); tinyJS->addNative("function Object.prototype.clone()", scObjectClone, 0, SCRIPTVARLINK_BUILDINDEFAULT); - tinyJS->addNative("function Integer.valueOf(str)", scIntegerValueOf, 0, SCRIPTVARLINK_BUILDINDEFAULT); // value of a single character +// tinyJS->addNative("function Integer.valueOf(str)", scIntegerValueOf, 0, SCRIPTVARLINK_BUILDINDEFAULT); // value of a single character tinyJS->addNative("function JSON.stringify(obj, replacer)", scJSONStringify, 0, SCRIPTVARLINK_BUILDINDEFAULT); // convert to JSON. replacer is ignored at the moment tinyJS->addNative("function Array.prototype.contains(obj)", scArrayContains, 0, SCRIPTVARLINK_BUILDINDEFAULT); tinyJS->addNative("function Array.prototype.remove(obj)", scArrayRemove, 0, SCRIPTVARLINK_BUILDINDEFAULT); diff --git a/TinyJS_Functions.h b/TinyJS_Functions.h index 105f7d2..9e1fa07 100755 --- a/TinyJS_Functions.h +++ b/TinyJS_Functions.h @@ -14,7 +14,7 @@ * * Authored / Changed By Armin Diedering * - * Copyright (C) 2010-2012 ardisoft + * Copyright (C) 2010-2014 ardisoft * * * Permission is hereby granted, free of charge, to any person obtaining a copy of diff --git a/TinyJS_MathFunctions.cpp b/TinyJS_MathFunctions.cpp index ab4215d..5f1ece9 100644 --- a/TinyJS_MathFunctions.cpp +++ b/TinyJS_MathFunctions.cpp @@ -16,7 +16,7 @@ * * Authored / Changed By Armin Diedering * - * Copyright (C) 2010-2012 ardisoft + * Copyright (C) 2010-2014 ardisoft * * * Permission is hereby granted, free of charge, to any person obtaining a copy of diff --git a/TinyJS_MathFunctions.h b/TinyJS_MathFunctions.h index f135ebb..ea87da6 100644 --- a/TinyJS_MathFunctions.h +++ b/TinyJS_MathFunctions.h @@ -16,7 +16,7 @@ * * Authored / Changed By Armin Diedering * - * Copyright (C) 2010-2012 ardisoft + * Copyright (C) 2010-2014 ardisoft * * * Permission is hereby granted, free of charge, to any person obtaining a copy of diff --git a/TinyJS_StringFunctions.cpp b/TinyJS_StringFunctions.cpp index cc8907a..99fa725 100644 --- a/TinyJS_StringFunctions.cpp +++ b/TinyJS_StringFunctions.cpp @@ -5,7 +5,7 @@ * * Authored By Armin Diedering * - * Copyright (C) 2010-2012 ardisoft + * Copyright (C) 2010-2014 ardisoft * * * Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -90,7 +90,7 @@ static void scStringIndexOf(const CFunctionsScopePtr &c, void *userdata) { if(pos_n.sign()<0) pos = 0; else if(pos_n.isInfinity()) pos = string::npos; else if(pos_n.isFinite()) pos = pos_n.toInt32(); - string::size_type p = (userdata==0) ? str.find(search) : str.rfind(search); + string::size_type p = (userdata==0) ? str.find(search, pos) : str.rfind(search, pos); int val = (p==string::npos) ? -1 : p; c->setReturnVar(c->newScriptVar(val)); } @@ -229,7 +229,18 @@ static void scStringReplace(const CFunctionsScopePtr &c, void *) { arguments.pop_back(); } ret_str.append(newsubstr); +#if 1 /* Fix from "vcmpeq" (see Issue 14) currently untested */ + if (match_begin == match_end) { + if (search_begin != str.end()) + ++search_begin; + else + break; + } else { + search_begin = match_end; + } +#else search_begin = match_end; +#endif } while(global && search(str, search_begin, substr, ignoreCase, sticky, match_begin, match_end)); } ret_str.append(search_begin, str.end()); @@ -264,7 +275,18 @@ static void scStringMatch(const CFunctionsScopePtr &c, void *) { do { offset = match_begin-str.begin(); retVar->addChild(int2string(idx++), c->newScriptVar(string(match_begin, match_end))); +#if 1 /* Fix from "vcmpeq" (see Issue 14) currently untested */ + if (match_begin == match_end) { + if (search_begin != str.end()) + ++search_begin; + else + break; + } else { + search_begin = match_end; + } +#else search_begin = match_end; +#endif } while(global && regex_search(str, search_begin, substr, ignoreCase, sticky, match_begin, match_end)); } if(idx) { @@ -324,30 +346,37 @@ static void scStringSplit(const CFunctionsScopePtr &c, void *) { string seperator; bool global, ignoreCase, sticky; +#ifndef NO_REGEXP CScriptVarRegExpPtr RegExp = getRegExpData(c, "separator", true, 0, seperator, global, ignoreCase, sticky); - +#else + getRegExpData(c, "separator", true, 0, seperator, global, ignoreCase, sticky); +#endif + CScriptVarPtr sep_var = c->getArgument("separator"); CScriptVarPtr limit_var = c->getArgument("limit"); int limit = limit_var->isUndefined() ? 0x7fffffff : limit_var->toNumber().toInt32(); CScriptVarPtr result(newScriptVar(c->getContext(), Array)); c->setReturnVar(result); - if(limit == 0 || !str.size()) + if(limit == 0) return; - else if(sep_var->isUndefined()) { + else if(!str.size() || sep_var->isUndefined()) { result->setArrayIndex(0, c->newScriptVar(str)); return; } if(seperator.size() == 0) { - for(int i=0; isetArrayIndex(i, c->newScriptVar(str.substr(i,1))); return; } int length = 0; string::const_iterator search_begin=str.begin(), match_begin, match_end; +#ifndef NO_REGEXP smatch match; +#endif bool found=true; while(found) { +#ifndef NO_REGEXP if(RegExp) { try { found = regex_search(str, search_begin, seperator, ignoreCase, sticky, match_begin, match_end, match); @@ -355,11 +384,13 @@ static void scStringSplit(const CFunctionsScopePtr &c, void *) { c->throwError(SyntaxError, string(e.what())+" - "+CScriptVarRegExp::ErrorStr(e.code())); } } else /* NO_REGEXP */ +#endif found = string_search(str, search_begin, seperator, ignoreCase, sticky, match_begin, match_end); string f; if(found) { result->setArrayIndex(length++, c->newScriptVar(string(search_begin, match_begin))); if(length>=limit) break; +#ifndef NO_REGEXP for(uint32_t i=1; isetArrayIndex(length++, c->newScriptVar(string(match[i].first, match[i].second))); @@ -368,6 +399,7 @@ static void scStringSplit(const CFunctionsScopePtr &c, void *) { if(length>=limit) break; } if(length>=limit) break; +#endif search_begin = match_end; } else { result->setArrayIndex(length++, c->newScriptVar(string(search_begin,str.end()))); diff --git a/TinyJS_StringFunctions.h b/TinyJS_StringFunctions.h index 9db9728..840fddf 100644 --- a/TinyJS_StringFunctions.h +++ b/TinyJS_StringFunctions.h @@ -5,7 +5,7 @@ * * Authored By Armin Diedering * - * Copyright (C) 2010-2012 ardisoft + * Copyright (C) 2010-2014 ardisoft * * * Permission is hereby granted, free of charge, to any person obtaining a copy of diff --git a/TinyJS_Threading.cpp b/TinyJS_Threading.cpp index 048711d..a802991 100644 --- a/TinyJS_Threading.cpp +++ b/TinyJS_Threading.cpp @@ -3,26 +3,40 @@ #undef HAVE_THREADING #if !defined(NO_THREADING) && !defined(HAVE_CUSTOM_THREADING_IMPL) # define HAVE_THREADING -# if defined(WIN32) && !defined(HAVE_PTHREAD) -# include +# ifdef HAVE_CXX_THREADS +# include # else -# include +# if defined(WIN32) && !defined(HAVE_PTHREAD) +# include +# else +# include +# ifndef HAVE_PTHREAD +# define HAVE_PTHREAD +# endif +# endif # endif #endif #ifdef HAVE_THREADING +////////////////////////////////////////////////////////////////////////// +// Mutex +////////////////////////////////////////////////////////////////////////// +#ifndef HAVE_CXX_THREADS + #ifndef HAVE_PTHREAD -// simple pthreads 4 windows -# define pthread_t HANDLE -# define pthread_create(t, stack, fnc, a) *(t) = CreateThread(NULL, stack, (LPTHREAD_START_ROUTINE)fnc, a, 0, NULL) -# define pthread_join(t, v) WaitForSingleObject(t, INFINITE), GetExitCodeThread(t,(LPDWORD)v), CloseHandle(t) +// simple mutex 4 windows +//# define pthread_mutex_t HANDLE +//# define pthread_mutex_init(m, a) *(m) = CreateMutex(NULL, false, NULL) +//# define pthread_mutex_destroy(m) CloseHandle(*(m)); +//# define pthread_mutex_lock(m) WaitForSingleObject(*(m), INFINITE); +//# define pthread_mutex_unlock(m) ReleaseMutex(*(m)); +# define pthread_mutex_t CRITICAL_SECTION +# define pthread_mutex_init(m, a) { InitializeCriticalSection(m); 0; } +# define pthread_mutex_destroy(m) do {} while(0) +# define pthread_mutex_lock(m) EnterCriticalSection(m) +# define pthread_mutex_unlock(m) LeaveCriticalSection(m) -# define pthread_mutex_t HANDLE -# define pthread_mutex_init(m, a) *(m) = CreateMutex(NULL, false, NULL) -# define pthread_mutex_destroy(m) CloseHandle(*(m)); -# define pthread_mutex_lock(m) WaitForSingleObject(*(m), INFINITE); -# define pthread_mutex_unlock(m) ReleaseMutex(*(m)); #endif class CScriptMutex_impl : public CScriptMutex::CScriptMutex_t { @@ -39,9 +53,11 @@ class CScriptMutex_impl : public CScriptMutex::CScriptMutex_t { void unlock() { pthread_mutex_unlock(&mutex); } + void *getRealMutex() { return &mutex; } pthread_mutex_t mutex; }; + CScriptMutex::CScriptMutex() { mutex = new CScriptMutex_impl; } @@ -50,31 +66,113 @@ CScriptMutex::~CScriptMutex() { delete mutex; } +#endif /*HAVE_CXX_THREADS*/ + +////////////////////////////////////////////////////////////////////////// +// CondVar +////////////////////////////////////////////////////////////////////////// + +#ifndef HAVE_CXX_THREADS + +#ifndef HAVE_PTHREAD +// simple conditional Variable 4 windows +# define pthread_cond_t CONDITION_VARIABLE +# define pthread_cond_init(c, a) InitializeConditionVariable(c) +# define pthread_cond_destroy(c) do {} while(0) +# define pthread_cond_wait(c, m) SleepConditionVariableCS(c, m, INFINITE) +# define pthread_cond_signal(c) WakeConditionVariable(c); +#endif + +class CScriptCondVar_impl : public CScriptCondVar::CScriptCondVar_t { +public: + CScriptCondVar_impl(CScriptCondVar *_this) : This(_this) { + pthread_cond_init(&cond, NULL); + } + ~CScriptCondVar_impl() { + pthread_cond_destroy(&cond); + } + CScriptCondVar *This; + void notify_one() { + pthread_cond_signal(&cond); + } + void wait(CScriptUniqueLock &Lock) { + pthread_cond_wait(&cond, (pthread_mutex_t *)Lock.mutex->getRealMutex()); + } + pthread_cond_t cond; +}; + +CScriptCondVar::CScriptCondVar() { + condVar = new CScriptCondVar_impl(this); +} +CScriptCondVar::~CScriptCondVar() { + delete condVar; +} + +#endif /*HAVE_CXX_THREADS*/ + + + +////////////////////////////////////////////////////////////////////////// +// Threading +////////////////////////////////////////////////////////////////////////// + + +#ifdef HAVE_CXX_THREADS +# define pthread_attr_t int +# define pthread_attr_init(a) do {} while(0) +# define pthread_attr_destroy(a) do {} while(0) +# define pthread_t std::thread +# define pthread_create(t, attr, fnc, a) *(t) = std::thread(fnc, this); +# define pthread_join(t, v) t.join(); +#elif !defined(HAVE_PTHREAD) +// simple pthreads 4 windows +# define pthread_attr_t SIZE_T +# define pthread_attr_init(attr) (*attr=0) +# define pthread_attr_destroy(a) do {} while(0) +# define pthread_attr_setstacksize(attr, stack) *attr=stack; + +# define pthread_t HANDLE +# define pthread_create(t, attr, fnc, a) *(t) = CreateThread(NULL, attr ? *((pthread_attr_t*)attr) : 0, (LPTHREAD_START_ROUTINE)fnc, a, 0, NULL) +# define pthread_join(t, v) WaitForSingleObject(t, INFINITE), GetExitCodeThread(t,(LPDWORD)v), CloseHandle(t) +#endif + class CScriptThread_impl : public CScriptThread::CScriptThread_t { public: - CScriptThread_impl(CScriptThread *_this) : activ(false), running(false), This(_this) {} + CScriptThread_impl(CScriptThread *_this) : retvar((void*)-1), activ(false), running(false), started(false), This(_this) {} ~CScriptThread_impl() {} void Run() { - if(running) return; + if(started) return; activ = true; - pthread_create(&thread, NULL, (void*(*)(void*))ThreadFnc, this); - while(!running); +// pthread_attr_t attribute; +// pthread_attr_init(&attribute); +// pthread_attr_setstacksize(&attribute,1024); + pthread_create(&thread, NULL /*&attribute*/, (void*(*)(void*))ThreadFnc, this); +// pthread_attr_destroy(&attribute); + while(!started); } - int Stop() { - if(!running) return -1; - void *retvar; + int Stop(bool Wait) { + if(!running) return started ? retValue() : -1; activ = false; - pthread_join(thread, &retvar); - running = false; + if(Wait) { + pthread_join(thread, &retvar); + } return (int)retvar; } + int retValue() { return (int)retvar; } bool isActiv() { return activ; } + bool isRunning() { return running; } + bool isStarted() { return started; } static void *ThreadFnc(CScriptThread_impl *This) { - This->running = true; - return (void*) This->This->ThreadFnc(); + This->running = This->started = true; + This->retvar = (void*)This->This->ThreadFnc(); + This->running = false; + This->This->ThreadFncFinished(); + return This->retvar; } + void *retvar; bool activ; bool running; + bool started; CScriptThread *This; pthread_t thread; }; @@ -85,4 +183,54 @@ CScriptThread::CScriptThread() { CScriptThread::~CScriptThread() { delete thread; } -#endif // HAVE_THREADING \ No newline at end of file +void CScriptThread::ThreadFncFinished() {} + +CScriptCoroutine::StopIteration_t CScriptCoroutine::StopIteration; + +bool CScriptCoroutine::next() +{ + if(!isStarted()) { + Run(); + wake_main.wait(); + } else if(isRunning()) { + wake_thread.post(); + wake_main.wait(); + } else + return false; + if(!isRunning()) return false; + return true; +} +bool CScriptCoroutine::yield_no_throw() { + wake_main.post(); + wake_thread.wait(); + return isActiv(); +} +void CScriptCoroutine::yield() { + wake_main.post(); + wake_thread.wait(); + if(!isActiv()) { + throw StopIteration; + } +} +int CScriptCoroutine::ThreadFnc() { + int ret=-1; + try { + ret = Coroutine(); + } catch(StopIteration_t &) { + return 0; + } catch(std::exception & e) { + printf("CScriptCoroutine has received an uncaught exception: %s\n", e.what()); + return -1; + } catch(...) { + printf("CScriptCoroutine has received an uncaught and unknown exception\n"); + return -1; + } + return ret; +} +void CScriptCoroutine::ThreadFncFinished() { + wake_main.post(); +} + + +#endif // HAVE_THREADING + diff --git a/TinyJS_Threading.h b/TinyJS_Threading.h index 790c8fe..9b36607 100644 --- a/TinyJS_Threading.h +++ b/TinyJS_Threading.h @@ -9,7 +9,7 @@ * * Authored By Armin Diedering * - * Copyright (C) 2010-2012 ardisoft + * Copyright (C) 2010-2014 ardisoft * * * Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -31,40 +31,130 @@ * SOFTWARE. */ +#ifdef HAVE_CXX_THREADS + +# include +# include +typedef std::mutex CScriptMutex; +typedef std::unique_lock CScriptUniqueLock; +typedef std::condition_variable CScriptCondVar; + +#else + class CScriptMutex { public: CScriptMutex(); ~CScriptMutex(); void lock() { mutex->lock(); } void unlock() { mutex->unlock(); } + void *getRealMutex() { return mutex->getRealMutex(); } class CScriptMutex_t{ public: // virtual ~CScriptMutex_t()=0; virtual void lock()=0; virtual void unlock()=0; + virtual void *getRealMutex()=0; }; private: CScriptMutex_t *mutex; }; +class CScriptUniqueLock { +public: + CScriptUniqueLock(CScriptMutex &Mutex) : mutex(&Mutex) { mutex->lock(); } + ~CScriptUniqueLock() { mutex->unlock(); } + CScriptMutex *mutex; +}; + +class CScriptCondVar { +public: + CScriptCondVar(); + virtual ~CScriptCondVar(); + void notify_one() { condVar->notify_one(); } + void wait(CScriptUniqueLock &Lock) { condVar->wait(Lock); } + class CScriptCondVar_t{ + public: + virtual void notify_one()=0; + virtual void wait(CScriptUniqueLock &Lock)=0; + }; +private: + CScriptCondVar_t *condVar; +}; + +#endif /*HAVE_CXX_THREADS*/ + + +class CScriptSemaphore +{ +private: + CScriptCondVar cond; + CScriptMutex mutex; + unsigned int count; + unsigned int max_count; + +public: + CScriptSemaphore(unsigned int currentCount=1, unsigned int maxCount=1) : count(currentCount), max_count(maxCount) {} + void post() { + CScriptUniqueLock lock(mutex); + + ++count; + cond.notify_one(); + } + void wait() { + CScriptUniqueLock lock(mutex); + while(!count) cond.wait(lock); + --count; + } + unsigned int currentWaits() { + CScriptUniqueLock lock(mutex); + return count; + } +}; + class CScriptThread { public: CScriptThread(); virtual ~CScriptThread(); void Run() { thread->Run(); } - int Stop() { return thread->Stop(); } + int Stop(bool Wait=true) { return thread->Stop(Wait); } + int retValue() { return thread->retValue(); } virtual int ThreadFnc()=0; + virtual void ThreadFncFinished(); bool isActiv() { return thread->isActiv(); } + bool isRunning() { return thread->isRunning(); } + bool isStarted() { return thread->isStarted(); } class CScriptThread_t{ public: virtual void Run()=0; - virtual int Stop()=0; + virtual int Stop(bool Wait)=0; virtual bool isActiv()=0; + virtual bool isRunning()=0; + virtual bool isStarted()=0; + virtual int retValue()=0; }; private: CScriptThread_t *thread; }; +class CScriptCoroutine : protected CScriptThread { +public: + CScriptCoroutine() : wake_thread(0), wake_main(0) {} + typedef struct{} StopIteration_t; + static StopIteration_t StopIteration; + bool next(); // returns true if coroutine is running +protected: + virtual int Coroutine()=0; + void yield(); + bool yield_no_throw(); +private: + virtual int ThreadFnc(); + virtual void ThreadFncFinished(); + CScriptSemaphore wake_thread; + CScriptSemaphore wake_main; +}; + + + #endif // NO_THREADING #endif // TinyJS_Threading_h__ diff --git a/config.h b/config.h index 041a512..0642030 100644 --- a/config.h +++ b/config.h @@ -8,7 +8,7 @@ * * Authored By Armin Diedering * - * Copyright (C) 2010-2012 ardisoft + * Copyright (C) 2010-2014 ardisoft * * * Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -83,6 +83,16 @@ */ //#define PREVENT_REDECLARATION_IN_FUNCTION_SCOPES +////////////////////////////////////////////////////////////////////////// + +/* GENERATOR's + * =========== + * functions with "yield" in it is detected as Generator. + * Generator-support needs threading-stuff + * To disable Generators define NO_GENERATORS + */ +//#define NO_GENERATORS + ////////////////////////////////////////////////////////////////////////// @@ -90,14 +100,20 @@ * =============== * 42TinyJS is basically thread-save. * You can run different or the same JS-code simultaneously in different instances of class TinyJS. - * The threading-stuff is currently only needed by the pool-allocator + * >>> NOTE: You can NOT run more threads on the SAME instance of class TinyJS <<< + * The threading-stuff is needed by the pool-allocator (locking) and the generator-/yield-stuff * to deactivate threading define NO_THREADING * NOTE: if NO_POOL_ALLOCATOR not defined you can not run JS-code simultaneously - * NO_POOL_ALLOCATOR implies NO_THREADING */ //#define NO_THREADING -/* on Windows the windows-threading-API is used by default. +/* with C++2011 (or MS VisualC++ 2012 and above) the C++ 2011 STL-Threading-API is used. + * You can define NO_CXX_THREADS to use alternate API's + */ +//#define NO_CXX_THREADS + +/* if C++ 2011 STL-Threading-API not available + * on Windows the windows-threading-API is used by default. * on non-Windows (WIN32 is not defined) it is tried to use the POSIX pthread-API * to force the pthread-API define HAVE_PTHREAD (windows needs in this case * a pthread-lib e.g http://http://sourceware.org/pthreads-win32/) @@ -113,7 +129,15 @@ // DO NOT MAKE CHANGES OF THE FOLLOWING STUFF // //////////////////////////////////////////////// -#if defined(NO_POOL_ALLOCATOR) && !defined(NO_THREADING) +#if defined(NO_THREADING) && !defined(NO_GENERATORS) +# define NO_GENERATORS +#pragma message("\n***********************************************************************\n\ +* You have defined NO_THREADING and not defined NO_GENERATORS\n\ +* NOTE: GENERATORS needs THREADING. Generators/Yield are deactivated\n\ +***********************************************************************\n") +#endif + +#if defined(NO_POOL_ALLOCATOR) && defined(NO_GENERATORS) && !defined(NO_THREADING) # define NO_THREADING #endif @@ -124,5 +148,30 @@ ***********************************************************************\n") #endif +#if defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L + +# define HAVE_CXX11_RVALUE_REFERENCE 1 +# define MEMBER_DELETE =delete + +# if !defined(NO_CXX_THREADS) && !defined(NO_THREADING) +# define HAVE_CXX_THREADS 1 +# endif +#else +# if _MSC_VER >= 1600 +# define HAVE_CXX11_RVALUE_REFERENCE 1 +# endif +# if _MSC_VER >= 1700 +# if !defined(NO_CXX_THREADS) && !defined(NO_THREADING) +# define HAVE_CXX_THREADS 1 +# endif +# endif +# if _MSC_VER >= 1800 +# define define MEMBER_DELETE =delete +# endif +#endif + +#ifndef MEMBER_DELETE +# define MEMBER_DELETE +#endif #endif // _42TinyJS_config_h__ diff --git a/lib-tiny-js.2012.vcxproj b/lib-tiny-js.2012.vcxproj new file mode 100644 index 0000000..0f8607d --- /dev/null +++ b/lib-tiny-js.2012.vcxproj @@ -0,0 +1,95 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {9751C465-0294-43CD-A5D9-BD038ABA3961} + lib-tiny-js + lib-tiny-js + + + + StaticLibrary + false + MultiByte + true + false + v110 + + + StaticLibrary + false + false + MultiByte + v110 + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + $(SolutionDir)$(Configuration)\ + Build\$(ProjectName)\$(Configuration)\ + $(SolutionDir)$(Configuration)\ + Build\$(ProjectName)\$(Configuration)\ + E:\Eigene Dateien\Visual Studio 2010\Projects\42tiny-js\pthread\pthreads-w32-2-9-1-release;$(IncludePath) + + + + Disabled + WIN32;_DEBUG;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + Level3 + EditAndContinue + + + + + Full + true + WIN32;NDEBUG;%(PreprocessorDefinitions) + MultiThreadedDLL + true + Level3 + ProgramDatabase + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lib-tiny-js.2012.vcxproj.filters b/lib-tiny-js.2012.vcxproj.filters new file mode 100644 index 0000000..ec27f62 --- /dev/null +++ b/lib-tiny-js.2012.vcxproj.filters @@ -0,0 +1,63 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav + + + + + Quelldateien + + + Quelldateien + + + Quelldateien + + + Quelldateien + + + Quelldateien + + + Quelldateien + + + + + Headerdateien + + + Headerdateien + + + Headerdateien + + + Headerdateien + + + Headerdateien + + + Headerdateien + + + Headerdateien + + + Headerdateien + + + \ No newline at end of file diff --git a/pool_allocator.cpp b/pool_allocator.cpp index 92739c9..1478501 100644 --- a/pool_allocator.cpp +++ b/pool_allocator.cpp @@ -5,7 +5,7 @@ * * Authored By Armin Diedering * - * Copyright (C) 2010-2012 ardisoft + * Copyright (C) 2010-2014 ardisoft * * * Permission is hereby granted, free of charge, to any person obtaining a copy of diff --git a/pool_allocator.h b/pool_allocator.h index 4a16c0c..6072e6f 100644 --- a/pool_allocator.h +++ b/pool_allocator.h @@ -5,7 +5,7 @@ * * Authored By Armin Diedering * - * Copyright (C) 2010-2012 ardisoft + * Copyright (C) 2010-2014 ardisoft * * * Permission is hereby granted, free of charge, to any person obtaining a copy of diff --git a/run_tests.2012.vcxproj b/run_tests.2012.vcxproj new file mode 100644 index 0000000..d6aad31 --- /dev/null +++ b/run_tests.2012.vcxproj @@ -0,0 +1,96 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {C6C6CD82-1400-464C-9B80-E15450CFDD18} + Win32Proj + run_tests + run_tests + + + + Application + true + MultiByte + false + false + v110 + + + Application + false + true + MultiByte + false + false + v110 + + + + + + + + + + + + + true + Build\$(ProjectName)\$(Configuration)\ + + + false + Build\$(ProjectName)\$(Configuration)\ + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;WITH_TIME_LOGGER;%(PreprocessorDefinitions) + + + Console + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;WITH_TIME_LOGGER;%(PreprocessorDefinitions) + + + Console + true + true + true + + + + + + + + {9751c465-0294-43cd-a5d9-bd038aba3961} + + + + + + \ No newline at end of file diff --git a/run_tests.2012.vcxproj.filters b/run_tests.2012.vcxproj.filters new file mode 100644 index 0000000..8390eb7 --- /dev/null +++ b/run_tests.2012.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Quelldateien + + + \ No newline at end of file diff --git a/run_tests.2012.vcxproj.user b/run_tests.2012.vcxproj.user new file mode 100644 index 0000000..77e44a0 --- /dev/null +++ b/run_tests.2012.vcxproj.user @@ -0,0 +1,11 @@ + + + + -k + WindowsLocalDebugger + + + -k + WindowsLocalDebugger + + \ No newline at end of file diff --git a/run_tests.cpp b/run_tests.cpp index 19e4165..385def2 100644 --- a/run_tests.cpp +++ b/run_tests.cpp @@ -14,7 +14,7 @@ * * Authored / Changed By Armin Diedering * - * Copyright (C) 2010-2012 ardisoft + * Copyright (C) 2010-2014 ardisoft * * * Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -54,6 +54,10 @@ #endif #define _CRT_SECURE_NO_WARNINGS +#ifdef WIN32 +#include +#endif + #include "TinyJS.h" //#include "TinyJS_Functions.h" //#include "TinyJS_MathFunctions.h" @@ -222,8 +226,12 @@ class end { // this is for VisualStudio debugging stuff. It's holds the console ~end() { if(active) { +#ifdef WIN32 + system("pause"); +#else printf("press Enter (end)"); getchar(); +#endif } } bool active; @@ -278,16 +286,16 @@ bool run_test(const char *filename) { char fn[64]; sprintf(fn, "%s.fail.txt", filename); - /* logging is currently deactivated because stack-overflow by recursive vars +// logging is currently deactivated because stack-overflow by recursive vars FILE *f = fopen(fn, "wt"); if (f) { - std::string symbols = s.getRoot()->getParsableString("", " "); + std::string symbols = s.getRoot()->CScriptVar::getParsableString(); fprintf(f, "%s", symbols.c_str()); fclose(f); } - */ - printf("FAIL - symbols written to %s\n", fn); + + printf("FAIL - symbols written to %s\n", fn); } delete[] buffer; diff --git a/run_tests.vcxproj.user b/run_tests.vcxproj.user new file mode 100644 index 0000000..77e44a0 --- /dev/null +++ b/run_tests.vcxproj.user @@ -0,0 +1,11 @@ + + + + -k + WindowsLocalDebugger + + + -k + WindowsLocalDebugger + + \ No newline at end of file diff --git a/time_logger.h b/time_logger.h index 9af7223..592515c 100644 --- a/time_logger.h +++ b/time_logger.h @@ -5,7 +5,7 @@ * * Authored By Armin Diedering * - * Copyright (C) 2010-2012 ardisoft + * Copyright (C) 2010-2014 ardisoft * * * Permission is hereby granted, free of charge, to any person obtaining a copy of diff --git a/tiny-js.2012.sln b/tiny-js.2012.sln new file mode 100644 index 0000000..61a590e --- /dev/null +++ b/tiny-js.2012.sln @@ -0,0 +1,34 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Script", "Script.2012.vcxproj", "{70D538BA-867B-4564-8DEE-F2C78C5AD4C8}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lib-tiny-js", "lib-tiny-js.2012.vcxproj", "{9751C465-0294-43CD-A5D9-BD038ABA3961}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "run_tests", "run_tests.2012.vcxproj", "{C6C6CD82-1400-464C-9B80-E15450CFDD18}" + ProjectSection(ProjectDependencies) = postProject + {9751C465-0294-43CD-A5D9-BD038ABA3961} = {9751C465-0294-43CD-A5D9-BD038ABA3961} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {70D538BA-867B-4564-8DEE-F2C78C5AD4C8}.Debug|Win32.ActiveCfg = Debug|Win32 + {70D538BA-867B-4564-8DEE-F2C78C5AD4C8}.Debug|Win32.Build.0 = Debug|Win32 + {70D538BA-867B-4564-8DEE-F2C78C5AD4C8}.Release|Win32.ActiveCfg = Release|Win32 + {70D538BA-867B-4564-8DEE-F2C78C5AD4C8}.Release|Win32.Build.0 = Release|Win32 + {9751C465-0294-43CD-A5D9-BD038ABA3961}.Debug|Win32.ActiveCfg = Debug|Win32 + {9751C465-0294-43CD-A5D9-BD038ABA3961}.Debug|Win32.Build.0 = Debug|Win32 + {9751C465-0294-43CD-A5D9-BD038ABA3961}.Release|Win32.ActiveCfg = Release|Win32 + {9751C465-0294-43CD-A5D9-BD038ABA3961}.Release|Win32.Build.0 = Release|Win32 + {C6C6CD82-1400-464C-9B80-E15450CFDD18}.Debug|Win32.ActiveCfg = Debug|Win32 + {C6C6CD82-1400-464C-9B80-E15450CFDD18}.Debug|Win32.Build.0 = Debug|Win32 + {C6C6CD82-1400-464C-9B80-E15450CFDD18}.Release|Win32.ActiveCfg = Release|Win32 + {C6C6CD82-1400-464C-9B80-E15450CFDD18}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal