diff --git a/.gitignore b/.gitignore index 8567b2b..6f8924f 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,4 @@ !.vscode/launch.json !.vscode/extensions.json +build/apps/program diff --git a/LICENSE b/LICENSE index e9ab04d..f174e9b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) < ;match=.+>> All rights reserved. +Copyright (c) Ludovic 'Archivist' Lagouardette(c)2018 All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/Makefile b/Makefile index 8df0e29..98cc127 100644 --- a/Makefile +++ b/Makefile @@ -13,8 +13,8 @@ # ************************************************************** # -CXX := -c++ -CXXFLAGS := -pedantic-errors -Wall -Wextra -Werror -O3 -Wno-unused-parameter +CXX := -g++-7 +CXXFLAGS := -pedantic-errors -Wall -Wextra -Werror -O3 -Wno-unused-parameter -std=c++17 LDFLAGS := -L/usr/lib -lstdc++ -lm -lpthread BUILD := build OBJ_DIR := $(BUILD)/objects diff --git a/include/9float.hpp b/include/9float.hpp new file mode 100644 index 0000000..8028f7e --- /dev/null +++ b/include/9float.hpp @@ -0,0 +1,147 @@ +#pragma once +#include +#include + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnarrowing" +namespace ninefloat{ + +struct private_t{}; + +private_t priv; + +template +class Q{ + integer_type backend; + static constexpr int degen=fractionals; + + + constexpr Q (const integer_type value, private_t) + : backend{value} + {{std::cout< + constexpr Q (const srcT value) + : backend{(integer_type)(value*(1<::degen; + backend/=oth.backend; + return this; + } + + Q constexpr operator*=(const Q& oth) + { + if constexpr(Q::degen%2==0) + { + backend=(backend>>(Q::degen/2))*(oth.backend>>(Q::degen/2)); + }else{ + backend=(backend>>(Q::degen>>1))*(oth.backend>>((Q::degen>>1)+1)); + } + return this; + } + + Q constexpr operator+(const Q& oth) + { + return Q{backend+oth.backend, priv}; + } + + Q constexpr operator-(const Q& oth) + { + return Q{backend-oth.backend, priv}; + } + + Q constexpr operator/(const Q& oth) + { + return Q{(1<::degen)*backend/oth.backend, priv}; + } + + Q constexpr operator*(const Q& oth) + { + if constexpr(Q::degen%2==0) + { + return Q{(backend>>(Q::degen/2))*(oth.backend>>(Q::degen/2)),priv}; + }else{ + return Q{(backend>>(Q::degen>>1))*(oth.backend>>((Q::degen>>1)+1)),priv}; + } + } + + bool constexpr operator==(const Q& oth) + { + return backend == oth.backend; + } + + bool constexpr operator!=(const Q& oth) + { + return backend != oth.backend; + } + + bool constexpr operator>=(const Q& oth) + { + return backend >= oth.backend; + } + + bool constexpr operator<=(const Q& oth) + { + return backend <= oth.backend; + } + + bool constexpr operator>(const Q& oth) + { + return backend > oth.backend; + } + + bool constexpr operator<(const Q& oth) + { + return backend < oth.backend; + } + + constexpr operator float() + { + float n=backend; + n/=(1<::degen); + return n; + } + + constexpr operator double() + { + double n=backend; + n/=(1<::degen); + return n; + } +}; +#pragma GCC diagnostic pop + +} \ No newline at end of file diff --git a/include/TinyJS/TinyJS.h b/include/TinyJS/TinyJS.h deleted file mode 100755 index 545d076..0000000 --- a/include/TinyJS/TinyJS.h +++ /dev/null @@ -1,2302 +0,0 @@ -/* - * 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-2014 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 -#include -#include -#include -#include -#include "TinyJS/config.h" - -#ifdef NO_POOL_ALLOCATOR - template - class fixed_size_object {}; -#else -# include "TinyJS/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)) -#else -# define DEPRECATED(_Text) -#endif - -#ifndef ASSERT -# define ASSERT(X) assert(X) -#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_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, -#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, - LEX_R_DO, - LEX_R_WHILE, - LEX_R_FOR, - LEX_R_IN, - LEX_T_OF, - LEX_R_BREAK, - LEX_R_CONTINUE, - LEX_R_RETURN, - LEX_R_VAR, - LEX_R_LET, - LEX_R_CONST, - 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, -#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_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_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_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, - SCRIPTVARLINK_CONFIGURABLE = 1<<1, - 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 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 TEMPORARY_MARK_SLOTS 5 - -#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_CONSTRUCTOR_VAR "constructor" -#define TINYJS_TEMP_NAME "" -#define TINYJS_BLANK_DATA "" - -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(int32_t intData); -std::string int2string(uint32_t 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), 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), column(Column){} - CScriptException(const std::string &Message, const char *File="", int Line=-1, int 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), column(Column){} - std::string toString(); -}; - - -////////////////////////////////////////////////////////////////////////// -/// 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 - 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 - void reset(const POS &toPos); ///< Reset this lex so we can start again - - std::string currentFile; - struct POS { - const char *tokenStart; - int currentLine; - const char *currentLineStart; - int currentColumn() { return tokenStart-currentLineStart; } - } pos; - int currentLine() { return pos.currentLine; } - int currentColumn() { return pos.currentColumn(); } - bool lineBreakBeforeToken; -private: - const char *data; - const char *dataPos; - char currCh, nextCh; - - void getNextCh(); - void getNextToken(); ///< Get the text token from our text string -}; - - -////////////////////////////////////////////////////////////////////////// -/// 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 -{ -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; } -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) {} - std::string tokenStr; -private: -}; - -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() {} - enum { - LETS = 0, - VARS, - CONSTS, - END - }; - STRING_SET_t varNames[END]; - 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 ); - void addConsts( STRING_VECTOR_t &Vars ); - std::string addVarsInLetscope(STRING_VECTOR_t &Vars); - std::string addLets(STRING_VECTOR_t &Lets); - bool empty() { return varNames[LETS].empty() && varNames[VARS].empty() && varNames[CONSTS].empty() && functions.empty(); } -private: -}; -class CScriptTokenDataForwardsPtr { -public: - CScriptTokenDataForwardsPtr() : ptr(0) {} - CScriptTokenDataForwardsPtr(const CScriptTokenDataForwardsPtr &Copy) : ptr(0) { *this=Copy; } - CScriptTokenDataForwardsPtr &operator=(const CScriptTokenDataForwardsPtr &Copy) { - if(ptr != Copy.ptr) { - if(ptr) ptr->unref(); - 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; - 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: -}; - -class CScriptTokenDataTry : 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 -////////////////////////////////////////////////////////////////////////// - -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), 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); - 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 dynamic_cast(tokenData)->tokenStr; } - double &Float() { ASSERT(LEX_TOKEN_DATA_FLOAT(token)); return *floatData; } - 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); } - 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; - - 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; - CScriptTokenData *tokenData; - }; -}; - - -////////////////////////////////////////////////////////////////////////// -/// CScriptTokenizer - converts the code in a 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; - return *this; - } - TOKEN_VECT *tokens; - TOKEN_VECT_it pos; - 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); - 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();} - const std::string &tkStr() { static std::string empty; return LEX_TOKEN_DATA_STRING(getToken().token)?getToken().String():empty; } -private: - 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); - 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; - ScriptTokenPosition prevPos; - std::vector tokenScopeStack; -}; - - -////////////////////////////////////////////////////////////////////////// -/// 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; -typedef void (*JSCallback)(const CFunctionsScopePtr &var, void *userdata); - -class CTinyJS; -class CScriptResult; - -////////////////////////////////////////////////////////////////////////// -/// 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) MEMBER_DELETE; ///< private -> no assignment-Copy -public: - virtual ~CScriptVar(); - virtual CScriptVarPtr clone()=0; - - /// Type - 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 - - virtual bool isIterator(); - virtual bool isGenerator(); - - bool isBasic() { return Childs.empty(); } ///< Is this *not* an array/object/etc - - - ////////////////////////////////////////////////////////////////////////// - /// Value - ////////////////////////////////////////////////////////////////////////// - - 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(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(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(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(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(CScriptResult &execute); - virtual bool toBoolean(); - std::string toString(int32_t radix=0); ///< shortcut for this->toPrimitive_hintString()->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(); - 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; - - 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) { \ - 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; - -#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 - 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 - 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) - virtual void removeAllChildren(); - - /// ARRAY - 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 - 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; } - - //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 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; - int refs; ///< The number of references held to this - used for garbage collection - CScriptVar *prev; -public: - CScriptVar *next; - uint32_t temporaryMark[TEMPORARY_MARK_SLOTS]; - - friend class CScriptVarPtr; -}; - - -////////////////////////////////////////////////////////////////////////// -/// 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); - - // 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(); - var = Copy.var; if(var) var->ref(); - } - return *this; - } - // 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; } - - void clear() { if(var) var->unref(); var=0; } -protected: - CScriptVar *var; -}; - -////////////////////////////////////////////////////////////////////////// -/// CScriptVarPointer - template -////////////////////////////////////////////////////////////////////////// - -template -class CScriptVarPointer : public CScriptVarPtr { -public: - 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; } -}; - - -////////////////////////////////////////////////////////////////////////// -/// CScriptVarLink -////////////////////////////////////////////////////////////////////////// -class CScriptVarLink : public fixed_size_object -{ -private: // prevent gloabal creating - CScriptVarLink(const CScriptVarPtr &var, const std::string &name = TINYJS_TEMP_NAME, int flags = SCRIPTVARLINK_DEFAULT); -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; } - const CScriptVarPtr &setVarPtr(const CScriptVarPtr &Var) { return var = Var; } ///< simple Replace the Variable pointed to - - - bool isOwned() const { return owner!=0; } - - bool isWritable() const { return (flags & SCRIPTVARLINK_WRITABLE) != 0; } - 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); } - - CScriptVar *getOwner() { return owner; }; - void setOwner(CScriptVar *Owner) { owner = Owner; } - - /// forward to ScriptVar - - CScriptVarPrimitivePtr toPrimitive() { ///< by default call getDefaultValue_hintNumber by a Date-object calls getDefaultValue_hintString - return var->toPrimitive(); } - 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(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(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(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(CScriptResult &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]; -#endif - CScriptVarLink *ref(); - void unref(); -private: - int refs; - friend class CScriptVarLinkPtr; -}; - - -////////////////////////////////////////////////////////////////////////// -/// CScriptVarLinkPtr -////////////////////////////////////////////////////////////////////////// - -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); - - // 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(); } - - // 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; - } - - // getter & setter - CScriptVarLinkWorkPtr getter(); - CScriptVarLinkWorkPtr getter(CScriptResult &execute); - CScriptVarLinkWorkPtr setter(const CScriptVarPtr &Var); - CScriptVarLinkWorkPtr setter(CScriptResult &execute, const CScriptVarPtr &Var); - - // if - operator bool() const { return link!=0; } - - // 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; } - - operator const CScriptVarPtr &() const { static CScriptVarPtr NullPtr; return link?link->getVarPtr():NullPtr; } - - void clear() { if(link) link->unref(); link=0; } -protected: - CScriptVarLink *link; -}; - - -////////////////////////////////////////////////////////////////////////// -/// CScriptVarLinkWorkPtr -////////////////////////////////////////////////////////////////////////// - -class CScriptVarLinkWorkPtr : public CScriptVarLinkPtr { -public: - // 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(); } - - // 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; } - - // copy - CScriptVarLinkWorkPtr(const CScriptVarLinkWorkPtr &Copy) : CScriptVarLinkPtr(Copy), referencedOwner(Copy.referencedOwner) {} - CScriptVarLinkWorkPtr &operator=(const CScriptVarLinkWorkPtr &Copy) { CScriptVarLinkPtr::operator=(Copy); referencedOwner = Copy.referencedOwner; return *this; } - - // getter & setter - CScriptVarLinkWorkPtr getter(); - CScriptVarLinkWorkPtr getter(CScriptResult &execute); - CScriptVarLinkWorkPtr setter(const CScriptVarPtr &Var); - CScriptVarLinkWorkPtr setter(CScriptResult &execute, const CScriptVarPtr &Var); - - - void swap(CScriptVarLinkWorkPtr &Link) { - CScriptVarPtr _referencedOwner = referencedOwner; referencedOwner = Link.referencedOwner; Link.referencedOwner = _referencedOwner; - CScriptVarLink *_link=link; link=Link.link; Link.link=_link; - } - - 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: - CScriptVarPtr referencedOwner; -}; - - -////////////////////////////////////////////////////////////////////////// -#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__) - - -////////////////////////////////////////////////////////////////////////// -/// CScriptVarPrimitive -////////////////////////////////////////////////////////////////////////// - -define_ScriptVarPtr_Type(Primitive); -class CScriptVarPrimitive : public CScriptVar { -protected: - 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 ~CScriptVarPrimitive(); - - virtual bool isPrimitive(); ///< return true; - - virtual CScriptVarPrimitivePtr getRawPrimitive(); - virtual bool toBoolean(); /// false by default - virtual CNumber toNumber_Callback()=0; - virtual std::string toCString(int radix=0)=0; - - virtual CScriptVarPtr toObject(); - virtual CScriptVarPtr toString_CallBack(CScriptResult &execute, int radix=0); -protected: -}; - - -////////////////////////////////////////////////////////////////////////// -/// CScriptVarUndefined -////////////////////////////////////////////////////////////////////////// - -define_dummy_t(Undefined); -define_ScriptVarPtr_Type(Undefined); -class CScriptVarUndefined : public CScriptVarPrimitive { -protected: - CScriptVarUndefined(CTinyJS *Context); - CScriptVarUndefined(const CScriptVarUndefined &Copy) : CScriptVarPrimitive(Copy) {} ///< Copy protected -> use clone for public -public: - virtual ~CScriptVarUndefined(); - virtual CScriptVarPtr clone(); - - virtual bool isUndefined(); // { return true; } - - 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); -}; -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); } - - -////////////////////////////////////////////////////////////////////////// -/// CScriptVarNull -////////////////////////////////////////////////////////////////////////// - -define_dummy_t(Null); -define_ScriptVarPtr_Type(Null); -class CScriptVarNull : public CScriptVarPrimitive { -protected: - CScriptVarNull(CTinyJS *Context); - CScriptVarNull(const CScriptVarNull &Copy) : CScriptVarPrimitive(Copy) {} ///< Copy protected -> use clone for public -public: - virtual ~CScriptVarNull(); - virtual CScriptVarPtr clone(); - - 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(Null, CTinyJS *Context, Null_t) { return new CScriptVarNull(Context); } -inline define_newScriptVar_NamedFnc(Null, CTinyJS *Context) { return new CScriptVarNull(Context); } - - -////////////////////////////////////////////////////////////////////////// -/// CScriptVarString -////////////////////////////////////////////////////////////////////////// - -define_ScriptVarPtr_Type(String); -class CScriptVarString : public CScriptVarPrimitive { -protected: - CScriptVarString(CTinyJS *Context, const std::string &Data); - 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 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 toObject(); - virtual CScriptVarPtr toString_CallBack(CScriptResult &execute, int radix=0); - - uint32_t stringLength() { return data.size(); } - int getChar(uint32_t Idx); -protected: - std::string data; -private: - 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); } - - -////////////////////////////////////////////////////////////////////////// -/// 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 CNumber { -private: - enum NType { - tnNULL, tInt32, tDouble, tNaN, tInfinity - }; - CNumber(NType Type, int32_t InfinitySign=0) : type(Type) { Int32 = InfinitySign; } -public: - - 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(); } - -////////////////////////////////////////////////////////////////////////// -/// CScriptVarNumber -////////////////////////////////////////////////////////////////////////// - -define_ScriptVarPtr_Type(Number); -class CScriptVarNumber : public CScriptVarPrimitive { -protected: - CScriptVarNumber(CTinyJS *Context, const CNumber &Data); - CScriptVarNumber(const CScriptVarNumber &Copy) : CScriptVarPrimitive(Copy), data(Copy.data) {} ///< Copy protected -> use clone for public -public: - virtual ~CScriptVarNumber(); - virtual CScriptVarPtr clone(); - virtual bool isNumber(); // { return true; } - virtual bool isInt(); // { return true; } - 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 &); -}; -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)); } - - -////////////////////////////////////////////////////////////////////////// -/// CScriptVarBool -////////////////////////////////////////////////////////////////////////// - -define_ScriptVarPtr_Type(Bool); -class CScriptVarBool : public CScriptVarPrimitive { -protected: - CScriptVarBool(CTinyJS *Context, bool Data); - 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 bool toBoolean(); - virtual CNumber toNumber_Callback(); - virtual std::string toCString(int radix=0); - - virtual std::string getVarType(); // { return "boolean"; } - - virtual CScriptVarPtr toObject(); -protected: - bool data; - - 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); } - - -////////////////////////////////////////////////////////////////////////// -/// 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(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 ~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(CScriptResult &execute, int radix=0); - virtual void setTemporaryMark_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_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); } - - -////////////////////////////////////////////////////////////////////////// -/// 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 -////////////////////////////////////////////////////////////////////////// - -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(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 -////////////////////////////////////////////////////////////////////////// - -define_dummy_t(Array); -define_ScriptVarPtr_Type(Array); -class CScriptVarArray : public CScriptVarObject { -protected: - CScriptVarArray(CTinyJS *Context); - CScriptVarArray(const CScriptVarArray &Copy) : CScriptVarObject(Copy), toStringRecursion(Copy.toStringRecursion) {} ///< Copy protected -> use clone for public -public: - virtual ~CScriptVarArray(); - virtual CScriptVarPtr clone(); - 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(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); } - - -////////////////////////////////////////////////////////////////////////// -/// 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(CScriptResult &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 */ - - -////////////////////////////////////////////////////////////////////////// -/// 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 getVarType(); // { return "function"; } - virtual std::string getParsableString(const std::string &indentString, const std::string &indent, uint32_t uniqueID, bool &hasRecursion); - virtual CScriptVarPtr toString_CallBack(CScriptResult &execute, int radix=0); - virtual CScriptTokenDataFnc *getFunctionData(); - void setFunctionData(CScriptTokenDataFnc *Data); -private: - CScriptTokenDataFnc *data; - - 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 setTemporaryMark_recursive(uint32_t ID); - CScriptVarPtr callFunction(CScriptResult &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 -////////////////////////////////////////////////////////////////////////// - -define_ScriptVarPtr_Type(FunctionNative); -class CScriptVarFunctionNative : public CScriptVarFunction { -protected: - 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(); - virtual CScriptVarPtr clone()=0; - virtual bool isNative(); // { return true; } - - 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, 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(); - 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*, const char*); -}; -inline define_newScriptVar_Fnc(FunctionNativeCallback, CTinyJS *Context, JSCallback Callback, void *Userdata, const char *Name=0) { return new CScriptVarFunctionNativeCallback(Context, Callback, Userdata, Name); } - - -////////////////////////////////////////////////////////////////////////// -/// CScriptVarFunctionNativeClass -////////////////////////////////////////////////////////////////////////// - -template -class CScriptVarFunctionNativeClass : public CScriptVarFunctionNative { -protected: - 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); } - - 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 *, const char *); -}; -template -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); } - - -////////////////////////////////////////////////////////////////////////// -/// 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); } - - -////////////////////////////////////////////////////////////////////////// -/// 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 -////////////////////////////////////////////////////////////////////////// - -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 CScriptVarLinkWorkPtr 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) : CScriptVarLinkPtr()) {} -public: - virtual ~CScriptVarScopeFnc(); - 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() - - #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 - - void throwError(ERROR_TYPES ErrorType, const std::string &message); - -protected: - 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); } - - -////////////////////////////////////////////////////////////////////////// -/// 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 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: - 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); } - - -////////////////////////////////////////////////////////////////////////// -/// 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 CScriptVarLinkWorkPtr findInScopes(const std::string &childName); -private: - 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); } - - -////////////////////////////////////////////////////////////////////////// -/// 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); } -template -inline CScriptVarPtr CScriptVar::newScriptVar(T1 t1, T2 t2) { return ::newScriptVar(context, t1, t2); } -//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(); - ~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() */ - 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() */ - 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() */ - 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); - /** 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); - - 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 - 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_BUILDINDEFAULT); - template - 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); - } - - /// 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 - //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: - CScriptTokenizer *t; /// current tokenizer - bool haveTry; - std::vectorscopes; - CScriptVarScopePtr root; - const CScriptVarScopePtr &scope() { return scopes.back(); } - - class CScopeControl { // helper-class to manage scopes - private: - 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++; } - 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; - 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 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: - 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 constStopIteration; - - std::vector pseudo_refered; - - 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(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); - } - -public: - // function call - CScriptVarPtr callFunction(const CScriptVarFunctionPtr &Function, std::vector &Arguments, const CScriptVarPtr &This, CScriptVarPtr *newThis=0); - 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 - ////////////////////////////////////////////////////////////////////////// - - // parsing - in order of precedence - CScriptVarPtr mathsOp(CScriptResult &execute, const CScriptVarPtr &a, const CScriptVarPtr &b, int op); -private: - 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); -public: - CScriptVarLinkPtr 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 -public: - void throwError(CScriptResult &execute, ERROR_TYPES ErrorType, const std::string &message); - void throwException(ERROR_TYPES ErrorType, const std::string &message); - 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: - ////////////////////////////////////////////////////////////////////////// - /// native Object-Constructors & prototype-functions - - void native_Object(const CFunctionsScopePtr &c, void *data); - void native_Object_getPrototypeOf(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_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); - 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); - 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_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: - 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; } -}; - - -////////////////////////////////////////////////////////////////////////// -template -inline const CScriptVarPtr &CScriptVar::constScriptVar(T t) { return context->constScriptVar(t); } -////////////////////////////////////////////////////////////////////////// -inline CNumber CScriptVarLink::toNumber() { return var->toNumber(); } -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/include/TinyJS/TinyJS_Functions.h b/include/TinyJS/TinyJS_Functions.h deleted file mode 100755 index 998866e..0000000 --- a/include/TinyJS/TinyJS_Functions.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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-2014 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. - */ - -#pragma message("The include "__FILE__" is deprecated - Functions now registered by default") - -#ifndef TINYJS_FUNCTIONS_H -#define TINYJS_FUNCTIONS_H - -#include "TinyJS/TinyJS.h" - -/// Register useful functions with the TinyJS interpreter -extern void DEPRECATED("is deprecated - Functions now registered by default") registerFunctions(CTinyJS *tinyJS); - -#endif diff --git a/include/TinyJS/TinyJS_MathFunctions.h b/include/TinyJS/TinyJS_MathFunctions.h deleted file mode 100644 index d68992d..0000000 --- a/include/TinyJS/TinyJS_MathFunctions.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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-2014 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. - */ - -#pragma message("The include "__FILE__" is deprecated - MathFunctions now registered by default") - -#ifndef TINYJS_MATHFUNCTIONS_H -#define TINYJS_MATHFUNCTIONS_H - -#include "TinyJS/TinyJS.h" - -/// Register useful math. functions with the TinyJS interpreter -extern void DEPRECATED("is deprecated - MathFunctions now registered by default") registerMathFunctions(CTinyJS *tinyJS); - -#endif diff --git a/include/TinyJS/TinyJS_StringFunctions.h b/include/TinyJS/TinyJS_StringFunctions.h deleted file mode 100644 index 6d65cf3..0000000 --- a/include/TinyJS/TinyJS_StringFunctions.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 42TinyJS - * - * A fork of TinyJS with the goal to makes a more JavaScript/ECMA compliant engine - * - * Authored By Armin Diedering - * - * Copyright (C) 2010-2014 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. - */ - -#pragma message("The include "__FILE__" is deprecated - StringFunctions now registered by default") - -#ifndef TINYJS_STRINGFUNCTIONS_H -#define TINYJS_STRINGFUNCTIONS_H - -#include "TinyJS/TinyJS.h" - -/// Register useful functions with the TinyJS interpreter -extern void DEPRECATED("is deprecated - StringFunctions now registered by default") registerStringFunctions(CTinyJS *tinyJS); - -#endif diff --git a/include/TinyJS/TinyJS_Threading.h b/include/TinyJS/TinyJS_Threading.h deleted file mode 100644 index 3c8d5af..0000000 --- a/include/TinyJS/TinyJS_Threading.h +++ /dev/null @@ -1,160 +0,0 @@ -#ifndef TinyJS_Threading_h__ -#define TinyJS_Threading_h__ -#include "TinyJS/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-2014 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 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(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(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/include/TinyJS/config.h b/include/TinyJS/config.h deleted file mode 100644 index 0642030..0000000 --- a/include/TinyJS/config.h +++ /dev/null @@ -1,177 +0,0 @@ -#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-2014 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 - -////////////////////////////////////////////////////////////////////////// - -/* 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 - - -////////////////////////////////////////////////////////////////////////// - -/* MULTI-THREADING - * =============== - * 42TinyJS is basically thread-save. - * You can run different or the same JS-code simultaneously in different instances of class TinyJS. - * >>> 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 - */ -//#define NO_THREADING - -/* 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/) - */ -//#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_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 - -#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 - -#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/include/TinyJS/pool_allocator.h b/include/TinyJS/pool_allocator.h deleted file mode 100644 index 827e73b..0000000 --- a/include/TinyJS/pool_allocator.h +++ /dev/null @@ -1,162 +0,0 @@ -/* - * 42TinyJS - * - * A fork of TinyJS with the goal to makes a more JavaScript/ECMA compliant engine - * - * Authored By Armin Diedering - * - * Copyright (C) 2010-2014 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__ - -#include -#include -#include -#include "TinyJS/config.h" -#include "TinyJS/TinyJS_Threading.h" - -/************************************************************************ - * 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. - ************************************************************************/ - -#if !defined(DEBUG_POOL_ALLOCATOR) && (defined(_DEBUG) || defined(LOG_POOL_ALLOCATOR_MEMORY_USAGE)) -# define DEBUG_POOL_ALLOCATOR -#endif - -struct block_head; -class fixed_size_allocator { -public: - ~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; } -#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&); - 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; - int allocs; - int frees; - 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::alloc(size, typeid(T).name()); -#else - 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::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__ diff --git a/include/TinyJS/time_logger.h b/include/TinyJS/time_logger.h deleted file mode 100644 index 592515c..0000000 --- a/include/TinyJS/time_logger.h +++ /dev/null @@ -1,128 +0,0 @@ -/* - * 42TinyJS - * - * A fork of TinyJS with the goal to makes a more JavaScript/ECMA compliant engine - * - * Authored By Armin Diedering - * - * Copyright (C) 2010-2014 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__ -#if defined(WITH_TIME_LOGGER) - -#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) -# define TimeLoggerHelper(a) do{}while(0) -#endif /* _DEBUG */ - - - -#endif // time_logger_h__ diff --git a/src/TinyJS/TinyJS.cpp b/src/TinyJS/TinyJS.cpp deleted file mode 100755 index cc2af89..0000000 --- a/src/TinyJS/TinyJS.cpp +++ /dev/null @@ -1,6556 +0,0 @@ -/* - * 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-2014 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 -# ifndef _MSC_VER -# define DEBUG_MEMORY 1 -# endif -#endif -#include -#include -#include - -#include "TinyJS/TinyJS.h" - -#ifndef ASSERT -# define ASSERT(X) assert(X) -#endif - -#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 -#else -# include -# include -# include -#endif - -using namespace std; - -// ----------------------------------------------------------------------------------- -////////////////////////////////////////////////////////////////////////// -/// 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;igetName().c_str(), allocatedLinks[i]->getVarPtr()->getRefs()); - allocatedLinks[i]->getVarPtr()->trace(" "); - } - allocatedVars.clear(); - allocatedLinks.clear(); -} -#endif - - -////////////////////////////////////////////////////////////////////////// -/// Utils -////////////////////////////////////////////////////////////////////////// - -inline bool isWhitespace(char ch) { - return (ch==' ') || (ch=='\t') || (ch=='\n') || (ch=='\r'); -} - -inline bool isNumeric(char ch) { - return (ch>='0') && (ch<='9'); -} -uint32_t isArrayIndex(const string &str) { - if(str.size()==0 || !isNumeric(str[0]) || (str.size()>1 && 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(); -} -inline bool isHexadecimal(char ch) { - return ((ch>='0') && (ch<='9')) || ((ch>='a') && (ch<='f')) || ((ch>='A') && (ch<='F')); -} -inline bool isOctal(char ch) { - return ((ch>='0') && (ch<='7')); -} -inline bool isAlpha(char ch) { - return ((ch>='a') && (ch<='z')) || ((ch>='A') && (ch<='Z')) || ch=='_' || 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); - } -} -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) { - 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) { - char buffer[5] = "\\x00"; - string nStr; nStr.reserve(str.length()); - nStr.push_back('\"'); - for (string::const_iterator i=str.begin();i!=str.end();i++) { - const char *replaceWith = 0; - switch (*i) { - case '\\': replaceWith = "\\\\"; break; - case '\n': replaceWith = "\\n"; break; - case '\r': replaceWith = "\\r"; break; - case '\a': replaceWith = "\\a"; break; - case '\b': replaceWith = "\\b"; break; - case '\f': replaceWith = "\\f"; break; - case '\t': replaceWith = "\\t"; break; - case '\v': replaceWith = "\\v"; break; - case '"': replaceWith = "\\\""; break; - default: { - int nCh = ((unsigned char)*i) & 0xff; - if(nCh<32 || nCh>127) { - static char hex[] = "0123456789ABCDEF"; - buffer[2] = hex[(nCh>>4)&0x0f]; - buffer[3] = hex[nCh&0x0f]; - replaceWith = buffer; - }; - } - } - if (replaceWith) - nStr.append(replaceWith); - else - nStr.push_back(*i); - } - nStr.push_back('\"'); - return nStr; -} - -static inline string getIDString(const string& str) { - if(isIDString(str.c_str()) && CScriptToken::isReservedWord(str)==LEX_ID) - return str; - return getJSString(str); -} - - -////////////////////////////////////////////////////////////////////////// -/// CScriptException -////////////////////////////////////////////////////////////////////////// - -string CScriptException::toString() { - ostringstream msg; - msg << ERROR_NAME[errorType] << ": " << message; - 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(const char *Code, const string &File, int Line, int Column) : data(Code) { - currentFile = File; - pos.currentLineStart = pos.tokenStart = data; - pos.currentLine = Line; - reset(pos); -} - -void CScriptLex::reset(const POS &toPos) { ///< Reset this lex so we can start again - dataPos = toPos.tokenStart; - tk = last_tk = 0; - tkStr = ""; - pos = toPos; - lineBreakBeforeToken = false; - currCh = nextCh = 0; - getNextCh(); // currCh - getNextCh(); // nextCh - getNextToken(); -} - -void CScriptLex::check(int expected_tk, int alternate_tk/*=-1*/) { - if (expected_tk==';' && tk==LEX_EOF) return; // ignore last missing ';' - if (tk!=expected_tk && tk!=alternate_tk) { - ostringstream errorString; - if(expected_tk == LEX_EOF) - errorString << "Got unexpected " << CScriptToken::getTokenStr(tk); - else - 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 = pos.currentLine; - getNextToken(); - lineBreakBeforeToken = line != pos.currentLine; -} - -void CScriptLex::getNextCh() { - if(currCh == '\n') { // Windows or Linux - pos.currentLine++; - pos.tokenStart = pos.currentLineStart = dataPos - (nextCh == LEX_EOF ? 0 : 1); - } - 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' - else - 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_PLUSPLUS, LEX_MINUSMINUS, LEX_EOF}; -void CScriptLex::getNextToken() { - 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; - } - last_tk = tk; - tk = LEX_EOF; - tkStr.clear(); - // record beginning of this token - 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 = 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; - 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 (isOctal(currCh) || (!isOct && isNumeric(currCh)) || (isHex && isHexadecimal(currCh))) { - tkStr += currCh; - getNextCh(); - } - if (!isHex && !isOct && currCh=='.') { - tk = LEX_FLOAT; - tkStr += '.'; - getNextCh(); - while (isNumeric(currCh)) { - tkStr += currCh; - getNextCh(); - } - } - // do fancy e-style floating point - if (!isHex && !isOct && (currCh=='e' || currCh=='E')) { - tk = LEX_FLOAT; - tkStr += currCh; getNextCh(); - if (currCh=='-') { tkStr += currCh; getNextCh(); } - while (isNumeric(currCh)) { - tkStr += currCh; getNextCh(); - } - } - } else if (currCh=='"' || currCh=='\'') { // strings... - char endCh = currCh; - getNextCh(); - while (currCh && currCh!=endCh && currCh!='\n') { - if (currCh == '\\') { - getNextCh(); - switch (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(); - 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"; - buf[0] = currCh; - for(int i=1; i<3 && isOctal(nextCh); i++) { - getNextCh(); buf[i] = currCh; - } - tkStr += (char)strtol(buf, 0, 8); - } - else tkStr += currCh; - } - } - } else { - tkStr += currCh; - } - getNextCh(); - } - if(currCh != endCh) - throw new CScriptException(SyntaxError, "unterminated string literal", currentFile, pos.currentLine, currentColumn()); - 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=='<') { - if (currCh=='=') { // <= - tk = LEX_LEQUAL; - getNextCh(); - } else if (currCh=='<') { // << - tk = LEX_LSHIFT; - getNextCh(); - if (currCh=='=') { // <<= - tk = LEX_LSHIFTEQUAL; - getNextCh(); - } - } - } else if (tk=='>') { - if (currCh=='=') { // >= - tk = LEX_GEQUAL; - getNextCh(); - } else if (currCh=='>') { // >> - tk = LEX_RSHIFT; - getNextCh(); - if (currCh=='=') { // >>= - tk = LEX_RSHIFTEQUAL; - getNextCh(); - } else if (currCh=='>') { // >>> - tk = LEX_RSHIFTU; - getNextCh(); - if (currCh=='=') { // >>>= - tk = LEX_RSHIFTUEQUAL; - getNextCh(); - } - } - } - } 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_ASTERISKEQUAL; - getNextCh(); - } else if (tk=='/') { - // 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 of regular expressions", currentFile, pos.currentLine, currentColumn()); -#endif - tkStr = "/"; - while (currCh && currCh!='/' && currCh!='\n') { - if (currCh == '\\' && nextCh == '/') { - tkStr.append(1, currCh); - getNextCh(); - } - tkStr.append(1, currCh); - 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=='g' || currCh=='i' || currCh=='m' || currCh=='y'); - } else - throw new CScriptException(SyntaxError, "unterminated regular expression literal", currentFile, pos.currentLine, currentColumn()); - } else if(currCh=='=') { - tk = LEX_SLASHEQUAL; - getNextCh(); - } - } else if (tk=='%' && currCh=='=') { - tk = LEX_PERCENTEQUAL; - getNextCh(); - } - } - /* This isn't quite right yet */ -} - - -////////////////////////////////////////////////////////////////////////// -// 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 = 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; - } - return true; -} - -void CScriptTokenDataForwards::addVars( STRING_VECTOR_t &Vars ) { - 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 ) -{ - 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; - 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 -////////////////////////////////////////////////////////////////////////// - -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() -{ - 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_CONST, "const", 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 }, - { LEX_R_YIELD, "yield", 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_OF, "of", true }, - { LEX_T_FUNCTION_OPERATOR, "function", true }, - { LEX_T_GET, "get", true }, - { LEX_T_SET, "set", 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, "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 }, -}; -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(); - 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)) - intData = IntData; - 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_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; - if(LEX_TOKEN_DATA_FLOAT(token)) - floatData = new double(*Copy.floatData); - else if(!LEX_TOKEN_DATA_SIMPLE(token)) - (tokenData = Copy.tokenData)->ref(); - else - intData = Copy.intData; - return *this; -} -string CScriptToken::getParsableString(TOKEN_VECT &Tokens, const string &IndentString, const string &Indent) { - return getParsableString(Tokens.begin(), Tokens.end(), IndentString, Indent); -} -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_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, my_indentString, Indent)); - if(it->Fnc().body.front().token != '{') { - OutString.append(";"); - } - } 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)) { - 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; - } - } - if(need_space && old_need_space) destination << " "; - destination << OutString; - } - return destination.str(); - -} - -void CScriptToken::clear() -{ - if(LEX_TOKEN_DATA_FLOAT(token)) - delete floatData; - 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() : l(0), prevPos(&tokens) { -} -CScriptTokenizer::CScriptTokenizer(CScriptLex &Lexer) : l(0), prevPos(&tokens) { - tokenizeCode(Lexer); -} -CScriptTokenizer::CScriptTokenizer(const char *Code, const string &File, int Line, int Column) : l(0), prevPos(&tokens) { - CScriptLex lexer(Code, File, Line, Column); - tokenizeCode(lexer); -} -void CScriptTokenizer::tokenizeCode(CScriptLex &Lexer) { - try { - l=&Lexer; - tokens.clear(); - tokenScopeStack.clear(); - ScriptTokenState state; - pushForwarder(state); - if(l->tk == '§') { // special-Token at Start means the code begins not at Statement-Level - l->match('§'); - tokenizeLiteral(state, 0); - } else do { - tokenizeStatement(state, 0); - } while (l->tk!=LEX_EOF); - 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; - } catch (...) { - l=0; - throw; - } -} - -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; -} - - - -void CScriptTokenizer::match(int ExpectedToken, int AlternateToken/*=-1*/) { - if(check(ExpectedToken, AlternateToken)) - getNextToken(); -} -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 && currentToken!=AlternateToken) { - ostringstream errorString; - if(ExpectedToken == LEX_EOF) - errorString << "Got unexpected " << CScriptToken::getTokenStr(currentToken); - else { - 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()); - } - return true; -} -void CScriptTokenizer::pushTokenScope(TOKEN_VECT &Tokens) { - tokenScopeStack.push_back(ScriptTokenPosition(&Tokens)); - tk = getToken().token; -} - -void CScriptTokenizer::setPos(ScriptTokenPosition &TokenPos) { - ASSERT( TokenPos.tokens == tokenScopeStack.back().tokens); - tokenScopeStack.back().pos = TokenPos.pos; - tk = getToken().token; -} -void CScriptTokenizer::skip(int Tokens) { - ASSERT(tokenScopeStack.back().tokens->end()-tokenScopeStack.back().pos-Tokens>=0); - tokenScopeStack.back().pos+=Tokens-1; - getNextToken(); -} - -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 { - TOKENIZE_FLAGS_canLabel = 1<<0, - TOKENIZE_FLAGS_canBreak = 1<<1, - TOKENIZE_FLAGS_canContinue = 1<<2, - TOKENIZE_FLAGS_canReturn = 1<<3, - 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::tokenizeTry(ScriptTokenState &State, int Flags) { - l->match(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(')'); - - // catch-block - tokenizeBlock(State, Flags | TOKENIZE_FLAGS_noBlockStart); - State.Tokens.swap(catchBlock.block); - State.Forwarders.pop_back(); - } - // 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(ScriptTokenState &State, int Flags) { - - State.Marks.push_back(pushToken(State.Tokens)); // push Token & push tokenBeginIdx - pushToken(State.Tokens, '('); - tokenizeExpression(State, Flags); - pushToken(State.Tokens, ')'); - - State.Marks.push_back(pushToken(State.Tokens, '{')); // push Token & push blockBeginIdx - pushForwarder(State); - - - 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) { - 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 - 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; - } - - 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(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 < 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(ScriptTokenState &State, int Flags) { - State.Marks.push_back(pushToken(State.Tokens)); // push Token & push tokenBeginIdx - - pushToken(State.Tokens, '('); - tokenizeExpression(State, Flags); - pushToken(State.Tokens, ')'); - tokenizeStatementNoLet(State, Flags); - - setTokenSkip(State); -} - -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; - LoopData.labels.push_back(it->String()); - State.LoopLabels.push_back(it->String()); - it->token = LEX_T_DUMMY_LABEL; - } else - break; - } - } - return label_count; -} -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::tokenizeWhileAndDo(ScriptTokenState &State, int Flags) { - - bool do_while = l->tk==LEX_R_DO; - - CScriptToken LoopToken(LEX_T_LOOP); - CScriptTokenDataLoop &LoopData = LoopToken.Loop(); - LoopData.type = do_while ? CScriptTokenDataLoop::DO : CScriptTokenDataLoop::WHILE; - - // get loop-labels - uint32_t label_count = GetLoopLabels(State, LoopData); - - l->match(l->tk); // match while or do - - pushToken(State.Tokens, LoopToken); - - TOKEN_VECT mainTokens; - State.Tokens.swap(mainTokens); - - 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(ScriptTokenState &State, int Flags) { - - State.Marks.push_back(pushToken(State.Tokens)); // push Token & push tokenBeginIdx - - 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); - - setTokenSkip(State); - - if(l->tk == LEX_R_ELSE) { - State.Marks.push_back(pushToken(State.Tokens)); // push Token & push tokenBeginIdx - tokenizeStatementNoLet(State, Flags); - setTokenSkip(State); - } - - setTokenSkip(State); -} - -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); - - l->match(LEX_R_FOR); - if((for_in = for_each_in = (l->tk == LEX_ID && l->tkStr == "each"))) - l->match(LEX_ID); // match "each" - - 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) - 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); - } - } 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); - - if(LoopData.condition.front().token == LEX_T_FORWARD) { - LoopData.init.push_back(LoopData.condition.front()); - LoopData.condition.erase(LoopData.condition.begin()); - } - mainTokens.back().token = LEX_T_FOR_IN; - } else { - l->check(';'); // no automatic ;-injection - pushToken(State.Tokens, ';'); - State.Tokens.swap(LoopData.init); - if(l->tk != ';') tokenizeExpression(State, Flags); - l->check(';'); // no automatic ;-injection - l->match(';'); // no automatic ;-injection - State.Tokens.swap(LoopData.condition); - } - - 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(); - - 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 { - 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 (Lexer->tk!='}') Lexer->match(',', '}'); - } - Lexer->match('}'); -} -static void tokenizeVarIdentifierDestructuringArray(CScriptLex *Lexer, DESTRUCTURING_VARS_t &Vars, STRING_VECTOR_t *VarNames) { - int idx = 0; - Lexer->match('['); - while(Lexer->tk != ']') { - if(Lexer->tk == ',') - Vars.push_back(DESTRUCTURING_VAR_t("", "")); // empty - else - tokenizeVarIdentifierDestructuring(Lexer, Vars, int2string(idx), VarNames); - ++idx; - if (Lexer->tk!=']') Lexer->match(',',']'); - } - Lexer->match(']'); -} -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 - tokenizeVarIdentifierDestructuringArray(Lexer, Vars, VarNames); - Vars.push_back(DESTRUCTURING_VAR_t("", "]")); // marks array end - } else if(Lexer->tk == '{') { - Vars.push_back(DESTRUCTURING_VAR_t(Path, "{")); // marks object begin - tokenizeVarIdentifierDestructuringObject(Lexer, Vars, VarNames); - Vars.push_back(DESTRUCTURING_VAR_t("", "}")); // marks object end - } else { - if(VarNames) VarNames->push_back(Lexer->tkStr); - Vars.push_back(DESTRUCTURING_VAR_t(Path, Lexer->tkStr)); - Lexer->match(LEX_ID); - } -} -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(); - tokenizeVarIdentifierDestructuring(l, token.DestructuringVar().vars, "", VarNames); - return token; -} - -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; - CScriptLex::POS functionPos = l->pos; - - int tk = l->tk; - if(Accessor) { - 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 = !noLetDef && State.Forwarders.front() == State.Forwarders.back(); - - CScriptToken FncToken(tk); - CScriptTokenDataFnc &FncData = FncToken.Fnc(); - - 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(tokenizeVarIdentifier()); - if (l->tk!=')') 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(); - - ScriptTokenState functionState; - functionState.HaveReturnValue = functionState.FunctionIsGenerator = false; - if(l->tk == '{' || tk==LEX_T_GET || tk==LEX_T_SET) - tokenizeBlock(functionState, TOKENIZE_FLAGS_canReturn | TOKENIZE_FLAGS_canYield); - else { - tokenizeExpression(functionState, TOKENIZE_FLAGS_canYield); - l->match(';'); - 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) { - State.Forwarders.front()->functions.insert(FncToken); - FncToken.token = LEX_T_FUNCTION_PLACEHOLDER; - } - State.Tokens.push_back(FncToken); -} - -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(); - - State.Marks.push_back(pushToken(State.Tokens)); // push Token & push BeginIdx - - 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 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(l->tk==',') { - leftHand = false; - pushToken(State.Tokens); - } - else - break; - } - if(Expression) { - string redeclared = State.Forwarders.back()->addLets(vars); - if(redeclared.size()) - throw new CScriptException(TypeError, "redeclaration of variable '"+redeclared+"'", l->currentFile, currLine, currColumn); - 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); - State.Forwarders.back()->vars_in_letscope.clear(); // only clear vars_in_letscope - State.Marks.pop_back(); - } else { - if(!noIN) pushToken(State.Tokens, ';'); - - string redeclared; - 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 = State.Forwarders.front()->addLets(vars); -#else - State.Forwarders.front()->addVars(vars); -#endif - } else - redeclared = State.Forwarders.back()->addLets(vars); - if(redeclared.size()) - throw new CScriptException(TypeError, "redeclaration of variable '"+redeclared+"'", l->currentFile, currLine, currColumn); - } - setTokenSkip(State); - if(leftHand) State.LeftHand = true; -} - -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(); - - bool leftHand = true; - int tk = l->tk; - State.Marks.push_back(pushToken(State.Tokens)); // push Token & push BeginIdx - - STRING_VECTOR_t vars; - for(;;) - { - 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(l->tk==',') { - leftHand = false; - pushToken(State.Tokens); - } - else - break; - } - if(!noIN) pushToken(State.Tokens, ';'); - - setTokenSkip(State); - - if(tk==LEX_R_VAR) - State.Forwarders.front()->addVars(vars); - else - State.Forwarders.front()->addConsts(vars); - string redeclared; - if(State.Forwarders.size()>1) // have let-scope - redeclared = State.Forwarders.back()->addVarsInLetscope(vars); -#ifdef PREVENT_REDECLARATION_IN_FUNCTION_SCOPES - 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(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(); - - Objc.type = CScriptTokenDataObjectLiteral::OBJECT; - Objc.destructuring = Objc.structuring = true; - - string msg, msgFile; - int msgLine=0, msgColumn=0; - - l->match('{'); - 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); - 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 == '}')) { - 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; - 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()) - 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=='=' || (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.LeftHand = true; - State.Tokens.push_back(ObjectToken); -} -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(); - - 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; - 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(',', ']'); - } - l->match(']'); - if(Objc.destructuring && Objc.structuring) { - if(nestedObject) { - if(l->tk!=',' && l->tk!=']' && l->tk!='=') - Objc.destructuring = false; - } - else - 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.LeftHand = true; - State.Tokens.push_back(ObjectToken); -} - -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(State.Tokens); - if(l->tk==':' && canLabel) { - 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()); - 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.LeftHand = true; - } - 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(State.Tokens); - break; - case '{': - _tokenizeLiteralObject(State, ObjectLiteralFlags); - break; - case '[': - _tokenizeLiteralArray(State, ObjectLiteralFlags); - break; - case LEX_R_LET: // let as expression - tokenizeLet(State, Flags); - break; - case LEX_R_FUNCTION: - tokenizeFunction(State, Flags); - break; - case LEX_R_NEW: - State.Marks.push_back(pushToken(State.Tokens)); // push Token & push BeginIdx - { - 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); - } - State.FunctionIsGenerator = true; - break; -#endif - case '(': - 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(ScriptTokenState &State, int Flags) { - while(l->tk == '.' || l->tk == '[') { - if(l->tk == '.') { - pushToken(State.Tokens); - if(CScriptToken::isReservedWord(l->tk)) - l->tk = LEX_ID; // fake reserved-word as member.ID - pushToken(State.Tokens , LEX_ID); - } else { - State.Marks.push_back(pushToken(State.Tokens)); - State.pushLeftHandState(); - tokenizeExpression(State, Flags & ~TOKENIZE_FLAGS_noIn); - State.popLeftHandeState(); - pushToken(State.Tokens, ']'); - setTokenSkip(State); - } - State.LeftHand = true; - } -} -void CScriptTokenizer::tokenizeFunctionCall(ScriptTokenState &State, int Flags) { - bool for_new = (Flags & TOKENIZE_FLAGS_callForNew)!=0; Flags &= ~TOKENIZE_FLAGS_callForNew; - tokenizeLiteral(State, Flags); - tokenizeMember(State, Flags); - while(l->tk == '(') { - State.LeftHand = false; - State.Marks.push_back(pushToken(State.Tokens)); // push Token & push BeginIdx - State.pushLeftHandState(); - while(l->tk!=')') { - tokenizeAssignment(State, Flags & ~TOKENIZE_FLAGS_noIn); - if (l->tk!=')') pushToken(State.Tokens, ',', ')'); - } - State.popLeftHandeState(); - pushToken(State.Tokens); - setTokenSkip(State); - if(for_new) break; - tokenizeMember(State, Flags); - } -} - -void CScriptTokenizer::tokenizeSubExpression(ScriptTokenState &State, 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 */ '&', '^', '|', - }; - 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) { - switch(l->tk) { - case '-': - case '+': - case '!': - case '~': - case LEX_R_TYPEOF: - case LEX_R_VOID: - case LEX_R_DELETE: - Flags &= ~TOKENIZE_FLAGS_canLabel; - noLeftHand = true; - pushToken(State.Tokens); // Precedence 3 - break; - case LEX_PLUSPLUS: // pre-increment - case LEX_MINUSMINUS: // pre-decrement - { - int tk = l->tk; - Flags &= ~TOKENIZE_FLAGS_canLabel; - noLeftHand = true; - 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()); - } - default: - right2left_end = true; - } - } - tokenizeFunctionCall(State, Flags); - - if (!l->lineBreakBeforeToken && (l->tk==LEX_PLUSPLUS || l->tk==LEX_MINUSMINUS)) { // post-in-/de-crement - noLeftHand = true;; - 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(State.Tokens); // Precedence 5-14 - } - else - break; - } - if(noLeftHand) State.LeftHand = false; -} - -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::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) ) { - if(!State.LeftHand) - throw new CScriptException(ReferenceError, "invalid assignment left-hand side", l->currentFile, l->currentLine(), l->currentColumn()); - pushToken(State.Tokens); - tokenizeAssignment(State, Flags); - State.LeftHand = false; - } -} -void CScriptTokenizer::tokenizeExpression(ScriptTokenState &State, int Flags) { - tokenizeAssignment(State, Flags); - while(l->tk == ',') { - pushToken(State.Tokens); - tokenizeAssignment(State, Flags); - State.LeftHand = false; - } -} -void CScriptTokenizer::tokenizeBlock(ScriptTokenState &State, int Flags) { - bool addBlockStart = (Flags&TOKENIZE_FLAGS_noBlockStart)==0; - 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(State, Flags); - pushToken(State.Tokens, '}'); - - if(addBlockStart) removeEmptyForwarder(State); // clean-up BlockStarts - - setTokenSkip(State); -} - -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(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: - 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(State.Tokens, ';'); // push ';' - setTokenSkip(State); - break; - case LEX_R_BREAK: - case LEX_R_CONTINUE: - { - bool isBreak = l->tk == LEX_R_BREAK; - State.Marks.push_back(pushToken(State.Tokens)); // push Token - - if(l->tk != ';' && !l->lineBreakBeforeToken) { - l->check(LEX_ID); - 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(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, switch or labeled statement" : "'continue' must be inside loop", - l->currentFile, l->currentLine(), l->currentColumn()); - pushToken(State.Tokens, ';'); // push ';' - setTokenSkip(State); - } - break; - case LEX_ID: - { - 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; - 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) { - 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; -} -int CScriptTokenizer::pushToken(TOKEN_VECT &Tokens, const CScriptToken &Token) { - int ret = Tokens.size(); - Tokens.push_back(Token); - return ret; -} -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::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(ScriptTokenState &State) -{ - 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(); -} - -void CScriptTokenizer::removeEmptyForwarder( TOKEN_VECT &Tokens, FORWARDER_VECTOR_t &Forwarders, vector &Marks ) -{ - 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()); -} - - -////////////////////////////////////////////////////////////////////////// -/// CScriptVar -////////////////////////////////////////////////////////////////////////// - -CScriptVar::CScriptVar(CTinyJS *Context, const CScriptVarPtr &Prototype) { - extensible = true; - context = Context; - memset(temporaryMark, 0, sizeof(temporaryMark)); - if(context->first) { - next = context->first; - next->prev = this; - } else { - next = 0; - } - context->first = this; - prev = 0; - refs = 0; - if(Prototype) - addChild(TINYJS___PROTO___VAR, Prototype, SCRIPTVARLINK_WRITABLE); -#if DEBUG_MEMORY - mark_allocated(this); -#endif -} -CScriptVar::CScriptVar(const CScriptVar &Copy) { - extensible = Copy.extensible; - context = Copy.context; - memset(temporaryMark, 0, sizeof(temporaryMark)); - if(context->first) { - next = context->first; - next->prev = this; - } else { - next = 0; - } - context->first = this; - prev = 0; - refs = 0; - SCRIPTVAR_CHILDS_cit it; - for(it = Copy.Childs.begin(); it!= Copy.Childs.end(); ++it) { - addChild((*it)->getName(), (*it)->getVarPtr(), (*it)->getFlags()); - } - -#if DEBUG_MEMORY - mark_allocated(this); -#endif -} -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; - else - context->first = next; - if(next) - next->prev = prev; -} - -/// Type - -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;} -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::isRealNumber() {return false;} -bool CScriptVar::isNumber() {return false;} -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 -////////////////////////////////////////////////////////////////////////// - -CScriptVarPrimitivePtr CScriptVar::getRawPrimitive() { - return CScriptVarPrimitivePtr(); // default NULL-Ptr -} -CScriptVarPrimitivePtr CScriptVar::toPrimitive() { - return toPrimitive_hintNumber(); -} - -CScriptVarPrimitivePtr CScriptVar::toPrimitive(CScriptResult &execute) { - return toPrimitive_hintNumber(execute); -} - -CScriptVarPrimitivePtr CScriptVar::toPrimitive_hintString(int32_t radix) { - CScriptResult execute; - CScriptVarPrimitivePtr var = toPrimitive_hintString(execute, radix); - execute.cThrow(); - return var; -} -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 to primitive type"); - } - return ret; - } - return this; - } - return constScriptVar(Undefined); -} -CScriptVarPrimitivePtr CScriptVar::toPrimitive_hintNumber() { - CScriptResult execute; - CScriptVarPrimitivePtr var = toPrimitive_hintNumber(execute); - execute.cThrow(); - return var; -} -CScriptVarPrimitivePtr CScriptVar::toPrimitive_hintNumber(CScriptResult &execute) { - if(execute) { - if(!isPrimitive()) { - CScriptVarPtr ret = callJS_valueOf(execute); - if(execute && !ret->isPrimitive()) { - ret = callJS_toString(execute); - if(execute && !ret->isPrimitive()) - context->throwError(execute, TypeError, "can't convert to primitive type"); - } - return ret; - } - return this; - } - return constScriptVar(Undefined); -} - -CScriptVarPtr CScriptVar::callJS_valueOf(CScriptResult &execute) { - if(execute) { - CScriptVarPtr FncValueOf = findChildWithPrototypeChain("valueOf").getter(execute); - 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); - } - } else - return valueOf_CallBack(); - } - return this; -} -CScriptVarPtr CScriptVar::valueOf_CallBack() { - return this; -} - -CScriptVarPtr CScriptVar::callJS_toString(CScriptResult &execute, int radix/*=0*/) { - if(execute) { - CScriptVarPtr FncToString = findChildWithPrototypeChain("toString").getter(execute); - 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)); - return context->callFunction(execute, FncToString, Params, this); - } - } else - return toString_CallBack(execute, radix); - } - return this; -} -CScriptVarPtr CScriptVar::toString_CallBack(CScriptResult &execute, int radix/*=0*/) { - return this; -} - -CNumber CScriptVar::toNumber() { return toPrimitive_hintNumber()->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(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(); } -bool CScriptVar::getBool() { return toBoolean(); } -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 indentString+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 - -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"; -} - -CScriptVarLinkPtr CScriptVar::findChild(const string &childName) { - if(Childs.empty()) return 0; - SCRIPTVAR_CHILDS_it it = lower_bound(Childs.begin(), Childs.end(), childName); - if(it != Childs.end() && (*it)->getName() == childName) - return *it; - return 0; -} - -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)) { - 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; - } - return 0; -} - -CScriptVarLinkPtr CScriptVar::findChildInPrototypeChain(const string &childName) { - unsigned int uniqueID = context->allocUniqueID(); - // Look for links to actual parent classes - CScriptVarPtr object = this; - CScriptVarLinkPtr __proto__; - while( object->getTemporaryMark() != uniqueID && (__proto__ = object->findChild(TINYJS___PROTO___VAR)) ) { - CScriptVarLinkPtr implementation = __proto__->getVarPtr()->findChild(childName); - if (implementation) { - context->freeUniqueID(); - return implementation; - } - object->setTemporaryMark(uniqueID); // prevents recursions - object = __proto__; - } - context->freeUniqueID(); - return 0; -} - -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 child; -} -CScriptVarLinkPtr CScriptVar::findChildByPath(const string &path) { - string::size_type p = path.find('.'); - CScriptVarLinkPtr child; - if (p == string::npos) - return findChild(path); - if( (child = findChild(path.substr(0,p))) ) - return child->getVarPtr()->findChildByPath(path.substr(p+1)); - return 0; -} - -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)); -} - -CScriptVarLinkPtr CScriptVar::findChildOrCreateByPath(const string &path) { - string::size_type p = path.find('.'); - if (p == string::npos) - return findChildOrCreate(path); - string childName(path, 0, p); - CScriptVarLinkPtr l = findChild(childName); - if (!l) l = addChild(childName, newScriptVar(Object)); - return l->getVarPtr()->findChildOrCreateByPath(path.substr(p+1)); -} - -void CScriptVar::keys(set &Keys, bool OnlyEnumerable/*=true*/, uint32_t ID/*=0*/) -{ - 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()->getTemporaryMark() != ID ) - __proto__->getVarPtr()->keys(Keys, OnlyEnumerable, ID); -} - -/// add & remove -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 = CScriptVarLinkPtr(child?child:constScriptVar(Undefined), childName, linkFlags); - link->setOwner(this); - - Childs.insert(it, 1, link); -#ifdef _DEBUG - } else { - ASSERT(0); // addChild - the child exists -#endif - } - return link; -} -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) { - CScriptVarLinkPtr link(child, childName, linkFlags); - link->setOwner(this); - Childs.insert(it, 1, link); - return link; - } else { - (*it)->setVarPtr(child); - return (*it); - } -} - -bool CScriptVar::removeLink(CScriptVarLinkPtr &link) { - if (!link) return false; - SCRIPTVAR_CHILDS_it it = lower_bound(Childs.begin(), Childs.end(), link->getName()); - if(it != Childs.end() && (*it) == link) { - Childs.erase(it); -#ifdef _DEBUG - } else { - ASSERT(0); // removeLink - the link is not atached to this var -#endif - } - link.clear(); - return true; -} -void CScriptVar::removeAllChildren() { - Childs.clear(); -} - -CScriptVarPtr CScriptVar::getArrayIndex(uint32_t idx) { - CScriptVarLinkPtr link = findChild(int2string(idx)); - if (link) return link; - else return constScriptVar(Undefined); // undefined -} - -void CScriptVar::setArrayIndex(uint32_t idx, const CScriptVarPtr &value) { - string sIdx = int2string(idx); - CScriptVarLinkPtr link = findChild(sIdx); - - if (link) { - link->setVarPtr(value); - } else { - addChild(sIdx, value); - } -} - -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) { - CScriptResult execute; - return context->mathsOp(execute, this, b, op); -} - -void CScriptVar::trace(const string &name) { - string indentStr; - 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(getTemporaryMark() == uniqueID) - extra = " recursion detected"; - TRACE("%s'%s' = '%s' %s%s\n", - indentStr.c_str(), - name.c_str(), - toString().c_str(), - getFlagsAsString().c_str(), - extra); - if(getTemporaryMark() != uniqueID) { - setTemporaryMark(uniqueID); - indentStr+=indent; - for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it) { - if((*it)->isEnumerable()) - (*it)->getVarPtr()->trace(indentStr, uniqueID, (*it)->getName()); - } - indentStr = indentStr.substr(0, indentStr.length()-2); - } -} - -string CScriptVar::getFlagsAsString() { - 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 (isRegExp()) flagstr = flagstr + "REGEXP "; - if (isNaN()) flagstr = flagstr + "NaN "; - if (isInfinity()) flagstr = flagstr + "INFINITY "; - return flagstr; -} - -CScriptVar *CScriptVar::ref() { - refs++; - return this; -} -void CScriptVar::unref() { - refs--; - ASSERT(refs>=0); // printf("OMFG, we have unreffed too far!\n"); - if (refs==0) - delete this; -} - -int CScriptVar::getRefs() { - return refs; -} - -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()->setTemporaryMark_recursive(ID); - } - } -} - - -////////////////////////////////////////////////////////////////////////// -/// CScriptVarLink -////////////////////////////////////////////////////////////////////////// - -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; -} - -CScriptVarLink::~CScriptVarLink() { -#if DEBUG_MEMORY - mark_deallocated(this); -#endif -} - -CScriptVarLink *CScriptVarLink::ref() { - refs++; - return this; -} -void CScriptVarLink::unref() { - refs--; - ASSERT(refs>=0); // printf("OMFG, we have unreffed too far!\n"); - if (refs==0) - delete this; -} - - -////////////////////////////////////////////////////////////////////////// -/// CScriptVarLinkPtr -////////////////////////////////////////////////////////////////////////// - -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; -} - -CScriptVarLinkWorkPtr CScriptVarLinkPtr::getter() { - return CScriptVarLinkWorkPtr(*this).getter(); -} - -CScriptVarLinkWorkPtr CScriptVarLinkPtr::getter( CScriptResult &execute ) { - return CScriptVarLinkWorkPtr(*this).getter(execute); -} - -CScriptVarLinkWorkPtr CScriptVarLinkPtr::setter( const CScriptVarPtr &Var ) { - return CScriptVarLinkWorkPtr(*this).setter(Var); -} - -CScriptVarLinkWorkPtr CScriptVarLinkPtr::setter( CScriptResult &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; -} - - -////////////////////////////////////////////////////////////////////////// -/// CScriptVarLinkWorkPtr -////////////////////////////////////////////////////////////////////////// - -CScriptVarLinkWorkPtr CScriptVarLinkWorkPtr::getter() { - if(link && link->getVarPtr()) { - CScriptResult execute; - CScriptVarPtr ret = getter(execute); - execute.cThrow(); - return ret; - } - return *this; -} -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); - 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()) { - CScriptResult execute; - CScriptVarPtr ret = setter(execute, Var); - execute.cThrow(); - return ret; - } - return *this; -} - -CScriptVarLinkWorkPtr CScriptVarLinkWorkPtr::setter( CScriptResult &execute, const CScriptVarPtr &Var ) { - if(execute) { - 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; -} - - -////////////////////////////////////////////////////////////////////////// -/// CScriptVarPrimitive -////////////////////////////////////////////////////////////////////////// - -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( CScriptResult &execute, int radix/*=0*/ ) { - return newScriptVar(toCString(radix)); -} - - -////////////////////////////////////////////////////////////////////////// -// 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"; } - - -////////////////////////////////////////////////////////////////////////// -// CScriptVarNull -////////////////////////////////////////////////////////////////////////// - -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; } - -CNumber CScriptVarNull::toNumber_Callback() { return 0; } -string CScriptVarNull::toCString(int radix/*=0*/) { return "null"; } -string CScriptVarNull::getVarType() { return "null"; } - - -////////////////////////////////////////////////////////////////////////// -/// 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); -*/ -} -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 indentString+getJSString(data); } -string CScriptVarString::getVarType() { return "string"; } - -CScriptVarPtr CScriptVarString::toObject() { - CScriptVarPtr ret = newScriptVar(CScriptVarPrimitivePtr(this), context->stringPrototype); - ret->addChild("length", newScriptVar(data.size()), SCRIPTVARLINK_CONSTANT); - return ret; -} - -CScriptVarPtr CScriptVarString::toString_CallBack( CScriptResult &execute, int radix/*=0*/ ) { - return this; -} - -int CScriptVarString::getChar(uint32_t Idx) { - if((string::size_type)Idx >= data.length()) - return -1; - else - return (unsigned char)data[Idx]; -} - - -////////////////////////////////////////////////////////////////////////// -/// CNumber -////////////////////////////////////////////////////////////////////////// - -NegativeZero_t NegativeZero; -declare_dummy_t(NaN); -Infinity InfinityPositive(1); -Infinity InfinityNegative(-1); -#if 1 -static inline bool _isNaN(volatile double *Value1, volatile double *Value2) { - return !(*Value1==*Value2); -} - -static inline bool isNaN(double Value) { - return _isNaN(&Value, &Value); -} -inline bool isNegZero(double d) { - double x=-0.0; - return memcmp(&d, &x, sizeof(double))==0; -} - -CNumber &CNumber::operator=(double Value) { - double integral; - if(isNegZero(Value)) - type=tnNULL, Int32=0; - else if(numeric_limits::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; -} -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)); - } -} - -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); - } -} - -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)); - } -} - - -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 || (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)); - } else - return CNumber(Int32 % Value.Int32); -} - -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)); -} - -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)); -} - -CNumber CNumber::abs() const { - if(sign()<0) return -CNumber(*this); - else return CNumber(*this); -} - -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< 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; - if(*firstdig=='-') firstdig++; - do { - temp = *p; - *p = *firstdig; - *firstdig = temp; - p--; - firstdig++; - } while (firstdig < p); - return buf; -} - -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; -} -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; - } - 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; -} - -#endif - - -////////////////////////////////////////////////////////////////////////// -/// 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); -} - - -////////////////////////////////////////////////////////////////////////// -// CScriptVarBool -////////////////////////////////////////////////////////////////////////// - -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; } - -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); } - -void CScriptVarObject::removeAllChildren() -{ - CScriptVar::removeAllChildren(); - value.clear(); -} - -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.append(nl).append(indentString); - } - destination.append("}"); - return destination; -} -string CScriptVarObject::getVarType() { return "object"; } -string CScriptVarObject::getVarTypeTagName() { return "Object"; } - -CScriptVarPtr CScriptVarObject::toObject() { return this; } - -CScriptVarPtr CScriptVarObject::valueOf_CallBack() { - if(value) - return value->valueOf_CallBack(); - return CScriptVar::valueOf_CallBack(); -} -CScriptVarPtr CScriptVarObject::toString_CallBack(CScriptResult &execute, int radix) { - if(value) - return value->toString_CallBack(execute, radix); - return newScriptVar("[object "+getVarTypeTagName()+"]"); -} - -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 -////////////////////////////////////////////////////////////////////////// - -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_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 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()); -} - -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), toStringRecursion(false) { - 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( CScriptResult &execute, int radix/*=0*/ ) { - ostringstream destination; - if(toStringRecursion) { - return newScriptVar(""); - } - toStringRecursion = true; - try { - int len = getArrayLength(); - for (int i=0;itoString(execute); - if (isetReturnVar(newScriptVar(c->getArgument("this")->getArrayLength())); -} - - -////////////////////////////////////////////////////////////////////////// -/// 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(CScriptResult &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 ""; - } -} - -#endif /* NO_REGEXP */ - - -////////////////////////////////////////////////////////////////////////// -/// CScriptVarDefaultIterator -////////////////////////////////////////////////////////////////////////// - -//declare_dummy_t(DefaultIterator); -CScriptVarDefaultIterator::CScriptVarDefaultIterator(CTinyJS *Context, const CScriptVarPtr &Object, int Mode) - : CScriptVarObject(Context, Context->iteratorPrototype), 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 -////////////////////////////////////////////////////////////////////////// - -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(CScriptResult &execute, int radix){ - bool hasRecursion; - return newScriptVar(getParsableString("", " ", 0, hasRecursion)); -} - -CScriptTokenDataFnc *CScriptVarFunction::getFunctionData() { return data; } - -void CScriptVarFunction::setFunctionData(CScriptTokenDataFnc *Data) { - if(data) { data->unref(); data = 0; } - if(Data) { - data = Data; data->ref(); - 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); - } -} - - -////////////////////////////////////////////////////////////////////////// -/// 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::setTemporaryMark_recursive(uint32_t ID) { - CScriptVarFunction::setTemporaryMark_recursive(ID); - boundedThis->setTemporaryMark_recursive(ID); - for(vector::iterator it=boundedArguments.begin(); it!=boundedArguments.end(); ++it) - (*it)->setTemporaryMark_recursive(ID); -} - -CScriptVarPtr CScriptVarFunctionBounded::callFunction( CScriptResult &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; } - - -////////////////////////////////////////////////////////////////////////// -/// CScriptVarFunctionNativeCallback -////////////////////////////////////////////////////////////////////////// - -CScriptVarFunctionNativeCallback::~CScriptVarFunctionNativeCallback() {} -CScriptVarPtr CScriptVarFunctionNativeCallback::clone() { return new CScriptVarFunctionNativeCallback(*this); } -void CScriptVarFunctionNativeCallback::callFunction(const CFunctionsScopePtr &c) { jsCallback(c, jsUserData); } - - -////////////////////////////////////////////////////////////////////////// -/// 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() {} -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 = ... -CScriptVarLinkWorkPtr CScriptVarScope::findInScopes(const string &childName) { - return CScriptVar::findChild(childName); -} -CScriptVarScopePtr CScriptVarScope::getParent() { return CScriptVarScopePtr(); } ///< no Parent - - -////////////////////////////////////////////////////////////////////////// -/// CScriptVarScopeFnc -////////////////////////////////////////////////////////////////////////// - -declare_dummy_t(ScopeFnc); -CScriptVarScopeFnc::~CScriptVarScopeFnc() {} -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); - } - return ret; -} - -void CScriptVarScopeFnc::setReturnVar(const CScriptVarPtr &var) { - addChildOrReplace(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) { - 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() { - 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 -////////////////////////////////////////////////////////////////////////// - -declare_dummy_t(ScopeLet); -CScriptVarScopeLet::CScriptVarScopeLet(const CScriptVarScopePtr &Parent) // constructor for LetScope - : 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; } -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 -////////////////////////////////////////////////////////////////////////// - -declare_dummy_t(ScopeWith); -CScriptVarScopeWith::~CScriptVarScopeWith() {} -CScriptVarPtr CScriptVarScopeWith::scopeLet() { // to create var like: let a = ... - return getParent()->scopeLet(); -} -CScriptVarLinkWorkPtr CScriptVarScopeWith::findInScopes(const string &childName) { - if(childName == "this") return with; - 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; -} - - -////////////////////////////////////////////////////////////////////////// -/// CTinyJS -////////////////////////////////////////////////////////////////////////// - -extern "C" void _registerFunctions(CTinyJS *tinyJS); -extern "C" void _registerStringFunctions(CTinyJS *tinyJS); -extern "C" void _registerMathFunctions(CTinyJS *tinyJS); - -CTinyJS::CTinyJS() { - CScriptVarPtr var; - t = 0; - haveTry = false; - first = 0; - uniqueID = 0; - currentMarkSlot = -1; - stackBase = 0; - - - ////////////////////////////////////////////////////////////////////////// - // Object-Prototype - // must be created as first object because this prototype is the base of all objects - objectPrototype = newScriptVar(Object); - - // 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); - - ////////////////////////////////////////////////////////////////////////// - // Add built-in classes - ////////////////////////////////////////////////////////////////////////// - // 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); - 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, 0, SCRIPTVARLINK_CONSTANT); - arrayPrototype = var->findChild(TINYJS_PROTOTYPE_CLASS); - 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(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"; - - ////////////////////////////////////////////////////////////////////////// - // RegExp -#ifndef NO_REGEXP - var = addNative("function RegExp()", this, &CTinyJS::native_RegExp, 0, SCRIPTVARLINK_CONSTANT); - regexpPrototype = var->findChild(TINYJS_PROTOTYPE_CLASS); - 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 */ - - ////////////////////////////////////////////////////////////////////////// - // 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", 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(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); - 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, 0, SCRIPTVARLINK_CONSTANT); - booleanPrototype = var->findChild(TINYJS_PROTOTYPE_CLASS); - 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, 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("")); - 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, 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); - pseudo_refered.push_back(&constUndefined); - 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); - _registerStringFunctions(this); - _registerMathFunctions(this); -} - -CTinyJS::~CTinyJS() { - ASSERT(!t); - for(vector::iterator it = pseudo_refered.begin(); it!=pseudo_refered.end(); ++it) - **it = CScriptVarPtr(); - for(int i=Error; iremoveAllChildren(); - scopes.clear(); - ClearUnreferedVars(); - root = CScriptVarPtr(); -#ifdef _DEBUG - for(CScriptVar *p = first; p; p=p->next) - printf("%p\n", p); -#endif -#if DEBUG_MEMORY - show_allocated(); -#endif -} - -////////////////////////////////////////////////////////////////////////// -/// throws an Error & Exception -////////////////////////////////////////////////////////////////////////// - -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()); -} -void CTinyJS::throwException(ERROR_TYPES ErrorType, const string &message ) { - throw new CScriptException(ErrorType, message, t->currentFile, t->currentLine(), t->currentColumn()); -} - -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()); -} -void CTinyJS::throwException(ERROR_TYPES ErrorType, const string &message, CScriptTokenizer::ScriptTokenPosition &Pos ){ - throw new CScriptException(ErrorType, message, t->currentFile, Pos.currentLine(), Pos.currentColumn()); -} - -////////////////////////////////////////////////////////////////////////// - -void CTinyJS::trace() { - root->trace(); -} - -void CTinyJS::execute(CScriptTokenizer &Tokenizer) { - evaluateComplex(Tokenizer); -} - -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); -} - -CScriptVarLinkPtr CTinyJS::evaluateComplex(CScriptTokenizer &Tokenizer) { - t = &Tokenizer; - CScriptResult execute; - try { - do { - execute_statement(execute); - while (t->tk==';') t->match(';'); // skip empty statements - } while (t->tk!=LEX_EOF); - } catch (...) { - haveTry = false; - t=0; // clean up Tokenizer - throw; // - } - t=0; - ClearUnreferedVars(execute.value); - - uint32_t UniqueID = allocUniqueID(); - setTemporaryID_recursive(UniqueID); - if(execute.value) execute.value->setTemporaryMark_recursive(UniqueID); - for(CScriptVar *p = first; p; p=p->next) - { - if(p->getTemporaryMark() != UniqueID) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wformat" - printf("%s %p\n", p->getVarType().c_str(), p); -#pragma GCC diagnostic pop - } - freeUniqueID(); - - if (execute.value) - return CScriptVarLinkPtr(execute.value); - // return undefined... - return CScriptVarLinkPtr(constScriptVar(Undefined)); -} -CScriptVarLinkPtr CTinyJS::evaluateComplex(const char *Code, const string &File, int Line, int Column) { - CScriptTokenizer Tokenizer(Code, File, Line, Column); - return evaluateComplex(Tokenizer); -} -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)->toString(); -} -string CTinyJS::evaluate(const char *Code, const string &File, int Line, int Column) { - 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); -} - -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, int LinkFlags) { - 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 (lex.tk == '.') { - lex.match('.'); - 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(); - funcName = lex.tkStr; - lex.match(LEX_ID); - } - - unique_ptr pFunctionData(new CScriptTokenDataFnc); - pFunctionData->name = funcName; - lex.match('('); - while (lex.tk!=')') { - pFunctionData->arguments.push_back(CScriptToken(LEX_ID, lex.tkStr)); - lex.match(LEX_ID); - if (lex.tk!=')') lex.match(',',')'); - } - lex.match(')'); - Var->setFunctionData(pFunctionData.release()); - Var->addChild(TINYJS_PROTOTYPE_CLASS, newScriptVar(Object), SCRIPTVARLINK_WRITABLE); - - base->addChild(funcName, Var, LinkFlags); - return Var; - -} - -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->getVarPtr()->addChild(TINYJS_FUNCTION_CLOSURE_VAR, scope(), 0); - funcVar->getVarPtr()->addChild(TINYJS_PROTOTYPE_CLASS, newScriptVar(Object), SCRIPTVARLINK_WRITABLE)->getVarPtr()->addChild(TINYJS_CONSTRUCTOR_VAR, funcVar->getVarPtr(), SCRIPTVARLINK_WRITABLE); - return funcVar; -} - -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) { - CScriptResult execute; - CScriptVarPtr retVar = callFunction(execute, Function, Arguments, This, newThis); - execute.cThrow(); - return retVar; -} - -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); - - 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)); - - CScriptResult function_execute; - 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) { - CScriptToken &FncArguments = Fnc->arguments[arguments_idx]; - if(FncArguments.token == LEX_ID) - functionRoot->addChildOrReplace(FncArguments.String(), value); - else - assign_destructuring_var(functionRoot, FncArguments.DestructuringVar(), value, function_execute); - } - } - arguments->addChild("length", newScriptVar(length_arguments)); - -#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); - ScopeControl.addFncScope(functionRoot); - if (Function->isNative()) { - try { - CScriptVarFunctionNativePtr(Function)->callFunction(functionRoot); - CScriptVarLinkPtr ret = functionRoot->findChild(TINYJS_RETURN_VAR); - function_execute.set(CScriptResult::Return, ret ? CScriptVarPtr(ret) : constUndefined); - } catch (CScriptVarPtr v) { - 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 - * 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); - if(Fnc->body.front().token == '{') - 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(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; - } - // 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; - - // 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 - 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->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(); - // both a String or one a String and op='+' - if( (a_isString && b_isString) || ((a_isString || b_isString) && op == '+')) { - string da = a->isNull() ? "" : a->toString(execute); - string db = b->isNull() ? "" : b->toString(execute); - switch (op) { - 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(da': return constScriptVar(da>db); - case LEX_GEQUAL: return constScriptVar(da>=db); - } - } - // 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_EQUAL: return constScriptVar(true); - case LEX_NEQUAL: - case LEX_GEQUAL: - case LEX_LEQUAL: - case '<': - case '>': return constScriptVar(false); - } - } - 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"); - } -} - -void CTinyJS::assign_destructuring_var(const CScriptVarPtr &Scope, const CScriptTokenDataDestructuringVar &Objc, const CScriptVarPtr &Val, CScriptResult &execute) { - if(!execute) return; - if(Objc.vars.size() == 1) { - if(Scope) - Scope->addChildOrReplace(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(;;) { - 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); - CScriptVarPtr Val = execute_assignment(execute); - if(hideLetScope) CScriptVarScopeLetPtr(scope())->setletExpressionInitMode(false); - assign_destructuring_var(0, Objc, Val, execute); - } - if (t->tk == ',') - t->match(','); - else - break; - } -} -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->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->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); - } - } - } -} - -CScriptVarLinkWorkPtr CTinyJS::execute_literals(CScriptResult &execute) { - switch(t->tk) { - case LEX_ID: - if(execute) { - 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") - a = root; // fake 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; - } - t->match(LEX_ID); - break; - case LEX_INT: - { - CScriptVarPtr a = newScriptVar(t->getToken().Int()); - a->setExtensible(false); - 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; -#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; -#endif /* NO_REGEXP */ - case LEX_T_OBJECT_LITERAL: - if(execute) { - 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); - } - } - return a; - } - } else - 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('('); - t->check(LEX_T_FORWARD); - ScopeControl.addLetScope(); - execute_statement(execute); // execute forwarder - execute_var_init(true, execute); - t->match(')'); - return execute_assignment(execute); - } else { - t->skip(t->getToken().Int()); - } - break; - case LEX_T_FUNCTION_OPERATOR: - if(execute) { - CScriptVarLinkWorkPtr a = parseFunctionDefinition(t->getToken()); - t->match(LEX_T_FUNCTION_OPERATOR); - return a; - } - 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) { - CScriptVarPtr Constructor = objClass->getVarPtr(); - if(Constructor->isFunction()) { - CScriptVarPtr obj(newScriptVar(Object)); - CScriptVarLinkPtr prototype = Constructor->findChild(TINYJS_PROTOTYPE_CLASS); - if(!prototype || prototype->getVarPtr()->isUndefined() || prototype->getVarPtr()->isNull()) { - prototype = Constructor->addChild(TINYJS_PROTOTYPE_CLASS, newScriptVar(Object), SCRIPTVARLINK_WRITABLE); - obj->addChildOrReplace(TINYJS___PROTO___VAR, prototype, SCRIPTVARLINK_WRITABLE); - } - 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('('); - while(t->tk!=')') { - CScriptVarPtr value = execute_assignment(execute).getter(execute); - if (execute) - arguments.push_back(value); - if (t->tk!=')') t->match(',', ')'); - } - t->match(')'); - } - if(execute) { - CScriptVarPtr returnVar = callFunction(execute, Constructor, arguments, obj, &obj); - if(returnVar->isObject()) - return CScriptVarLinkWorkPtr(returnVar); - return CScriptVarLinkWorkPtr(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 constScriptVar(true); - case LEX_R_FALSE: - t->match(LEX_R_FALSE); - return constScriptVar(false); - case LEX_R_NULL: - t->match(LEX_R_NULL); - return constScriptVar(Null); - case '(': - if(execute) { - t->match('('); - CScriptVarLinkWorkPtr a = execute_base(execute).getter(execute); - t->match(')'); - return a; - } 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; - } - return constScriptVar(Undefined); - -} -CScriptVarLinkWorkPtr CTinyJS::execute_member(CScriptVarLinkWorkPtr &parent, CScriptResult &execute) { - CScriptVarLinkWorkPtr a; - parent.swap(a); - if(t->tk == '.' || t->tk == '[') { - while(t->tk == '.' || t->tk == '[') { - 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(); - t->match(LEX_ID); - } else { - if(execute) { - t->match('['); - name = execute_base(execute)->toString(execute); - t->match(']'); - } else - t->skip(t->getToken().Int()); - } - if (execute) { - CScriptVarPtr aVar = a; - a = aVar->findChildWithPrototypeChain(name); - if(!a) { - a(constScriptVar(Undefined), name); - a.setReferencedOwner(aVar); - } - } - } - } - return a; -} - -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)); - 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 - vector arguments; - while(t->tk!=')') { - CScriptVarLinkWorkPtr value = execute_assignment(execute).getter(execute); -// path += (*value)->getString(); - if (execute) { - arguments.push_back(value); - } - if (t->tk!=')') { t->match(','); /*path+=',';*/ } - } - t->match(')'); //path+=')'; - // setup a return variable - 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, fnc, arguments, This); - } - } else { - // function, but not executing - just parse args and be done - t->match('('); - while (t->tk != ')') { - CScriptVarLinkWorkPtr value = execute_base(execute); - // if (t->tk!=')') t->match(','); - } - t->match(')'); - } - a = execute_member(parent = a, execute); - } - return a; -} -// R->L: Precedence 3 (in-/decrement) ++ -- -// R<-L: Precedence 4 (unary) ! ~ + - typeof void delete -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(CScriptResult &execute) { - CScriptVarLinkWorkPtr a; - switch(t->tk) { - case '-': - if(execute_unary_rhs(execute, a)) - a(newScriptVar(-a->getVarPtr()->toNumber(execute))); - break; - case '+': - if(execute_unary_rhs(execute, a)) - a = newScriptVar(a->getVarPtr()->toNumber(execute)); - break; - case '!': - if(execute_unary_rhs(execute, a)) - a = constScriptVar(!a->getVarPtr()->toBoolean()); - break; - case '~': - if(execute_unary_rhs(execute, a)) - a = newScriptVar(~a->getVarPtr()->toNumber(execute)); - break; - case LEX_R_TYPEOF: - if(execute_unary_rhs(execute, a)) - a = newScriptVar(a->getVarPtr()->getVarType()); - break; - case LEX_R_VOID: - if(execute_unary_rhs(execute, a)) - a = constScriptVar(Undefined); - break; - case LEX_R_DELETE: - t->match(LEX_R_DELETE); // delete - a = execute_unary(execute); // no getter - delete can remove the accessor - if (execute) { - // !!! no right-hand-check by delete - if(a->isOwned() && a->isConfigurable() && a->getName() != "this") { - a->getOwner()->removeLink(a); // removes the link from owner - a = constScriptVar(true); - } - else - a = constScriptVar(false); - } - break; - case LEX_PLUSPLUS: - case LEX_MINUSMINUS: - { - int op = t->tk; - t->match(op); // pre increment/decrement - CScriptTokenizer::ScriptTokenPosition ErrorPos = t->getPos(); - a = execute_function_call(execute); - if (execute) { - 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; - 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(op); - if (execute) { - 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) * / % -CScriptVarLinkWorkPtr CTinyJS::execute_term(CScriptResult &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); - CScriptVarLinkWorkPtr b = execute_unary(execute); // L->R - if (execute) { - CheckRightHandVar(execute, b); - a = mathsOp(execute, a.getter(execute), b.getter(execute), op); - } - } - } - return a; -} - -// L->R: Precedence 6 (addition/subtraction) + - -CScriptVarLinkWorkPtr CTinyJS::execute_expression(CScriptResult &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); - CScriptVarLinkWorkPtr b = execute_term(execute); // L->R - if (execute) { - CheckRightHandVar(execute, b); - a = mathsOp(execute, a.getter(execute), b.getter(execute), op); - } - } - } - return a; -} - -// L->R: Precedence 7 (bitwise shift) << >> >>> -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); - while (t->tk>=LEX_SHIFTS_BEGIN && t->tk<=LEX_SHIFTS_END) { - int op = t->tk; - t->match(t->tk); - - CScriptVarLinkWorkPtr b = execute_expression(execute); // L->R - if (execute) { - CheckRightHandVar(execute, a); - // not in-place, so just replace - a = mathsOp(execute, a.getter(execute), b.getter(execute), op); - } - } - } - return a; -} -// L->R: Precedence 8 (relational) < <= > <= in instanceof -// L->R: Precedence 9 (equality) == != === !=== -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))) { - 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); - 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) { - 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 = allocUniqueID(); - CScriptVarPtr object = a->getVarPtr()->findChild(TINYJS___PROTO___VAR); - 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 - a = mathsOp(execute, a, b, op); - } - } - } - return a; -} - -// L->R: Precedence 10 (bitwise-and) & -// L->R: Precedence 11 (bitwise-xor) ^ -// L->R: Precedence 12 (bitwise-or) | -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); - a = a.getter(execute); - while (t->tk==op) { - t->match(t->tk); - 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.getter(execute), op); - } - } - } - return a; -} -// L->R: Precedence 13 ==> (logical-and) && -// L->R: Precedence 14 ==> (logical-or) || -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 - 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 - t->skip(t->getToken().Int()); - } - return a; -} - -// L<-R: Precedence 15 (condition) ?: -CScriptVarLinkWorkPtr CTinyJS::execute_condition(CScriptResult &execute) { - CScriptVarLinkWorkPtr a = execute_logic(execute); - if (t->tk=='?') { - CheckRightHandVar(execute, a); - bool cond = execute && a.getter(execute)->toBoolean(); - 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; -} - -// 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(CScriptResult &execute) { - return execute_assignment(execute_condition(execute), 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(); - t->match(t->tk); - CScriptVarLinkWorkPtr rhs = execute_assignment(execute).getter(execute); // L<-R - if (execute) { - 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()) { - 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.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]); - 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.getter(execute); -} -// L->R: Precedence 17 (comma) , -CScriptVarLinkPtr CTinyJS::execute_base(CScriptResult &execute) { - CScriptVarLinkPtr a; - for(;;) - { - a = execute_assignment(execute); // L->R - if (t->tk == ',') { - t->match(','); - } else - break; - } - return a; -} -void CTinyJS::execute_block(CScriptResult &execute) { - if(execute) { - t->match('{'); - CScopeControl ScopeControl(this); - if(t->tk==LEX_T_FORWARD) // 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()); -} -void CTinyJS::execute_statement(CScriptResult &execute) { - switch(t->tk) { - case '{': /* A block of code */ - execute_block(execute); - break; - case ';': /* Empty statement - to allow things like ;;; */ - t->match(';'); - break; - case LEX_T_FORWARD: - { - CScriptVarPtr in_scope = scope()->scopeLet(); - STRING_SET_t *varNames = t->getToken().Forwarder().varNames; - for(int i=0; ifindChild(*it); - if(!a) in_scope->addChild(*it, constScriptVar(Undefined), i==CScriptTokenDataForwards::CONSTS ? SCRIPTVARLINK_CONSTDEFAULT : SCRIPTVARLINK_VARDEFAULT); - } - 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) { - 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: - case LEX_R_CONST: - if(execute) - { - CScopeControl ScopeControl(this); - bool isLet = t->tk==LEX_R_LET, let_ext=false; - t->match(t->tk); - if(isLet && t->tk=='(') { - let_ext = true; - t->match('('); - t->check(LEX_T_FORWARD); - ScopeControl.addLetScope(); - execute_statement(execute); // forwarder - } - execute_var_init(let_ext, execute); - if(let_ext) { - t->match(')'); - execute_statement(execute); - } else - t->match(';'); - } else - t->skip(t->getToken().Int()); - break; - case LEX_R_WITH: - if(execute) { - t->match(LEX_R_WITH); - t->match('('); - CScriptVarLinkPtr var = execute_base(execute); - t->match(')'); - CScopeControl ScopeControl(this); - ScopeControl.addWithScope(var); - execute_statement(execute); - } else - t->skip(t->getToken().Int()); - break; - case LEX_R_IF: - if(execute) { - t->match(LEX_R_IF); - t->match('('); - bool cond = execute_base(execute)->toBoolean(); - 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()); - break; - case LEX_T_FOR_IN: - { - CScriptTokenDataLoop &LoopData = t->getToken().Loop(); - t->match(LEX_T_FOR_IN); - if(!execute) break; - - CScopeControl ScopeControl(this); - if(LoopData.init.size()) { - t->pushTokenScope(LoopData.init); - ScopeControl.addLetScope(); - execute_statement(execute); // forwarder - } - 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 - execute = tmp_execute; - } - break; - } - 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_T_LOOP: - { - CScriptTokenDataLoop &LoopData = t->getToken().Loop(); - t->match(LEX_T_LOOP); - if(!execute) break; - - CScopeControl ScopeControl(this); - 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; - } - - 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; - } - 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) - { - CScriptResult::TYPE type = t->tk==LEX_R_BREAK ? CScriptResult::Break : CScriptResult::Continue; - string label; - t->match(t->tk); - if(t->tk == LEX_ID) { - 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 = constUndefined; - if (t->tk != ';') - result = execute_base(execute); - t->match(';'); - execute.set(CScriptResult::Return, result); - } else - t->skip(t->getToken().Int()); - break; - case LEX_R_FUNCTION: - if(execute) { - CScriptVarLinkWorkPtr funcVar = parseFunctionDefinition(t->getToken()); - scope()->scopeVar()->addChildOrReplace(funcVar->getName(), funcVar, SCRIPTVARLINK_VARDEFAULT); - } - case LEX_T_FUNCTION_PLACEHOLDER: - t->match(t->tk); - break; - case LEX_T_TRY: - if(execute) { - CScriptTokenDataTry &TryData = t->getToken().Try(); - - bool old_haveTry = haveTry; - haveTry = true; - - // execute try-block - t->pushTokenScope(TryData.tryBlock); - execute_block(execute); - - 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(); - t->pushTokenScope(catchBlock->condition); // condition; - execute_statement(catch_execute); // forwarder - assign_destructuring_var(0, *catchBlock->indentifiers, execute.value, catch_execute); - bool condition = true; - 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; - } - } - } - if(TryData.finallyBlock.size()) { - CScriptResult finally_execute; // alway execute finally-block - t->pushTokenScope(TryData.finallyBlock); // finally; - execute_block(finally_execute); - execute(finally_execute); - } - // 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); - } - - } - t->match(LEX_T_TRY); - break; - case LEX_R_THROW: - if(execute) { - CScriptTokenizer::ScriptTokenPosition tokenPos = t->getPos(); - // int tokenStart = t->getToken().pos; - t->match(LEX_R_THROW); - CScriptVarPtr a = execute_base(execute); - if(execute) { - 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()); - } - } 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); - t->match(')'); - if(execute) { - t->match('{'); - CScopeControl ScopeControl(this); - 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_TYPEEQUAL); - 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) - 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(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: - ASSERT(found); - execute_statement(execute); - break; - } - } -end_while: - t->match('}'); - 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_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.isBreak() && find(Labels.begin(), Labels.end(), execute.target) != Labels.end()) { // break this label - execute.set(CScriptResult::Normal, false); - } - } - else - execute_statement(execute); - } - break; - case LEX_EOF: - t->match(LEX_EOF); - break; - default: - 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; - } -} - - -/// Finds a child, looking recursively up the scopes -CScriptVarLinkPtr CTinyJS::findInScopes(const string &childName) { - return scope()->findInScopes(childName); -} - -////////////////////////////////////////////////////////////////////////// -/// Object -////////////////////////////////////////////////////////////////////////// - -void CTinyJS::native_Object(const CFunctionsScopePtr &c, void *data) { - c->setReturnVar(c->getArgument(0)->toObject()); -} -void CTinyJS::native_Object_getPrototypeOf(const CFunctionsScopePtr &c, void *data) { - if(c->getArgumentsLength()>=1) { - CScriptVarPtr obj = c->getArgument(0); - if(obj->isObject()) { - c->setReturnVar(obj->findChild(TINYJS___PROTO___VAR)); - return; - } - } - c->throwError(TypeError, "argument is not an object"); -} - -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 idx=0; - for(STRING_SET_it it=keys.begin(); it!=keys.end(); ++it) - returnVar->setArrayIndex(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_prototype_toString(const CFunctionsScopePtr &c, void *data) { - CScriptResult execute; - int radix = 10; - if(c->getArgumentsLength()>=1) radix = c->getArgument("radix")->toNumber().toInt32(); - c->setReturnVar(c->getArgument("this")->toString_CallBack(execute, radix)); - if(!execute) { - // TODO - } -} - -////////////////////////////////////////////////////////////////////////// -/// Array -////////////////////////////////////////////////////////////////////////// - -void CTinyJS::native_Array(const CFunctionsScopePtr &c, void *data) { - CScriptVarPtr returnVar = c->newScriptVar(Array); - c->setReturnVar(returnVar); - int length = c->getArgumentsLength(); - CScriptVarPtr Argument_0_Var = c->getArgument(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)); -} - -////////////////////////////////////////////////////////////////////////// -/// String -////////////////////////////////////////////////////////////////////////// - -void CTinyJS::native_String(const CFunctionsScopePtr &c, void *data) { - CScriptVarPtr arg; - if(c->getArgumentsLength()==0) - arg = newScriptVar(""); - else - 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 -////////////////////////////////////////////////////////////////////////// - -void CTinyJS::native_Number(const CFunctionsScopePtr &c, void *data) { - CScriptVarPtr arg; - if(c->getArgumentsLength()==0) - arg = newScriptVar(0); - else - arg = newScriptVar(c->getArgument(0)->toNumber()); - if(data) - c->setReturnVar(arg->toObject()); - else - c->setReturnVar(arg); -} - - -////////////////////////////////////////////////////////////////////////// -/// Boolean -////////////////////////////////////////////////////////////////////////// - -void CTinyJS::native_Boolean(const CFunctionsScopePtr &c, void *data) { - CScriptVarPtr arg; - if(c->getArgumentsLength()==0) - arg = constScriptVar(false); - else - arg = constScriptVar(c->getArgument(0)->toBoolean()); - if(data) - c->setReturnVar(arg->toObject()); - else - 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 -////////////////////////////////////////////////////////////////////////// - -void CTinyJS::native_Function(const CFunctionsScopePtr &c, void *data) { - int length = c->getArgumentsLength(); - string params, body; - if(length>=1) - body = c->getArgument(length-1)->toString(); - if(length>=2) { - params = c->getArgument(0)->toString(); - for(int i=1; igetArgument(i)->toString()); - } - } - c->setReturnVar(parseFunctionsBodyFromString(params,body)); -} - -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 Args; - for(int i=1; igetArgument(i)); - c->setReturnVar(callFunction(Fnc, Args, This)); -} -void CTinyJS::native_Function_prototype_apply(const CFunctionsScopePtr &c, void *data) { - int length=0; - CScriptVarPtr Fnc = c->getArgument("this"); - 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); - 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) Args.push_back(value); - else Args.push_back(constScriptVar(Undefined)); - } - 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 -////////////////////////////////////////////////////////////////////////// - -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)->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)); } -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->getArgument("jsCode")->toString(); - CScriptVarScopePtr scEvalScope = scopes.back(); // save scope - scopes.pop_back(); // go back to the callers scope - CScriptResult execute; - CScriptTokenizer *oldTokenizer = t; t=0; - try { - CScriptTokenizer Tokenizer(Code.c_str(), "eval"); - t = &Tokenizer; - do { - 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(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; - } else - throw e; - } catch (...) { // all other exceptions - t = oldTokenizer; // restore tokenizer - scopes.push_back(scEvalScope); // restore Scopes; - throw; // re-throw - } - t = oldTokenizer; // restore tokenizer - scopes.push_back(scEvalScope); // restore Scopes; - 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) { - c->setReturnVar(constScriptVar(c->getArgument("objc")->toNumber().isNaN())); -} - -void CTinyJS::native_isFinite(const CFunctionsScopePtr &c, void *data) { - c->setReturnVar(constScriptVar(c->getArgument("objc")->toNumber().isFinite())); -} - -void CTinyJS::native_parseInt(const CFunctionsScopePtr &c, void *) { - 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 *) { - 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")->toString(); - // "§" is a spezal-token - it's for the tokenizer and means the code begins not in Statement-level - CScriptVarLinkWorkPtr returnVar; - CScriptTokenizer *oldTokenizer = t; t=0; - try { - CScriptTokenizer Tokenizer(Code.c_str(), "JSON.parse", 0, -1); - t = &Tokenizer; - CScriptResult execute; - returnVar = execute_literals(execute); - t->match(LEX_EOF); - } catch (CScriptException *e) { - t = oldTokenizer; - throw e; - } - t = oldTokenizer; - - if(returnVar) - c->setReturnVar(returnVar); -} - -void CTinyJS::setTemporaryID_recursive(uint32_t ID) { - for(vector::iterator it = pseudo_refered.begin(); it!=pseudo_refered.end(); ++it) - if(**it) (**it)->setTemporaryMark_recursive(ID); - for(int i=Error; isetTemporaryMark_recursive(ID); - root->setTemporaryMark_recursive(ID); -} - -void CTinyJS::ClearUnreferedVars(const CScriptVarPtr &extra/*=CScriptVarPtr()*/) { - uint32_t UniqueID = allocUniqueID(); - setTemporaryID_recursive(UniqueID); - if(extra) extra->setTemporaryMark_recursive(UniqueID); - CScriptVar *p = first; - - while(p) - { - if(p->getTemporaryMark() != UniqueID) - { - CScriptVarPtr var = p; - var->removeAllChildren(); - p = var->next; - } - else - p = p->next; - } - freeUniqueID(); -} - diff --git a/src/TinyJS/TinyJS_Functions.cpp b/src/TinyJS/TinyJS_Functions.cpp deleted file mode 100755 index 1b74035..0000000 --- a/src/TinyJS/TinyJS_Functions.cpp +++ /dev/null @@ -1,162 +0,0 @@ -/* - * 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-2014 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 -#include -#include -#include -#include "TinyJS/TinyJS.h" - -using namespace std; -// ----------------------------------------------- Actual Functions - -static void scTrace(const CFunctionsScopePtr &c, void * userdata) { - CTinyJS *js = (CTinyJS*)userdata; - if(c->getArgumentsLength()) - c->getArgument(0)->trace(); - else - js->getRoot()->trace("root"); -} - -static void scObjectDump(const CFunctionsScopePtr &c, void *) { - c->getArgument("this")->trace("> "); -} - -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(); - - int val = 0; - if (str.length()==1) - val = str.operator[](0); - c->setReturnVar(c->newScriptVar(val)); -} -*/ -static void scJSONStringify(const CFunctionsScopePtr &c, void *) { - 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"); -} - -static void scArrayContains(const CFunctionsScopePtr &c, void *data) { - CScriptVarPtr obj = c->getArgument("obj"); - CScriptVarPtr arr = c->getArgument("this"); - - int l = arr->getArrayLength(); - CScriptVarPtr equal = c->constScriptVar(Undefined); - for (int i=0;imathsOp(arr->getArrayIndex(i), LEX_EQUAL); - if(equal->toBoolean()) { - c->setReturnVar(c->constScriptVar(true)); - return; - } - } - c->setReturnVar(c->constScriptVar(false)); -} - -static void scArrayRemove(const CFunctionsScopePtr &c, void *data) { - CScriptVarPtr obj = c->getArgument("obj"); - CScriptVarPtr arr = c->getArgument("this"); - int i; - vector removedIndices; - - int l = arr->getArrayLength(); - CScriptVarPtr equal = c->constScriptVar(Undefined); - for (i=0;imathsOp(arr->getArrayIndex(i), LEX_EQUAL); - if(equal->toBoolean()) { - 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); - } - } - } - } -} - -static void scArrayJoin(const CFunctionsScopePtr &c, void *data) { - 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)->toString(); - } - - c->setReturnVar(c->newScriptVar(sstr.str())); -} - -// ----------------------------------------------- Register Functions -void registerFunctions(CTinyJS *tinyJS) { -} -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/src/TinyJS/TinyJS_MathFunctions.cpp b/src/TinyJS/TinyJS_MathFunctions.cpp deleted file mode 100644 index 6f7eae9..0000000 --- a/src/TinyJS/TinyJS_MathFunctions.cpp +++ /dev/null @@ -1,441 +0,0 @@ -/* - * 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-2014 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 -#include -#include -#include -#include "TinyJS/TinyJS.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 )) - -#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); - } - - 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 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) { - 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) { - 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) { - 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) { - 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(); - CNumber ret(InfinityPositive); - for(int i=0; ia) ret=a; - } - RETURN(ret); -} - -//Math.max(a,b) - returns maximum of two given values -static void scMathMax(const CFunctionsScopePtr &c, void *userdata) { - int length = c->getArgumentsLength(); - CNumber ret(InfinityNegative); - for(int i=0; ib) 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) { - PARAMETER_TO_NUMBER(a,"a"); RETURN_NAN_IS_NAN(a); - RETURN(a.isZero() ? 0 : a.sign()); -} -static void scMathRandom(const CFunctionsScopePtr &c, void *) { - static int inited=0; - if(!inited) { - inited = 1; - srand((unsigned int)time(NULL)); - } - 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) { - 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) { - 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) { - 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) { - 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) { - 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) { - 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) { - 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) { - 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) { - 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) { - 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) { - 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) { - 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) { - 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.atanh(a) - returns trig. hyperbolic arctangent of given angle in radians -static void scMathATanh(const CFunctionsScopePtr &c, void *userdata) { - 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) { - 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) { - 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) { - 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) { - 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) { - 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) { - 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) {} -extern "C" void _registerMathFunctions(CTinyJS *tinyJS) { - - CScriptVarPtr Math = tinyJS->getRoot()->addChild("Math", tinyJS->newScriptVar(Object), SCRIPTVARLINK_CONSTANT); - - // --- Math and Trigonometry functions --- - 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_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, 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_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, SCRIPTVARLINK_BUILDINDEFAULT); - tinyJS->addNative("function Math.sqrt(a)", scMathSqrt, 0, SCRIPTVARLINK_BUILDINDEFAULT); - -} diff --git a/src/TinyJS/TinyJS_StringFunctions.cpp b/src/TinyJS/TinyJS_StringFunctions.cpp deleted file mode 100644 index 3286e20..0000000 --- a/src/TinyJS/TinyJS_StringFunctions.cpp +++ /dev/null @@ -1,567 +0,0 @@ -/* - * 42TinyJS - * - * A fork of TinyJS with the goal to makes a more JavaScript/ECMA compliant engine - * - * Authored By Armin Diedering - * - * Copyright (C) 2010-2014 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 -#include "TinyJS/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 = 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 - c->setReturnVar(c->newScriptVar("")); -} - -static void scStringCharCodeAt(const CFunctionsScopePtr &c, void *) { - string str = this2string(c); - int p = c->getArgument("pos")->toNumber().toInt32(); - if (p>=0 && p<(int)str.length()) - c->setReturnVar(c->newScriptVar((unsigned char)str.at(p))); - else - c->setReturnVar(c->constScriptVar(NaN)); -} - -static void scStringConcat(const CFunctionsScopePtr &c, void *userdata) { - int length = c->getArgumentsLength(); - string str = this2string(c); - for(int i=(int)userdata; igetArgument(i)->toString()); - c->setReturnVar(c->newScriptVar(str)); -} - -static void scStringIndexOf(const CFunctionsScopePtr &c, void *userdata) { - 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, pos) : str.rfind(search, pos); - 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); -#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()); - } - 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))); -#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) { - 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 = this2string(c); - int length = c->getArgumentsLength()-((int)userdata & 1); - bool slice = ((int)userdata & 2) == 0; - 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")->toNumber().toInt32(); - 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 *) { - const string str = this2string(c); - - 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) - return; - 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); - } catch(regex_error e) { - 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))); - else - result->setArrayIndex(length++, c->constScriptVar(Undefined)); - if(length>=limit) break; - } - if(length>=limit) break; -#endif - 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 = this2string(c); - int length = c->getArgumentsLength()-(int)userdata; - 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")->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 = this2string(c); - transform(str.begin(), str.end(), str.begin(), ::tolower); - c->setReturnVar(c->newScriptVar(str)); -} - -static void scStringToUpperCase(const CFunctionsScopePtr &c, void *) { - 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 = this2string(c); - 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->getArgument("ch")->toString();; - 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->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) {} -extern "C" void _registerStringFunctions(CTinyJS *tinyJS) { - CScriptVarPtr fnc; - // charAt - 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, SCRIPTVARLINK_BUILDINDEFAULT); - tinyJS->addNative("function String.charCodeAt(this,pos)", scStringCharCodeAt, 0, SCRIPTVARLINK_BUILDINDEFAULT); - // concat - 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,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,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, 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, 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, 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, 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, SCRIPTVARLINK_BUILDINDEFAULT); - tinyJS->addNative("function String.trim(this)", scStringTrim, 0, SCRIPTVARLINK_BUILDINDEFAULT); - // trimLeft - 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, SCRIPTVARLINK_BUILDINDEFAULT); - tinyJS->addNative("function String.trimRight(this)", scStringTrim, (void*)2, SCRIPTVARLINK_BUILDINDEFAULT); - - 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, 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/src/TinyJS/TinyJS_Threading.cpp b/src/TinyJS/TinyJS_Threading.cpp deleted file mode 100644 index 487bcc3..0000000 --- a/src/TinyJS/TinyJS_Threading.cpp +++ /dev/null @@ -1,239 +0,0 @@ -#include "TinyJS/TinyJS_Threading.h" - -#undef HAVE_THREADING -#if !defined(NO_THREADING) && !defined(HAVE_CUSTOM_THREADING_IMPL) -# define HAVE_THREADING -# ifdef HAVE_CXX_THREADS -# include -# else -# 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 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) - -#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); - } - void *getRealMutex() { return &mutex; } - pthread_mutex_t mutex; -}; - - -CScriptMutex::CScriptMutex() { - mutex = new CScriptMutex_impl; -} - -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) : retvar((void*)-1), activ(false), running(false), started(false), This(_this) {} - ~CScriptThread_impl() {} - void Run() { - if(started) return; - activ = true; -// 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(bool Wait) { - if(!running) return started ? retValue() : -1; - activ = 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 = 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; -}; - -CScriptThread::CScriptThread() { - thread = new CScriptThread_impl(this); -} -CScriptThread::~CScriptThread() { -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor" - delete thread; -#pragma GCC diagnostic pop -} -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/src/TinyJS/pool_allocator.cpp b/src/TinyJS/pool_allocator.cpp deleted file mode 100644 index 3a34fba..0000000 --- a/src/TinyJS/pool_allocator.cpp +++ /dev/null @@ -1,258 +0,0 @@ -/* - * 42TinyJS - * - * A fork of TinyJS with the goal to makes a more JavaScript/ECMA compliant engine - * - * Authored By Armin Diedering - * - * Copyright (C) 2010-2014 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 "TinyJS/pool_allocator.h" -#include -#include -#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 ) -{ - num_objects = numObjects; - object_size = objectSize >= sizeof(block) ? objectSize : sizeof(block); - - head_of_free_list = head = 0; - -#ifdef DEBUG_POOL_ALLOCATOR - if(for_class) name = for_class; - allocs= - frees= - max = - current= - blocks = -#endif - refs = 0; -} - -fixed_size_allocator::~fixed_size_allocator() -{ - while(head) { - char *p = (char*)head; - head = head->next; - delete [] p; - } -#ifdef DEBUG_POOL_ALLOCATOR -# ifndef LOG_POOL_ALLOCATOR_MEMORY_USAGE - 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", 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 ) { - refs++; -#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); - } 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]; - -#ifdef DEBUG_POOL_ALLOCATOR - blocks++; -#endif - } - return p; -} -#include -#ifndef ASSERT -# define ASSERT(X) assert(X) -#endif - -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; -} - -static class _allocator_pool -{ -public: - _allocator_pool() : allocator_pool(0) { -#ifdef LOG_POOL_ALLOCATOR_MEMORY_USAGE - last_ok = last_access = 0; -#endif - } - ~_allocator_pool() { - 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_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 - last_access++; -#endif - if(last_allocate_allocator && last_allocate_allocator->objectSize()==size) { -#ifdef LOG_POOL_ALLOCATOR_MEMORY_USAGE - last_ok++; -#endif - return last_allocate_allocator; - } - else if(last_free_allocator && last_free_allocator->objectSize()==size) { -#ifdef LOG_POOL_ALLOCATOR_MEMORY_USAGE - last_ok++; -#endif - 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; - } - } - 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; - -//#define WITH_TIME_LOGGER -#include "TinyJS/time_logger.h" - -TimeLoggerCreate(alloc, false) -TimeLoggerCreate(free, false) -#ifdef NO_THREADING -# define LOCK do{}while(0) -#else -CScriptMutex fixed_size_allocator::locker; -class lock_help { -public: - 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; - if(!allocator_pool.allocator_pool) { - allocator_pool.allocator_pool = new allocator_pool_t(); - allocator_pool.last_allocate_allocator = allocator_pool.last_free_allocator = 0; - } - 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; - if(!allocator_pool.allocator_pool) { - ASSERT(0/* free called but not allocator defined*/); - 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/src/program.cpp b/src/program.cpp index 759b9dc..063e878 100644 --- a/src/program.cpp +++ b/src/program.cpp @@ -1,50 +1,2 @@ -#include "TinyJS/TinyJS.h" -#include -#include -#include - - -void js_print(const CFunctionsScopePtr &v, void *) { - printf("> %s\n", v->getArgument("text")->toString().c_str()); -} - -void js_dump(const CFunctionsScopePtr &v, void *) { - v->getContext()->getRoot()->trace("> "); -} - - -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; - CTinyJS *js = new CTinyJS(); - 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 */ - 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!\");"); - } catch (CScriptException *e) { - printf("%s\n", e->toString().c_str()); - delete e; - } - int lineNumber = 0; - while (js->evaluate("lets_quit") == "0") { - std::string buffer; - if(!std::getline(std::cin, buffer)) break; - try { - js->execute(buffer, "console.input", lineNumber++); - } catch (CScriptException *e) { - printf("%s\n", e->toString().c_str()); - delete e; - } - } - delete js; - return 0; -} \ No newline at end of file +#define CATCH_CONFIG_MAIN +#include "catch.hpp" \ No newline at end of file diff --git a/src/tests.cpp b/src/tests.cpp new file mode 100644 index 0000000..66a6fe3 --- /dev/null +++ b/src/tests.cpp @@ -0,0 +1,141 @@ +#include "catch.hpp" +#include +#include "9float.hpp" + +using namespace ninefloat; + +TEST_CASE("Addition") +{ + SECTION("with positive numbers") + { + int i = 1; + float j = 0.5; + Q target = 1.5; + Q qi = i; + Q qj = j; + Q qres = qi + qj; + REQUIRE(qres==target); + } + SECTION("with negative numbers") + { + int i = -1; + float j = -0.5; + Q target = -1.5; + Q qi = i; + Q qj = j; + Q qres = qi + qj; + REQUIRE(qres==target); + } + SECTION("with mixed numbers") + { + int i = 1; + float j = -0.5; + Q target = 0.5; + Q qi = i; + Q qj = j; + Q qres = qi + qj; + REQUIRE(qres==target); + } +} + +TEST_CASE("Substraction") +{ + SECTION("with positive numbers") + { + int i = 1; + float j = 0.5; + Q target = 0.5; + Q qi = i; + Q qj = j; + Q qres = qi - qj; + REQUIRE(qres==target); + } + SECTION("with negative numbers") + { + int i = -1; + float j = -0.5; + Q target = -0.5; + Q qi = i; + Q qj = j; + Q qres = qi - qj; + REQUIRE(qres==target); + } + SECTION("with mixed numbers") + { + int i = 1; + float j = -0.5; + Q target = 1.5; + Q qi = i; + Q qj = j; + Q qres = qi - qj; + REQUIRE(qres==target); + } +} + +TEST_CASE("Multiplication") +{ + SECTION("with positive numbers") + { + int i = 2; + float j = 2.5; + Q target = 5; + Q qi = i; + Q qj = j; + Q qres = qi * qj; + REQUIRE(qres==target); + } + SECTION("with negative numbers") + { + int i = -2; + float j = -2.5; + Q target = 5; + Q qi = i; + Q qj = j; + Q qres = qi * qj; + REQUIRE(qres==target); + } + SECTION("with mixed numbers") + { + int i = -2; + float j = 2.5; + Q target = -5; + Q qi = i; + Q qj = j; + Q qres = qi * qj; + REQUIRE(qres==target); + } +} + +TEST_CASE("Division") +{ + SECTION("with positive numbers") + { + int i = 10; + float j = 2; + Q target = 5; + Q qi = i; + Q qj = j; + Q qres = qi / qj; + REQUIRE(qres==target); + } + SECTION("with negative numbers") + { + int i = -10; + float j = -5; + Q target = 2; + Q qi = i; + Q qj = j; + Q qres = qi / qj; + REQUIRE(qres==target); + } + SECTION("with mixed numbers") + { + int i = -18; + float j = 3; + Q target = -6; + Q qi = i; + Q qj = j; + Q qres = qi / qj; + REQUIRE(qres==target); + } +} \ No newline at end of file