/** * yatescript.h * Yet Another (Java)script library * This file is part of the YATE Project http://YATE.null.ro * * Yet Another Telephony Engine - a fully featured software PBX and IVR * Copyright (C) 2011-2014 Null Team * * This software is distributed under multiple licenses; * see the COPYING file in the main directory for licensing * information for this specific distribution. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ #ifndef __YATESCRIPT_H #define __YATESCRIPT_H #include #ifdef _WINDOWS #ifdef LIBYSCRIPT_EXPORTS #define YSCRIPT_API __declspec(dllexport) #else #ifndef LIBYSCRIPT_STATIC #define YSCRIPT_API __declspec(dllimport) #endif #endif #endif /* _WINDOWS */ #ifndef YSCRIPT_API #define YSCRIPT_API #endif /** * Holds all Telephony Engine related classes. */ namespace TelEngine { class ExpEvaluator; class ExpOperation; class ScriptMutex; /** * This class holds a JSON Pointer as specified in RFC 6901 * @short JSON path */ class YSCRIPT_API JPath : public String { YCLASS(JPath,String) public: /** * Constructor * @param value Initial value */ JPath(const char* value = 0); /** * Copy constructor * @param other Object to copy */ JPath(const JPath& other); /** * Destructor */ ~JPath(); /** * Check if path is valid * @return True if valid, false otherwise */ inline bool valid() const { return m_data || !c_str(); } /** * Retrieve the number of items in path * @return The number of items in path */ inline unsigned int count() const { return m_count; } /** * Retrieve path item at a index * @param idx Index to retrieve * @return Path item at requested index, empty string if not found */ inline const String& at(unsigned int idx) const { return idx < count() ? m_data[idx] : String::empty(); } /** * Retrieve path item at a index * @param idx Index to retrieve * @return Path item at requested index, empty string if not found */ inline const String& at(unsigned int idx) { return idx < count() ? m_data[idx] : String::empty(); } /** * Retrieve path item at a index * @param idx Index to retrieve * @return Path item at requested index, empty string if not found */ inline const String& operator[](unsigned int idx) const { return at(idx); } /** * Retrieve path item at a index * @param idx Index to retrieve * @return Path item at requested index, empty string if not found */ inline const String& operator[](unsigned int idx) { return at(idx); } /** * Add a path item to path * @param path Destination string * @param value Item value to add * @return Given 'path' string reference */ static inline String& addItem(String& path, const char* value) { if (!value) return path; String tmp; char* s = (char*)value; for (unsigned int i = 0; *s; ++i, ++s) { char c = escapeChar(*s); if (!c) continue; if (!tmp) tmp = value; tmp.insert(i,'~'); s = (char*)(tmp.c_str() + (++i)); *s = c; } return path << '/' << tmp.safe(value); } /** * Check if an item is a valid Array index * A valid array index is a decimal string with no leading/trailing spaces or leading 0 * @param str String to check * @return Array index, negative if invalid */ static inline int validArrayIndex(const String& str) { return str.toInteger(-1,10); } /** * Retrieve escape char * @param value Requested character * @return Escape character, 0 if given character doesn't need to be escaped */ static inline char escapeChar(char value) { if (value == '~') return '0'; if (value == '/') return '1'; return 0; } /** * Retrieve unescape char * @param value Requested character * @return Unescape character, 0 if given character is not an escaped one */ static inline char unescapeChar(char value) { if (value == '0') return '~'; if (value == '1') return '/'; return 0; } protected: /** * Called whenever the String value changed. * Reset data, parse the path */ virtual void changed(); /** * Parse the path */ void parse(); /** * Reset data */ inline void reset() { m_count = 0; if (m_data) { delete[] m_data; m_data = 0; } } String* m_data; unsigned int m_count; }; /** * This class allows extending ExpEvaluator to implement custom fields and functions * @short ExpEvaluator extending interface */ class YSCRIPT_API ExpExtender { public: /** * Destructor */ virtual ~ExpExtender() { } /** * Retrieve the reference counted object owning this interface * @return Pointer to object owning the extender, NULL if no ownership */ virtual RefObject* refObj(); /** * Check if a certain field is assigned in extender * @param stack Evaluation stack in use * @param name Name of the field to test * @param context Pointer to arbitrary object passed from evaluation methods * @return True if the field is present */ virtual bool hasField(ObjList& stack, const String& name, GenObject* context) const; /** * Get a pointer to a field in extender * @param stack Evaluation stack in use * @param name Name of the field to retrieve * @param context Pointer to arbitrary object passed from evaluation methods * @return Pointer to field, NULL if not present */ virtual NamedString* getField(ObjList& stack, const String& name, GenObject* context) const; /** * Try to evaluate a single function * @param stack Evaluation stack in use, parameters are popped off this stack * and results are pushed back on stack * @param oper Function to evaluate * @param context Pointer to arbitrary object passed from evaluation methods * @return True if evaluation succeeded */ virtual bool runFunction(ObjList& stack, const ExpOperation& oper, GenObject* context); /** * Try to evaluate a single field * @param stack Evaluation stack in use, field value must be pushed on it * @param oper Field to evaluate * @param context Pointer to arbitrary object passed from evaluation methods * @return True if evaluation succeeded */ virtual bool runField(ObjList& stack, const ExpOperation& oper, GenObject* context); /** * Try to assign a value to a single field * @param stack Evaluation stack in use * @param oper Field to assign to, contains the field name and new value * @param context Pointer to arbitrary object passed from evaluation methods * @return True if assignment succeeded */ virtual bool runAssign(ObjList& stack, const ExpOperation& oper, GenObject* context); }; /** * A class used to keep a parsing context * @short The parsing context */ class YSCRIPT_API ParsePoint { public: /** * Constructor * @param expr Expression to be parsed * @param eval ExpEvaluator associated with this parsing context * @param lineNo The line number that is currently parsed * @param fileName File name associated with this context * @param seps Searched separator during parsing */ explicit inline ParsePoint(const char*& expr, ExpEvaluator* eval = 0, unsigned int lineNo = 0, const char* fileName = 0, const char* seps = 0) : m_expr(expr), m_searchedSeps(seps), m_count(0), m_foundSep(0), m_lineNo(lineNo), m_eval(eval), m_fileName(fileName) { } /** * Assignment operator * @param parsePoint Parsing context which is to be assigned to this parsing context */ ParsePoint& operator=(ParsePoint& parsePoint); /** * Line number assignment * @param line New line number */ ParsePoint& operator=(unsigned int line); /** * Cast operator to const char*& */ inline operator const char*&() { return m_expr; } /** * Assignement from const char* */ inline ParsePoint& operator=(const char* newExpr) { m_expr = newExpr; return *this; } /** * Prefix incrementation operator. Incrementes the internal expression */ inline ParsePoint& operator++() // prefix { ++m_expr; return *this; } /** * Postfix incrementation operator. Incrementes the internal expression */ inline ParsePoint& operator++(int unused) // postfix { ++m_expr; return *this; } /** * Get first char in the parsed expression * @return First char in the expression to be parsed */ inline char firstChar() { return *m_expr; } /** * Get line number of the parsed expression * @return Line number stored in parse point */ inline unsigned lineNumber() const { return m_lineNo; } /** * Expression to be parsed */ const char* m_expr; /** * Searched instruction separators */ const char* m_searchedSeps; /** * Number of how many times the parser must encouter a separator */ unsigned int m_count; /** * Separator that the parser encountered */ char m_foundSep; /** * Line numbet at which parsing is taking place */ unsigned int m_lineNo; /** * ExpEvaluator associated with this parsing context */ ExpEvaluator* m_eval; /** * File name associated for this context */ String m_fileName; }; /** * A class used to build stack based (posifix) expression parsers and evaluators * @short An expression parser and evaluator */ class YSCRIPT_API ExpEvaluator : public DebugEnabler { friend class ParsePoint; public: /** * Parsing styles */ enum Parser { C, SQL, }; /** * Operation codes */ enum Opcode { // FORTH style notation of effect on stack, C-syntax expression OpcNone = 0,// ( --- ) OpcNull, // ( --- A) OpcPush, // ( --- A) OpcDrop, // (A --- ) OpcDup, // (A --- A A) OpcSwap, // (A B --- B A) OpcRot, // (A B C --- B C A) OpcOver, // (A B --- A B A) // Arithmetic operators OpcAdd, // (A B --- A+B) OpcSub, // (A B --- A-B) OpcMul, // (A B --- A*B) OpcDiv, // (A B --- A/B) OpcMod, // (A B --- A%B) OpcNeg, // (A --- -A) OpcIncPre, // (A --- ++A) OpcDecPre, // (A --- --A) OpcIncPost, // (A --- A++) OpcDecPost, // (A --- A--) // Bitwise logic operators OpcAnd, // (A B --- A&B) OpcOr, // (A B --- A|B) OpcXor, // (A B --- A^B) OpcNot, // (A --- ~A) OpcShl, // (A B --- A<>B) // Boolean logic operators OpcLAnd, // (A B --- A&&B) OpcLOr, // (A B --- A||B) OpcLXor, // (A B --- A^^B) OpcLNot, // (A --- !A) // String concatenation OpcCat, // (A B --- A.B) // String matching OpcReM, // (A B --- Amatch/B/) OpcReIM, // (A B --- Amatch_insensitive/B/) OpcReNm, // (A B --- A!match/B/) OpcReINm, // (A B --- A!match_insensitive/B/) OpcLike, // (A B --- AlikeB) OpcILike, // (A B --- Alike_insensitiveB) OpcNLike, // (A B --- A!likeB) OpcNIlike, // (A B --- A!like_insensitiveB) // Comparation operators OpcEq, // (A B --- A==B) OpcNe, // (A B --- A!=B) OpcGt, // (A B --- A>B) OpcLt, // (A B --- A=B) OpcLe, // (A B --- A<=B) // Ternary conditional operator OpcCond, // (A B C --- A?B:C) // Field naming operator OpcAs, // (A B --- A[name=B]) // Field replacement OpcField, // (A --- A) // Call of function with N parameters OpcFunc, // (... funcN --- func(...)) // Label for a jump OpcLabel, // ( --- ) // Push with deep copy OpcCopy, // ( --- CopiedA) // Nullish coalescing OpcNullish, // (A B --- A??B) // Field assignment - can be ORed with other binary operators OpcAssign = 0x0100, // (A B --- B,(&A=B)) // Private extension area for derived classes OpcPrivate = 0x1000 }; /** * Constructs an evaluator from an operator dictionary * @param operators Pointer to operator dictionary, longest strings first * @param unaryOps Pointer to unary operators dictionary, longest strings first */ explicit ExpEvaluator(const TokenDict* operators = 0, const TokenDict* unaryOps = 0); /** * Constructs an evaluator from a parser style * @param style Style of parsing to use */ explicit ExpEvaluator(Parser style); /** * Copy constructor * @param original Evaluator to copy the operation list from */ ExpEvaluator(const ExpEvaluator& original); /** * Destructor */ virtual ~ExpEvaluator(); /** * Parse and compile an expression * @param expr Parsing context to compile * @param context Pointer to arbitrary object to be passed to called methods * @return Number of expressions compiled, zero on error */ int compile(ParsePoint& expr, GenObject* context = 0); /** * Evaluate the expression, optionally return results * @param results List to fill with results row * @param context Pointer to arbitrary object to be passed to called methods * @return True if expression evaluation succeeded, false on failure */ bool evaluate(ObjList* results, GenObject* context = 0) const; /** * Evaluate the expression, return computed results * @param results List to fill with results row * @param context Pointer to arbitrary object to be passed to called methods * @return True if expression evaluation succeeded, false on failure */ inline bool evaluate(ObjList& results, GenObject* context = 0) const { return evaluate(&results,context); } /** * Evaluate the expression, return computed results * @param results List of parameters to populate with results row * @param index Index of result row, zero to not include an index * @param prefix Prefix to prepend to parameter names * @param context Pointer to arbitrary object to be passed to called methods * @return Number of result columns, -1 on failure */ int evaluate(NamedList& results, unsigned int index = 0, const char* prefix = 0, GenObject* context = 0) const; /** * Evaluate the expression, return computed results * @param results Array of result rows to populate * @param index Index of result row, zero to just set column headers * @param context Pointer to arbitrary object to be passed to called methods * @return Number of result columns, -1 on failure */ int evaluate(Array& results, unsigned int index, GenObject* context = 0) const; /** * Simplify the expression, performs constant folding * @return True if the expression was simplified */ inline bool simplify() { return trySimplify(); } /** * Check if a parse or compile error was encountered * @return True if the evaluator encountered an error */ inline bool inError() const { return m_inError; } /** * Retrieve the number of line currently being parsed * @return Number of current parsed line, 1 is the first line */ inline unsigned int lineNumber() const { return m_lineNo; } /** * Check if the expression is empty (no operands or operators) * @return True if the expression is completely empty */ virtual bool null() const; /** * Dump a list of operations according to current operators dictionary * @param codes List of operation codes * @param res Result string representation of operations * @param lineNo True to include line numbers */ void dump(const ObjList& codes, String& res, bool lineNo = false) const; /** * Dump the postfix expression according to current operators dictionary * @param res Result string representation of operations * @param lineNo True to include line numbers */ virtual void dump(String& res, bool lineNo = false) const; /** * Dump a list of operations according to current operators dictionary * @param codes List of operation codes * @param lineNo True to include line numbers * @return String representation of operations */ inline String dump(const ObjList& codes, bool lineNo = false) const { String s; dump(codes,s,lineNo); return s; } /** * Dump the postfix expression according to current operators dictionary * @param lineNo True to include line numbers * @return String representation of operations */ inline String dump(bool lineNo = false) const { String s; dump(s,lineNo); return s; } /** * Retrieve the internally used operator dictionary * @return Pointer to operators dictionary in use */ inline const TokenDict* operators() const { return m_operators; } /** * Retrieve the internally used unary operators dictionary * @return Pointer to unary operators dictionary in use */ inline const TokenDict* unaryOps() const { return m_unaryOps; } /** * Retrieve the internally used expression extender * @return Pointer to the extender in use, NULL if none */ inline ExpExtender* extender() const { return m_extender; } /** * Set the expression extender to use in evaluation * @param ext Pointer to the extender to use, NULL to remove current */ void extender(ExpExtender* ext); /** * Retrieve the line number from one to three operands * @param op1 First operand * @param op2 Optional second operand * @param op3 Optional third operand * @return Line number at compile time, zero if not found */ static unsigned int getLineOf(ExpOperation* op1, ExpOperation* op2 = 0, ExpOperation* op3 = 0); /** * Push an operand on an evaluation stack * @param stack Evaluation stack to remove the operand from * @param oper Operation to push on stack, NULL will not be pushed */ static void pushOne(ObjList& stack, ExpOperation* oper); /** * Pops an operand off an evaluation stack, does not pop a barrier * @param stack Evaluation stack to remove the operand from * @return Operator removed from stack, NULL if stack underflow */ static ExpOperation* popOne(ObjList& stack); /** * Pops any operand (including barriers) off an evaluation stack * @param stack Evaluation stack to remove the operand from * @return Operator removed from stack, NULL if stack underflow */ static ExpOperation* popAny(ObjList& stack); /** * Pops and evaluate the value of an operand off an evaluation stack, does not pop a barrier * @param stack Evaluation stack to remove the operand from * @param context Pointer to arbitrary object to be passed to called methods * @return Value removed from stack, NULL if stack underflow or field not evaluable */ virtual ExpOperation* popValue(ObjList& stack, GenObject* context = 0) const; /** * Try to evaluate a single operation * @param stack Evaluation stack in use, operands are popped off this stack * and results are pushed back on stack * @param oper Operation to execute * @param context Pointer to arbitrary object to be passed to called methods * @return True if evaluation succeeded */ virtual bool runOperation(ObjList& stack, const ExpOperation& oper, GenObject* context = 0) const; /** * Convert all fields on the evaluation stack to their values * @param stack Evaluation stack to evaluate fields from * @param context Pointer to arbitrary object to be passed to called methods * @return True if all fields on the stack were evaluated properly */ virtual bool runAllFields(ObjList& stack, GenObject* context = 0) const; protected: /** * Method to skip over whitespaces, count parsed lines too * @param expr Current parsing context, advances on expression to be compiled * @return First character after whitespaces where expr points */ virtual char skipWhites(ParsePoint& expr); /** * Helper method to conditionally convert to lower case * @param chr Character to convert * @param makeLower True to convert chr to lower case * @return Converted character or original if conversion not requested */ inline static char condLower(char chr, bool makeLower) { return (makeLower && ('A' <= chr) && (chr <= 'Z')) ? (chr + ('a' - 'A')) : chr; } /** * Helper method to return next operator in the parsed text * @param expr Pointer to text to parse, gets advanced if succeeds * @param operators Pointer to operators table to use * @param caseInsensitive Match case-insensitive if set * @return Operator code, OpcNone on failure */ Opcode getOperator(const char*& expr, const TokenDict* operators, bool caseInsensitive = false) const; /** * Check if a character can be a letter character in a keyword or identifier * @param c Character to check * @return True if the character can be part of a keyword or identifier */ virtual bool keywordLetter(char c) const; /** * Check if a character can be can be a digit character in a keyword or identifier * @param c Character to check * @return True if the character can be part of a keyword or identifier */ virtual bool keywordDigit(char c) const; /** * Check if a character can be part of a keyword or identifier * @param c Character to check * @return True if the character can be part of a keyword or identifier */ virtual bool keywordChar(char c) const; /** * Helper method to count characters making a keyword * @param str Pointer to text without whitespaces in front * @return Length of the keyword, 0 if a valid keyword doesn't follow */ virtual int getKeyword(const char* str) const; /** * Helper method to display debugging errors internally * @param error Text of the error * @param text Optional text that caused the error * @param line Number of line generating the error, zero for parsing errors * @return Always returns false */ bool gotError(const char* error = 0, const char* text = 0, unsigned int line = 0) const; /** * Helper method to set error flag and display debugging errors internally * @param error Text of the error * @param text Optional text that caused the error * @param line Number of line generating the error, zero for parsing errors * @return Always returns false */ bool gotError(const char* error = 0, const char* text = 0, unsigned int line = 0); /** * Helper method to display debugging errors internally * @param error Text of the error * @param line Number of line generating the error, zero for parsing errors * @return Always returns false */ inline bool gotError(const char* error, unsigned int line) const { return gotError(error, 0, line); } /** * Helper method to set error flag and display debugging errors internally * @param error Text of the error * @param line Number of line generating the error, zero for parsing errors * @return Always returns false */ inline bool gotError(const char* error, unsigned int line) { return gotError(error, 0, line); } /** * Formats a line number to display in error messages * @param buf String buffer used to return the value * @param line Line number to format */ virtual void formatLineNo(String& buf, unsigned int line) const; /** * Runs the parser and compiler for one (sub)expression * @param expr Current parsing context, advances on expression to be compiled * @param stop Optional character expected after the expression * @param nested User defined object to pass for nested parsing * @return True if one expression was compiled and a separator follows */ bool runCompile(ParsePoint& expr, char stop, GenObject* nested = 0); /** * Runs the parser and compiler for one (sub)expression * @param expr Current parsing context, advances on expression to be compiled * @param stop Optional list of possible characters expected after the expression * @param nested User defined object to pass for nested parsing * @return True if one expression was compiled and a separator follows */ virtual bool runCompile(ParsePoint& expr, const char* stop = 0, GenObject* nested = 0); /** * Skip over comments and whitespaces * @param expr Current parsing context, advances on expression to be compiled * @param context Pointer to arbitrary object to be passed to called methods * @return First character after comments or whitespaces where expr points */ virtual char skipComments(ParsePoint& expr, GenObject* context = 0); /** * Process top-level preprocessor directives * @param expr Current parsing context, advances on expression to be compiled * @param context Pointer to arbitrary object to be passed to called methods * @return Number of expressions compiled, negative if no more directives */ virtual int preProcess(ParsePoint& expr, GenObject* context = 0); /** * Returns next operator in the parsed text * @param expr Current parsing context, advances on expression to be compiled if it succeeds * @return Operator code, OpcNone on failure */ virtual Opcode getOperator(ParsePoint& expr); /** * Returns next unary operator in the parsed text * @param expr Current parsing context, advances on expression to be compiled if it succeeds * @return Operator code, OpcNone on failure */ virtual Opcode getUnaryOperator(ParsePoint& expr); /** * Returns next unary postfix operator in the parsed text * @param expr Current parsing context, advances on expression to be compiled if it succeeds * @param precedence The precedence of the previous operator * @return Operator code, OpcNone on failure */ virtual Opcode getPostfixOperator(ParsePoint& expr, int precedence = 0); /** * Helper method to get the canonical name of an operator * @param oper Operator code * @return name of the operator, NULL if it doesn't have one */ virtual const char* getOperator(Opcode oper) const; /** * Get the precedence of an operator * @param oper Operator code * @return Precedence of the operator, zero (lowest) if unknown */ virtual int getPrecedence(Opcode oper) const; /** * Get the associativity of an operator * @param oper Operator code * @return True if the operator is right-to-left associative, false if left-to-right */ virtual bool getRightAssoc(Opcode oper) const; /** * Check if we are at an expression separator and optionally skip past it * @param expr Current parsing context to check, advances on expression to be compiled if asked to remove separator * @param remove True to skip past the found separator * @return True if a separator was found */ virtual bool getSeparator(ParsePoint& expr, bool remove); /** * Get an instruction or block, advance parsing pointer past it * @param expr Current parsing context, advances on expression to be compiled if it succeeds * @param stop Optional character expected after the instruction * @param nested User defined object passed from nested parsing * @return True if succeeded, must add the operands internally */ virtual bool getInstruction(ParsePoint& expr, char stop = 0, GenObject* nested = 0); /** * Get an operand, advance parsing pointer past it * @param expr Current parsing context, advances on expression to be compiled if it succeeds * @param endOk Consider reaching the end of string a success * @param precedence The precedence of the previous operator * @return True if succeeded, must add the operand internally */ virtual bool getOperand(ParsePoint& expr, bool endOk = true, int precedence = 0); /** * Get an inline simple type, usually string or number * @param expr Current parsing context, advances on expression to be compiled if it succeeds * @param constOnly Return only inline constants * @return True if succeeded, must add the operand internally */ virtual bool getSimple(ParsePoint& expr, bool constOnly = false); /** * Get a numerical operand, advance parsing pointer past it * @param expr Current parsing context, advances on expression to be compiled if it succeeds * @return True if succeeded, must add the operand internally */ virtual bool getNumber(ParsePoint& expr); /** * Get a string operand, advance parsing pointer past it * @param expr Current parsing context, advances on expression to be compiled if it succeeds * @return True if succeeded, must add the operand internally */ virtual bool getString(ParsePoint& expr); /** * Get a function call, advance parsing pointer past it * @param expr Current parsing context, advances on expression to be compiled if it succeeds * @return True if succeeded, must add the operand internally */ virtual bool getFunction(ParsePoint& expr); /** * Helper method - get a string, advance parsing pointer past it * @param expr Pointer to string separator, gets advanced on success * @param str String in which the result is returned * @return True if succeeded */ virtual bool getString(const char*& expr, String& str); /** * Helper method - get an escaped component of a string * @param expr Pointer past escape character, gets advanced on success * @param str String in which the result is returned * @param sep String separator character * @return True if succeeded */ virtual bool getEscape(const char*& expr, String& str, char sep); /** * Get a field keyword, advance parsing pointer past it * @param expr Current parsing context, advances on expression to be compiled if it succeeds * @return True if succeeded, must add the operand internally */ virtual bool getField(ParsePoint& expr); /** * Add an aready built operation to the expression and set its line number * @param oper Operation to add * @param line Line number where operation was compiled, zero to used parsing point */ void addOpcode(ExpOperation* oper, unsigned int line = 0); /** * Add a simple operator to the expression * @param oper Operator code to add * @param barrier True to create an evaluator stack barrier * @param line Line number where operation was compiled, zero to used parsing point * @return Newly added operation */ ExpOperation* addOpcode(Opcode oper, bool barrier = false, unsigned int line = 0); /** * Add a simple operator to the expression * @param oper Operator code to add * @param value 64 bit nteger value to add * @param barrier True to create an evaluator stack barrier * @return Newly added operation */ ExpOperation* addOpcode(Opcode oper, int64_t value, bool barrier = false); /** * Add a string constant to the expression * @param value String value to add, will be pushed on execution * @return Newly added operation */ ExpOperation* addOpcode(const String& value); /** * Add an integer constant to the expression * @param value Integer value to add, will be pushed on execution * @return Newly added operation */ ExpOperation* addOpcode(int64_t value); /** * Add a boolean constant to the expression * @param value Boolean value to add, will be pushed on execution * @return Newly added operation */ ExpOperation* addOpcode(bool value); /** * Add a function or field to the expression * @param oper Operator code to add, must be OpcField or OpcFunc * @param name Name of the field or function, case sensitive * @param value Numerical value used as parameter count to functions * @param barrier True to create an exavuator stack barrier * @param line Line number where operation was compiled, zero to used parsing point * @return Newly added operation */ ExpOperation* addOpcode(Opcode oper, const String& name, int64_t value = 0, bool barrier = false, unsigned int line = 0); /** * Remove from the code and return the last operation * @return Operation removed from end of code, NULL if no operations remaining */ ExpOperation* popOpcode(); /** * Try to apply simplification to the expression * @return True if the expression was simplified */ virtual bool trySimplify(); /** * Try to evaluate a list of operation codes * @param opcodes List of operation codes to evaluate * @param stack Evaluation stack in use, results are left on stack * @param context Pointer to arbitrary object to be passed to called methods * @return True if evaluation succeeded */ virtual bool runEvaluate(const ObjList& opcodes, ObjList& stack, GenObject* context = 0) const; /** * Try to evaluate a vector of operation codes * @param opcodes ObjVector of operation codes to evaluate * @param stack Evaluation stack in use, results are left on stack * @param context Pointer to arbitrary object to be passed to called methods * @param index Index in operation codes to start evaluation from * @return True if evaluation succeeded */ virtual bool runEvaluate(const ObjVector& opcodes, ObjList& stack, GenObject* context = 0, unsigned int index = 0) const; /** * Try to evaluate the expression * @param stack Evaluation stack in use, results are left on stack * @param context Pointer to arbitrary object to be passed to called methods * @return True if evaluation succeeded */ virtual bool runEvaluate(ObjList& stack, GenObject* context = 0) const; /** * Try to evaluate a single function * @param stack Evaluation stack in use, parameters are popped off this stack * and results are pushed back on stack * @param oper Function to evaluate * @param context Pointer to arbitrary object to be passed to called methods * @return True if evaluation succeeded */ virtual bool runFunction(ObjList& stack, const ExpOperation& oper, GenObject* context = 0) const; /** * Try to evaluate a single field * @param stack Evaluation stack in use, field value must be pushed on it * @param oper Field to evaluate * @param context Pointer to arbitrary object to be passed to called methods * @return True if evaluation succeeded */ virtual bool runField(ObjList& stack, const ExpOperation& oper, GenObject* context = 0) const; /** * Try to assign a value to a single field * @param stack Evaluation stack in use * @param oper Field to assign to, contains the field name and new value * @param context Pointer to arbitrary object to be passed to called methods * @return True if assignment succeeded */ virtual bool runAssign(ObjList& stack, const ExpOperation& oper, GenObject* context = 0) const; /** * Dump a single operation according to current operators dictionary * @param oper Operation to dump * @param res Result string representation of operations * @param lineNo True to include line numbers */ virtual void dump(const ExpOperation& oper, String& res, bool lineNo = false) const; /** * Internally used operator dictionary */ const TokenDict* m_operators; /** * Internally used unary operators dictionary */ const TokenDict* m_unaryOps; /** * Internally used list of operands and operator codes */ ObjList m_opcodes; /** * Internally used for faster appending to the operator codes list */ ObjList* m_lastOpcode; /** * Flag that we encountered a parse or compile error */ bool m_inError; /** * Current line index */ unsigned int m_lineNo; private: bool getOperandInternal(ParsePoint& expr, bool endOk, int precedence); ExpExtender* m_extender; }; /** * This class describes a single operation in an expression evaluator * @short A single operation in an expression */ class YSCRIPT_API ExpOperation : public NamedString { friend class ExpEvaluator; YCLASS(ExpOperation,NamedString) public: /** * Special value that is not recognized as an integer value * @return A value that indicates a non-integer value */ inline static int64_t nonInteger() { return LLONG_MIN; } /** * Copy constructor * @param original Operation to copy */ inline ExpOperation(const ExpOperation& original) : NamedString(original.name(),original), m_opcode(original.opcode()), m_number(original.number()), m_bool(original.isBoolean()), m_isNumber(original.isNumber()), m_lineNo(original.lineNumber()), m_barrier(original.barrier()) { } /** * Copy constructor with renaming, to be used for named results * @param original Operation to copy * @param name Name of the newly created operation * @param copyType True to copy operation type, false to create an OpcPush */ inline ExpOperation(const ExpOperation& original, const char* name, bool copyType = true) : NamedString(name,original), m_opcode(copyType ? original.opcode() : ExpEvaluator::OpcPush), m_number(original.number()), m_bool(original.isBoolean()), m_isNumber(original.isNumber()), m_lineNo(original.lineNumber()), m_barrier(original.barrier()) { } /** * Push String constructor * @param value String constant to push on stack on execution * @param name Optional of the newly created constant * @param autoNum Automatically convert to number if possible */ inline explicit ExpOperation(const String& value, const char* name = 0, bool autoNum = false) : NamedString(name,value), m_opcode(ExpEvaluator::OpcPush), m_number(autoNum ? value.toInt64(nonInteger()) : nonInteger()), m_bool(autoNum && value.isBoolean()), m_isNumber(autoNum && (value == YSTRING("NaN") || m_number != nonInteger())), m_lineNo(0), m_barrier(false) { if (m_bool) { m_number = value.toBoolean() ? 1 : 0; m_isNumber = true;} } /** * Push literal string constructor * @param value String constant to push on stack on execution * @param name Optional of the newly created constant */ inline explicit ExpOperation(const char* value, const char* name = 0) : NamedString(name,value), m_opcode(ExpEvaluator::OpcPush), m_number(nonInteger()), m_bool(false), m_isNumber(false), m_lineNo(0), m_barrier(false) { } /** * Push 64 bit Number constructor * @param value Integer constant to push on stack on execution * @param name Optional of the newly created constant */ inline explicit ExpOperation(int64_t value, const char* name = 0) : NamedString(name,"NaN"), m_opcode(ExpEvaluator::OpcPush), m_number(value), m_bool(false), m_isNumber(true), m_lineNo(0), m_barrier(false) { if (value != nonInteger()) String::operator=(value); } /** * Push Boolean constructor * @param value Boolean constant to push on stack on execution * @param name Optional of the newly created constant */ inline explicit ExpOperation(bool value, const char* name = 0) : NamedString(name,String::boolText(value)), m_opcode(ExpEvaluator::OpcPush), m_number(value ? 1 : 0), m_bool(true), m_isNumber(true), m_lineNo(0), m_barrier(false) { } /** * Constructor from components * @param oper Operation code * @param name Optional name of the operation or result * @param value Optional integer constant used as function parameter count * @param barrier True if the operation is an expression barrier on the stack */ inline ExpOperation(ExpEvaluator::Opcode oper, const char* name = 0, int64_t value = nonInteger(), bool barrier = false) : NamedString(name,""), m_opcode(oper), m_number(value), m_bool(false), m_isNumber(false), m_lineNo(0), m_barrier(barrier) { } /** * Constructor of non-integer operation from components * @param oper Operation code * @param name Name of the operation or result * @param value String value of operation * @param barrier True if the operation is an expression barrier on the stack */ inline ExpOperation(ExpEvaluator::Opcode oper, const char* name, const char* value, bool barrier = false) : NamedString(name,value), m_opcode(oper), m_number(nonInteger()), m_bool(false), m_isNumber(false), m_lineNo(0), m_barrier(barrier) { } /** * Constructor from components * @param oper Operation code * @param name Optional name of the operation or result * @param value String value of operation * @param number Integer value * @param barrier True if the operation is an expression barrier on the stack */ inline ExpOperation(ExpEvaluator::Opcode oper, const char* name, const char* value, int64_t number, bool barrier) : NamedString(name,value), m_opcode(oper), m_number(number), m_bool(false), m_isNumber(true), m_lineNo(0), m_barrier(barrier) { } /** * Retrieve the code of this operation * @return Operation code as declared in the expression evaluator */ inline ExpEvaluator::Opcode opcode() const { return m_opcode; } /** * Check if an integer value is stored * @return True if an integer value is stored */ inline bool isInteger() const { return m_number != nonInteger(); } /** * Retrieve the number stored in this operation * @return Stored number */ inline int64_t number() const { return m_number; } /** * Check if a boolean value is stored * @return True if a boolean value is stored */ inline bool isBoolean() const { return m_bool; } /** * Check if a number type value is stored * @return True if a number type value is stored */ inline bool isNumber() const { return m_isNumber; } /** * Check if this operation acts as an evaluator barrier on the stack * @return True if an expression should not pop this operation off the stack */ inline bool barrier() const { return m_barrier; } /** * Retrieve the line number where the operation was compiled from * @return Line number, zero if unknown */ inline unsigned int lineNumber() const { return m_lineNo; } /** * Set the line number where the operation was compiled from * @param line Number of the compiled line */ inline void lineNumber(unsigned int line) { m_lineNo = line; } /** * Number assignment operator * @param num Numeric value to assign to the operation * @return Assigned number */ inline int64_t operator=(int64_t num) { m_number = num; String::operator=(num); m_isNumber = true; return num; } /** * Retrieve the numeric value of the operation * @param defVal Default to return if not a number * @return Number contained in operation */ virtual int64_t valInteger(int64_t defVal = 0) const; /** * Convert to number * @return Value converted to number, NaN if not possible of if stored value is NaN */ virtual int64_t toNumber() const; /** * Retrieve the boolean value of the operation * @param defVal Default to return if not a boolean * @return True if the operation is to be interpreted as true value */ virtual bool valBoolean(bool defVal = false) const; /** * Retrieve the name of the type of the value of this operation * @return Name of the type of the value */ virtual const char* typeOf() const; /** * Clone and rename method * @param name Name of the cloned operation * @return New operation instance */ virtual ExpOperation* clone(const char* name) const; /** * Clone method * @return New operation instance, may keep a reference to the old instance */ inline ExpOperation* clone() const { return clone(name()); } /** * Deep copy method * @param mtx Pointer to the mutex that serializes the copied object * @return New operation instance */ virtual ExpOperation* copy(ScriptMutex* mtx) const { return clone(); } private: ExpEvaluator::Opcode m_opcode; int64_t m_number; bool m_bool; bool m_isNumber; unsigned int m_lineNo; bool m_barrier; }; /** * Small helper class that simplifies declaring native functions * @short Helper class to declare a native function */ class YSCRIPT_API ExpFunction : public ExpOperation { YCLASS(ExpFunction,ExpOperation) public: /** * Constructor * @param name Name of the function * @param argc Number of arguments expected by function * @param barrier True if the function is an expression barrier on the stack */ inline ExpFunction(const char* name, long int argc = 0, bool barrier = false) : ExpOperation(ExpEvaluator::OpcFunc,name,argc,barrier) { if (name) (*this) << "[function " << name << "()]"; } /** * Retrieve the boolean value of the function (not of its result) * @param defVal Parameter ignored * @return Always true */ virtual bool valBoolean(bool defVal = false) const { return true; } /** * Clone and rename method * @param name Name of the cloned operation * @return New operation instance */ virtual ExpOperation* clone(const char* name) const; }; /** * Helper class that allows wrapping entire objects in an operation * @short Object wrapper for evaluation */ class YSCRIPT_API ExpWrapper : public ExpOperation { public: /** * Constructor * @param object Pointer to the object to wrap * @param name Optional name of the wrapper * @param barrier True if the operation is an expression barrier on the stack */ inline ExpWrapper(GenObject* object, const char* name = 0, bool barrier = false) : ExpOperation(ExpEvaluator::OpcPush,name, object ? object->toString().c_str() : (const char*)0,barrier), m_object(object) { } /** * Constructor with special operation * @param opcode Operation code of the wrapper * @param object Pointer to the object to wrap * @param name Optional name of the wrapper */ inline ExpWrapper(ExpEvaluator::Opcode opcode, GenObject* object, const char* name = 0) : ExpOperation(opcode,name,object ? object->toString().c_str() : (const char*)0,false), m_object(object) { } /** * Destructor, deletes the held object */ virtual ~ExpWrapper() { TelEngine::destruct(m_object); } /** * Get a pointer to a derived class given that class name * @param name Name of the class we are asking for * @return Pointer to the requested class or NULL if this object doesn't implement it */ virtual void* getObject(const String& name) const; /** * Retrieve the boolean value of the operation * @param defVal Parameter ignored * @return True if the wrapped object is to be interpreted as true value */ virtual bool valBoolean(bool defVal = false) const; /** * Retrieve the name of the type of the value of this operation * @return Name of the type of the value */ virtual const char* typeOf() const; /** * Clone and rename method * @param name Name of the cloned operation * @return New operation instance */ virtual ExpOperation* clone(const char* name) const; /** * Deep copy method * @param mtx Pointer to the mutex that serializes the copied object * @return New operation instance */ virtual ExpOperation* copy(ScriptMutex* mtx) const; /** * Object access method * @return Pointer to the held object */ GenObject* object() const { return m_object; } /** * Replace held object if given object is not NULL and different from held one * @param gen Pointer to the new held object */ inline void setObject(GenObject* gen) { if (!gen || gen == m_object) return; TelEngine::destruct(m_object); m_object = gen; } private: GenObject* m_object; }; /** * An evaluator for multi-row (tables like in SQL) expressions * @short An SQL-like table evaluator */ class YSCRIPT_API TableEvaluator { public: /** * Copy constructor, duplicates current state of original * @param original Evaluator to copy */ TableEvaluator(const TableEvaluator& original); /** * Constructor from a parser synatx style * @param style Style of evaluator to create */ TableEvaluator(ExpEvaluator::Parser style); /** * Constructor from operator description table * @param operators Pointer to operators synatx table * @param unaryOps Pointer to unary operators dictionary */ TableEvaluator(const TokenDict* operators, const TokenDict* unaryOps); /** * Destructor */ virtual ~TableEvaluator(); /** * Evaluate the WHERE (selector) expression * @param context Pointer to arbitrary object to be passed to called methods * @return True if the current row is part of selection */ virtual bool evalWhere(GenObject* context = 0); /** * Evaluate the SELECT (results) expression * @param results List to fill with results row * @param context Pointer to arbitrary object to be passed to called methods * @return True if evaluation succeeded */ virtual bool evalSelect(ObjList& results, GenObject* context = 0); /** * Evaluate the LIMIT expression and cache the result * @param context Pointer to arbitrary object to be passed to called methods * @return Desired maximum number or result rows */ virtual unsigned int evalLimit(GenObject* context = 0); /** * Set the expression extender to use in all evaluators * @param ext Pointer to the extender to use, NULL to remove current */ void extender(ExpExtender* ext); protected: ExpEvaluator m_select; ExpEvaluator m_where; ExpEvaluator m_limit; unsigned int m_limitVal; }; class ScriptRun; /** * A mutex that serializes object access * @short Script context serialization mutex */ class YSCRIPT_API ScriptMutex : public Mutex { public: /** * Constructor * @param recursive True if the mutex has to be recursive (reentrant), * false for a normal fast mutex * @param name Static name of the mutex (for debugging purpose only) */ inline ScriptMutex(bool recursive, const char* name) : Mutex(recursive,name), m_objTrack(false) { } /** * Notification that an object was created in context serialized by this mutex * @param obj Created object */ virtual void objCreated(GenObject* obj) = 0; /** * Notification that an object was destroyed in context serialized by this mutex * @param obj Destroyed object */ virtual void objDeleted(GenObject* obj) = 0; /** * Check if object tracking is active * @return True if it's active, false otherwise */ inline bool objTrack() const { return m_objTrack; } protected: bool m_objTrack; }; /** * A script execution context, holds global variables and objects * @short Script execution context */ class YSCRIPT_API ScriptContext : public RefObject, public ExpExtender { public: /** * Constructor * @param name Name of the context */ inline explicit ScriptContext(const char* name = 0) : m_params(name), m_instIdx(0), m_instCount(1) { } /** * Access to the NamedList operator * @return Reference to the internal named list */ inline NamedList& params() { return m_params; } /** * Const access to the NamedList operator * @return Reference to the internal named list */ inline const NamedList& params() const { return m_params; } /** * Access any native NamedList hold by the context * @return Pointer to a native named list */ virtual NamedList* nativeParams() const { return 0; } /** * Override GenObject's method to return the internal name of the named list * @return A reference to the context name */ virtual const String& toString() const { return m_params; } /** * Get a pointer to a derived class given that class name * @param name Name of the class we are asking for * @return Pointer to the requested class or NULL if this object doesn't implement it */ virtual void* getObject(const String& name) const; /** * Retrieve the reference counted object owning this interface * @return Pointer to this script context */ virtual RefObject* refObj() { return this; } /** * Retrieve the Mutex object used to serialize object access, if any * @return Pointer to the mutex or NULL if none applies */ virtual ScriptMutex* mutex() = 0; /** * Check if a certain field is assigned in context * @param stack Evaluation stack in use * @param name Name of the field to test * @param context Pointer to arbitrary object passed from evaluation methods * @return True if the field is present */ virtual bool hasField(ObjList& stack, const String& name, GenObject* context) const; /** * Get a pointer to a field in the context * @param stack Evaluation stack in use * @param name Name of the field to retrieve * @param context Pointer to arbitrary object passed from evaluation methods * @return Pointer to field, NULL if not present */ virtual NamedString* getField(ObjList& stack, const String& name, GenObject* context) const; /** * Fill a list with the unique names of all fields * @param names List to which key names must be added */ virtual void fillFieldNames(ObjList& names); /** * Fill a list with the unique names of all fields * @param names List to which key names must be added * @param list List of parameters whose names to be added * @param checkDupl True to ignore duplicates from the given list * @param skip Parameters starting with this prefix will not be added */ static void fillFieldNames(ObjList& names, const NamedList& list, bool checkDupl = true, const char* skip = 0); /** * Fill a list with the unique names from a Hash list * @param names List to which key names must be added * @param list Hash list whose names are to be added */ static void fillFieldNames(ObjList& names, const HashList& list); /** * Try to evaluate a single function in the context * @param stack Evaluation stack in use, parameters are popped off this stack and results are pushed back on stack * @param oper Function to evaluate * @param context Pointer to context data passed from evaluation methods * @return True if evaluation succeeded */ virtual bool runFunction(ObjList& stack, const ExpOperation& oper, GenObject* context); /** * Try to evaluate a single field in the context * @param stack Evaluation stack in use, field value must be pushed on it * @param oper Field to evaluate * @param context Pointer to context data passed from evaluation methods * @return True if evaluation succeeded */ virtual bool runField(ObjList& stack, const ExpOperation& oper, GenObject* context); /** * Try to assign a value to a single field * @param stack Evaluation stack in use * @param oper Field to assign to, contains the field name and new value * @param context Pointer to context data passed from evaluation methods * @return True if assignment succeeded */ virtual bool runAssign(ObjList& stack, const ExpOperation& oper, GenObject* context); /** * Copy all fields from another context * @param stack Evaluation stack in use * @param original Script context to copy from * @param context Pointer to context data passed from evaluation methods * @return True if all fields were copied */ virtual bool copyFields(ObjList& stack, const ScriptContext& original, GenObject* context); /** * Add string parameters from list * @param list Parameters list * @param skipPrefix Skip parameters whose name start with specified prefix */ virtual void addFields(const NamedList& list, const char* skipPrefix = "__"); /** * Try to evaluate a single field searching for a matching context * @param stack Evaluation stack in use, field value must be pushed on it * @param oper Field to evaluate * @param context Pointer to context data passed from evaluation methods * @return True if evaluation succeeded */ bool runMatchingField(ObjList& stack, const ExpOperation& oper, GenObject* context); /** * Notification that an object was created in this context * Used for object tracking purposes * @param obj The created object */ virtual void createdObj(GenObject* obj) { } /** * Notification that an object was destroyed in this context * Used for object tracking purposes * @param obj The destroyed object */ virtual void deletedObj(GenObject* obj) { } /** * Activate object tracking * @param track O for not enabled, non-zero for enabling it */ virtual void trackObjs(unsigned int track = 0) { } /** * Retrieve a list of how many objects were allocated at each line * @return The list of counters for allocations, 0 if tracking is not active */ virtual ObjList* countAllocations() { return 0; } virtual void setInstance(unsigned int idx, unsigned int count) { m_instIdx = idx; m_instCount = count; } virtual unsigned int instanceIndex() const { return m_instIdx; } virtual unsigned int instanceCount() const { return m_instCount; } private: NamedList m_params; unsigned int m_instIdx; // instance index unsigned int m_instCount; // total number of instances }; /** * Preparsed script code fragment ready to be executed * @short Script parsed code */ class YSCRIPT_API ScriptCode : public RefObject { YCLASS(ScriptCode,RefObject) public: /** * Context initializer for language specific globals * @param context Pointer to the context to initialize * @return True if context was properly populated with globals */ virtual bool initialize(ScriptContext* context) const = 0; /** * Evaluation of a single code expression * @param runner Reference to the runtime to use in evaluation * @param results List to fill with expression results */ virtual bool evaluate(ScriptRun& runner, ObjList& results) const = 0; /** * Create a runner adequate for this block of parsed code * @param context Script context, must not be NULL * @param title An optional name for the runner * @return A new script runner, NULL if context is NULL or feature is not supported */ virtual ScriptRun* createRunner(ScriptContext* context, const char* title = 0) { return 0; } /** * Get the file name and the file line from which this code line was interpreted * @param line Code line * @param fileName On output, it contains the file name associated with code line * @param fileLine On output, it contains the file line associated with code line * @param wholePath If true, file name contains the whole file path, otherwise just the filename */ virtual void getFileLine(unsigned int line, String& fileName, unsigned int& fileLine, bool wholePath = true) { } }; /** * A stack for a script running instance * @short Script runtime stack */ class YSCRIPT_API ScriptStack : public ObjList { YCLASS(ScriptStack,ObjList) YNOCOPY(ScriptStack); public: /** * Constructor * @param owner The script running instance that will own this stack */ ScriptStack(ScriptRun* owner) : m_runner(owner) { } /** * Retrieve the script running instance that owns this stack * @return Pointer to owner script instance */ inline ScriptRun* runner() { return m_runner; } private: ScriptRun* m_runner; }; /** * Operation that is to be executed by the script runtime before current operation * @short Asynchronous execution support */ class YSCRIPT_API ScriptAsync : public GenObject { YCLASS(ScriptAsync,GenObject) public: /** * Constructor * @param owner The script running instance that will own this operation */ ScriptAsync(ScriptRun* owner) : m_runner(owner) { } /** * Destructor */ virtual ~ScriptAsync() { } /** * Retrieve the script running instance that owns this stack * @return Pointer to owner script instance */ inline ScriptRun* runner() { return m_runner; } /** * Execute the aynchronous operation with context unlocked if the script is paused * @return True if the operation should be removed (was one-shot) */ virtual bool run() = 0; private: ScriptRun* m_runner; }; /** * An instance of script code and data, status machine run by a single thread at a time * @short Script runtime execution */ class YSCRIPT_API ScriptRun : public GenObject, public ScriptMutex { friend class ScriptCode; YCLASS(ScriptRun,GenObject) YNOCOPY(ScriptRun); public: /** * Runtime states */ enum Status { Invalid, Running, Incomplete, Succeeded, Failed, }; /** * Constructor * @param code Code fragment to execute * @param context Script context, an empty one will be allocated if NULL */ ScriptRun(ScriptCode* code, ScriptContext* context = 0); /** * Destructor, disposes the code and context */ virtual ~ScriptRun(); /** * Retrieve the parsed code being executed * @return Pointer to ScriptCode object */ inline ScriptCode* code() const { return m_code; } /** * Retrieve the execution context associated with the runtime * @return Pointer to ScriptContext object */ inline ScriptContext* context() const { return m_context; } /** * Current state of the runtime */ inline Status state() const { return m_state; } /** * Get the text description of a runtime state * @param state State to describe * @return Description of the runtime state */ static const char* textState(Status state); /** * Get the text description of the current runtime state * @return Description of the runtime state */ inline const char* textState() const { return textState(m_state); } /** * Access the runtime execution stack * @return The internal execution stack */ inline ObjList& stack() { return m_stack; } /** * Const access the runtime execution stack * @return The internal execution stack */ inline const ObjList& stack() const { return m_stack; } /** * Create a duplicate of the runtime with its own stack and state * @return New clone of the runtime */ inline ScriptRun* clone() const { return new ScriptRun(code(),context()); } /** * Resets code execution to the beginning, does not clear context * @param init Initialize context * @return Status of the runtime after reset */ virtual Status reset(bool init = false); /** * Execute script from where it was left, may stop and return Incomplete state * @return Status of the runtime after code execution */ virtual Status execute(); /** * Execute script from the beginning until it returns a final state * @param init Initialize context * @return Final status of the runtime after code execution */ virtual Status run(bool init = true); /** * Pause the script, make it return Incomplete state * @return True if pausing the script succeeded or was already paused */ virtual bool pause(); /** * Call a script function or method * @param name Name of the function to call * @param args Values to pass as actual function arguments * @param thisObj Object to pass as "this" if applicable * @param scopeObj Optional object to be used for scope resolution inside the call * @return Final status of the runtime after function call */ virtual Status call(const String& name, ObjList& args, ExpOperation* thisObj = 0, ExpOperation* scopeObj = 0); /** * Check if a script has a certain function or method * @param name Name of the function to check * @return True if function exists in code */ virtual bool callable(const String& name); /** * Insert an asynchronous operation to be executed * @param oper Operation to be inserted, will be owned by the runtime instance * @return True if the operation was added */ virtual bool insertAsync(ScriptAsync* oper); /** * Append an asynchronous operation to be executed * @param oper Operation to be appended, will be owned by the runtime instance * @return True if the operation was added */ virtual bool appendAsync(ScriptAsync* oper); /** * Retrieve current file line being executed * @return The file line being evaluated */ virtual unsigned int currentLineNo() const { return 0; } /** * Retrieve the name of the source file from which code is being executed * @param wholePath Retrieve name including path * @return The file name */ virtual const String& currentFileName(bool wholePath = false) const { return String::empty(); } /** * Get the trace ID associated with this instance * @return The trace ID */ virtual const String& traceId() const { return m_traceId; } /** * Set an associated trace ID for this instance * @param tid Trace ID to associate */ virtual void setTraceId(const String& tid) { m_traceId = tid; } /** * Try to assign a value to a single field in the script context * @param oper Field to assign to, contains the field name and new value * @param context Pointer to arbitrary object to be passed to called methods * @return True if assignment succeeded */ bool runAssign(const ExpOperation& oper, GenObject* context = 0); void objCreated(GenObject* obj) { if (m_context) m_context->createdObj(obj); }; void objDeleted(GenObject* obj) { if (m_context) m_context->deletedObj(obj); }; protected: /** * Resume script from where it was left, may stop and return Incomplete state * @return Status of the runtime after code execution */ virtual Status resume(); private: ScriptCode* m_code; ScriptContext* m_context; Status m_state; ObjList m_stack; ObjList m_async; String m_traceId; }; /** * Abstract parser, base class for each language parser * @short Abstract script parser */ class YSCRIPT_API ScriptParser : public GenObject { YCLASS(ScriptParser,GenObject) public: /** * Destructor, releases code */ virtual ~ScriptParser(); /** * Parse a string as script source code * @param text Source code text * @param fragment True if the code is just an included fragment * @param file Name of the file that is being parsed * @param len Length of text, negative if unknown * @return True if the text was successfully parsed */ virtual bool parse(const char* text, bool fragment = false, const char* file = 0, int len = -1) = 0; /** * Parse a file as script source code * @param name Source file name * @param fragment True if the code is just an included fragment * @return True if the file was successfully parsed */ virtual bool parseFile(const char* name, bool fragment = false); /** * Clear any existing parsed code */ inline void clear() { setCode(0); } /** * Retrieve the currently stored parsed code * @return Parsed code block, may be NULL */ inline ScriptCode* code() const { return m_code; } /** * Set the maximum loaded file length * @param len New maximum file length */ inline void setMaxFileLen(unsigned int len) { m_maxFileLen = len; } /** * Retrieve the maximum loadable file size * @return The maximum number of octets that will be loaded from a file */ inline unsigned int maxFileLen() const { return m_maxFileLen; } /** * Create a context adequate for the parsed code * @return A new script context */ virtual ScriptContext* createContext(unsigned int instIdx = 0, unsigned int maxInst = 1) const; /** * Create a runner adequate for a block of parsed code * @param code Parsed code block * @param context Script context, an empty one will be allocated if NULL * @param title An optional name for the runner * @return A new script runner, NULL if code is NULL */ virtual ScriptRun* createRunner(ScriptCode* code, ScriptContext* context = 0, const char* title = 0, unsigned int instIdx = 0, unsigned int maxInst = 1) const; /** * Create a runner adequate for the parsed code * @param context Script context, an empty one will be allocated if NULL * @param title An optional name for the runner * @return A new script runner, NULL if code is not yet parsed */ inline ScriptRun* createRunner(ScriptContext* context = 0, const char* title = 0, unsigned int instIdx = 0, unsigned int maxInst = 1) const { return createRunner(code(),context,title,instIdx,maxInst); } /** * Check if a script has a certain function or method * @param name Name of the function to check * @return True if function exists in code */ virtual bool callable(const String& name); protected: /** * Default constructor for derived classes */ inline ScriptParser() : m_code(0), m_maxFileLen(500000) { } /** * Set the just parsed block of code * @param code Parsed code block, may be NULL */ void setCode(ScriptCode* code); private: ScriptCode* m_code; unsigned int m_maxFileLen; }; class JsFunction; /** * Javascript Object class, base for all JS objects * @short Javascript Object */ class YSCRIPT_API JsObject : public ScriptContext { friend class JsFunction; YCLASS(JsObject,ScriptContext) public: /** * Dump object flags */ enum DumpFlags { DumpFunc = 0x01, // Dump functions DumpProp = 0x02, // Dump non functions (data) DumpRecursive = 0x10, // Dump recursive (stop on root if not set) DumpType = 0x20, // Dump type (apply to functions also), DumpProto = 0x40, // Dump prototype DumpPropObjType = 0x80, // Dump non basic type for DumpPropOnly whithout DumpType // Masks DumpFuncOnly = DumpRecursive | DumpProto | DumpFunc, DumpPropOnly = DumpRecursive | DumpPropObjType | DumpProp, }; /** * Constructor * @param name Name of the object * @param mtx Pointer to the mutex that serializes this object * @param frozen True if the object is to be frozen from creation */ JsObject(const char* name = "Object", ScriptMutex* mtx = 0, bool frozen = false); /** * Constructor for an empty object * @param mtx Pointer to the mutex that serializes this object * @param name Full name of the object * @param line Code line where this object was created * @param frozen True if the object is to be frozen from creation */ JsObject(ScriptMutex* mtx, const char* name, unsigned int line, bool frozen = 0); /** * Constructor for an empty object with prototype * @param context Script context from which Object prototype is obtained * @param line Code line where this object was created * @param mtx Pointer to the mutex that serializes this object * @param frozen True if the object is to be frozen from creation */ JsObject(GenObject* context, unsigned int line, ScriptMutex* mtx = 0, bool frozen = false); /** * Destructor */ virtual ~JsObject(); /** * Retrieve the Mutex object used to serialize object access * @return Pointer to the mutex of the context this object belongs to */ virtual ScriptMutex* mutex() { return m_mutex; } /** * Clone and rename method * @param name Name of the cloned object * @param oper ExpOperation that required the clone * @return New object instance */ virtual JsObject* clone(const char* name, const ExpOperation& oper ) const { return new JsObject(m_mutex,name,oper.lineNumber()); } /** * Clone method * @param oper ExpOperation that required the clone * @return New object instance */ inline JsObject* clone(const ExpOperation& oper) const { return clone(toString(),oper); } /** * Set the object prototype * @param context Pointer to arbitrary object passed from evaluation methods * @param objName Name of the object prototype to set the this object */ void setPrototype(GenObject* context, const String& objName); /** * Deep copy method * @param mtx Pointer to the mutex that serializes the copied object * @param oper Caller of copying operation * @return New object instance, does not keep references to old object */ virtual JsObject* copy(ScriptMutex* mtx, const ExpOperation& oper) const; /** * Fill a list with the unique names of all fields * @param names List to which key names must be added */ virtual void fillFieldNames(ObjList& names); /** * Retrieve enclosed hashed list paramsters * @return HashList pointer, NULL if this object don't hold one */ virtual const HashList* getHashListParams() const; /** * Check if a certain field is assigned in the object or its prototype * @param stack Evaluation stack in use * @param name Name of the field to test * @param context Pointer to arbitrary object passed from evaluation methods * @return True if the field is present */ virtual bool hasField(ObjList& stack, const String& name, GenObject* context) const; /** * Get a pointer to a field in the object or its prototype * @param stack Evaluation stack in use * @param name Name of the field to retrieve * @param context Pointer to arbitrary object passed from evaluation methods * @return Pointer to field, NULL if not present */ virtual NamedString* getField(ObjList& stack, const String& name, GenObject* context) const; /** * Native constructor initialization, called by addConstructor on the prototype * @param construct Function that has this object as prototype */ virtual void initConstructor(JsFunction* construct) { } /** * Native object constructor, it's run on the prototype * @param stack Evaluation stack in use * @param oper Constructor function to evaluate * @param context Pointer to arbitrary object passed from evaluation methods * @return New created and populated Javascript object */ virtual JsObject* runConstructor(ObjList& stack, const ExpOperation& oper, GenObject* context); /** * Try to evaluate a single method * @param stack Evaluation stack in use, parameters are popped off this stack * and results are pushed back on stack * @param oper Function to evaluate * @param context Pointer to arbitrary object passed from evaluation methods * @return True if evaluation succeeded */ virtual bool runFunction(ObjList& stack, const ExpOperation& oper, GenObject* context); /** * Try to evaluate a single field * @param stack Evaluation stack in use, field value must be pushed on it * @param oper Field to evaluate * @param context Pointer to arbitrary object passed from evaluation methods * @return True if evaluation succeeded */ virtual bool runField(ObjList& stack, const ExpOperation& oper, GenObject* context); /** * Try to assign a value to a single field if object is not frozen * @param stack Evaluation stack in use * @param oper Field to assign to, contains the field name and new value * @param context Pointer to arbitrary object passed from evaluation methods * @return True if assignment succeeded */ virtual bool runAssign(ObjList& stack, const ExpOperation& oper, GenObject* context); /** * Pops and evaluate the value of an operand off an evaluation stack, does not pop a barrier * @param stack Evaluation stack to remove the operand from * @param context Pointer to arbitrary object to be passed to called methods * @return Value removed from stack, NULL if stack underflow or field not evaluable */ virtual ExpOperation* popValue(ObjList& stack, GenObject* context = 0); /** * Delete a field of the object * @param name Name of field to remove */ virtual void clearField(const String& name) { params().clearParam(name); } /** * Set a integer field in this object * @param name Name of field to set * @param val Integer value to set * @return True if set was successful */ inline bool setIntField(const char* name, int64_t val) { if (!name) return false; params().setParam(new ExpOperation(val,name)); return true; } /** * Set a boolean field in this object * @param name Name of field to set * @param val Boolean value to set * @return True if set was successful */ inline bool setBoolField(const char* name, bool val) { if (!name) return false; params().setParam(new ExpOperation(val,name)); return true; } /** * Set a string field in this object * @param name Name of field to set * @param val String value to set * @return True if set was successful */ inline bool setStringField(const char* name, const char* val) { if (!name) return false; params().setParam(new ExpOperation(val,name)); return true; } /** * Set a object field in this object * @param name Name of field to set * @param obj Object value to set * @return True if set was successful */ inline bool setObjField(const char* name, JsObject* obj) { if (!(name && obj)) return false; params().setParam(new ExpWrapper(obj,name)); return true; } /** * Get the integer value of the field with the given name * @param name Name of field to retrieve * @param val Field where to put the retrieved value * @return True if field was retrieved, false if not found or not the right type */ bool getIntField(const String& name, int64_t& val); /** * Get the boolean value of the field with the given name * @param name Name of field to retrieve * @param val Field where to put the retrieved value * @return True if field was retrieved, false if not found or not the right type */ bool getBoolField(const String& name, bool& val); /** * Get the string value of the field with the given name * @param name Name of field to retrieve * @param val Field where to put the retrieved value * @return True if field was retrieved, false if not found or not the right type */ bool getStringField(const String& name, String& val); /** * Get the object associated with the field with the given name * @param name Name of field to retrieve * @param obj Field where to put the retrieved value * @return True if field was retrieved, false if not found or not the right type */ bool getObjField(const String& name, JsObject*& obj); /** * Retrieve the object frozen status (cannot modify attributes or methods) * @return True if the object is frozen */ inline bool frozen() const { return m_frozen; } /** * Freeze the Javascript object preventing external changes to it */ inline void freeze() { m_frozen = true; } /** * Set the script line number at which this object was created * @param line Line number */ inline void lineNo(unsigned int line) { m_lineNo = line; } /** * Get the script line number at which this object was created; * @return The line number from the script where this object was created. */ inline unsigned int lineNo() const { return m_lineNo; } /** * Helper static method that adds an object to a parent * @param params List of parameters where to add the object * @param name Name of the new parameter * @param obj Pointer to the object to add */ static void addObject(NamedList& params, const char* name, JsObject* obj); /** * Helper static method that adds a constructor to a parent * @param params List of parameters where to add the constructor * @param name Name of the new parameter * @param obj Pointer to the prototype object to add */ static void addConstructor(NamedList& params, const char* name, JsObject* obj); /** * Helper static method that pops arguments off a stack to a list in proper order * @param obj Pointer to the object to use when popping each argument * @param stack Evaluation stack in use, parameters are popped off this stack * @param oper Function that is being evaluated * @param context Pointer to arbitrary object passed from evaluation methods * @param arguments List where the arguments are added in proper order * @return Number of arguments popped off stack */ static int extractArgs(JsObject* obj, ObjList& stack, const ExpOperation& oper, GenObject* context, ObjList& arguments); /** * Helper method that pops arguments off a stack to a list in proper order * @param stack Evaluation stack in use, parameters are popped off this stack * @param oper Function that is being evaluated * @param context Pointer to arbitrary object passed from evaluation methods * @param arguments List where the arguments are added in proper order * @return Number of arguments popped off stack */ inline int extractArgs(ObjList& stack, const ExpOperation& oper, GenObject* context, ObjList& arguments) { return extractArgs(this,stack,oper,context,arguments); } /** * Create an empty function call context * @param mtx Pointer to the mutex that serializes this object * @param thisObj Optional object that will be set as "this" * @return New empty object usable as call context */ static JsObject* buildCallContext(ScriptMutex* mtx, JsObject* thisObj = 0); /** * Initialize the standard global objects in a context * @param context Script context to initialize */ static void initialize(ScriptContext* context); /** * Set the creation line for this object and its properties * @param obj Object t set the line for * @param lineNo The line number to set * @param recursive True to set it to its sub-objects, false otherwise */ static void setLineForObj(JsObject* obj,unsigned int lineNo, bool recursive); /** * Get the name of the internal property used to track prototypes * @return The "__proto__" constant string */ inline static const String& protoName() { return s_protoName; } /** * Static helper method that deep copies all parameters * @param dst Destination parameters * @param src Source parameters * @param mtx Mutex to be used to synchronize all new objects */ static void deepCopyParams(NamedList& dst, const NamedList& src, ScriptMutex* mtx); /** * Helper method to return the hierarchical structure of an object * @param obj Object to dump structure * @param buf String to which the structure is added * @param flags Flags indicating what to dump */ static void dumpRecursive(const GenObject* obj, String& buf, unsigned int flags = 0xffffffff); /** * Helper method to display the hierarchical structure of an object * @param obj Object to display * @param flags Flags indicating what to dump (display) */ static void printRecursive(const GenObject* obj, unsigned int flags = 0xffffffff); /** * Static method to obtain a JSON representation of the given object * @param oper Object to represent in JSON format * @param spaces Number of spaces used for one indentation level * @return String represention of the object */ static ExpOperation* toJSON(const ExpOperation* oper, int spaces); /** * * @param oper Object to handle * @return */ static bool resolveReferences(ExpOperation* oper); /** * Find a value in object by path * @param oper Object to handle * @param path Path to use * @return Found property value, NULL if not found */ static ExpOperation* find(ExpOperation* oper, const JPath& path); protected: /** * Try to evaluate a single native method * @param stack Evaluation stack in use, parameters are popped off this stack * and results are pushed back on stack * @param oper Function to evaluate * @param context Pointer to arbitrary object passed from evaluation methods * @return True if evaluation succeeded */ virtual bool runNative(ObjList& stack, const ExpOperation& oper, GenObject* context); /** * Retrieve the Mutex object used to serialize object access * @return Pointer to the mutex of the context this object belongs to */ inline ScriptMutex* mutex() const { return m_mutex; } /** * Set the Mutex used to serialize this object, set to 0 to reset it * @param mtx Mutex to set */ inline void setMutex(ScriptMutex* mtx) { m_mutex = mtx; } /** * Static method to obtain a JSON representation of the given object * @param ns Object to represent in JSON format * @param buf String used as output for the JSON represantion * @param spaces Number of spaces used for one indentation level * @param indent Current number of spaces used for indentation * @param data Internal data used for various purposes * @param path Current path if any * @param crtProp Current property if any */ static inline void toJSON(const NamedString* ns, String& buf, int spaces, int indent = 0, void* data = 0, const String& path = String::empty(), const String& crtProp = String::empty()) { internalToJSON(ns,true,buf,spaces,indent,data,path,crtProp); } /** * Static helper method for escaping special characters when JSON stringifying * @param str String to escape * @return Escaped string */ static String strEscape(const char* str); private: static bool recursiveToJSON(String& newPath, JsObject* jso, String& buf, int spaces, int indent, void* data, const String& path, const String& crtProp); static void internalToJSON(const GenObject* obj, bool isStr, String& buf, int spaces, int indent = 0, void* data = 0, const String& path = String::empty(), const String& crtProp = String::empty()); static const String s_protoName; bool m_frozen; ScriptMutex* m_mutex; unsigned int m_lineNo; // creation line for this object; }; /** * Javascript Function class, implements user defined functions * @short Javascript Function */ class YSCRIPT_API JsFunction : public JsObject { YCLASS(JsFunction,JsObject) public: /** * Constructor * @param mtx Pointer to the mutex that serializes this object */ JsFunction(ScriptMutex* mtx = 0); /** * Constructor with function name * @param mtx Pointer to the mutex that serializes this object * @param name Name of the function * @param line Code line where this object was created * @param args Optional list of formal parameter names, will be emptied * @param lbl Number of the entry point label * @param code The script code to be used while running the function */ JsFunction(ScriptMutex* mtx, const char* name, unsigned int line, ObjList* args = 0, long int lbl = 0, ScriptCode* code = 0); /** * Try to evaluate a single user defined method * @param stack Evaluation stack in use, parameters are popped off this stack * and results are pushed back on stack * @param oper Function to evaluate * @param context Pointer to arbitrary object passed from evaluation methods * @param thisObj Object that should act as "this" for the function call * @return True if evaluation succeeded */ virtual bool runDefined(ObjList& stack, const ExpOperation& oper, GenObject* context, JsObject* thisObj = 0); /** * Function constructor initialization * @param construct The Function function */ virtual void initConstructor(JsFunction* construct); /** * Retrieve the ExpFunction matching this Javascript function * @return Pointer to ExpFunction representation */ inline const ExpFunction* getFunc() const { return &m_func; } /** * Set the name of this function if still empty * @param name Name to set as first assigned name */ inline void firstName(const char* name) { if (m_func.name().null()) const_cast(m_func.name()) = name; } /** * Retrieve the name of the N-th formal argument * @param index Index of the formal argument * @return Pointer to formal argument name, NULL if index too large */ inline const String* formalName(unsigned int index) const { return static_cast(m_formal[index]); } /** * Retrieve the entry label of the code for this function * @return Number of the entry point label, zero if no code defined */ inline long int label() const { return m_label; } /** * Deep copy method * @param mtx Pointer to the mutex that serializes the copied array * @param oper ExpOperation that required the copy * @return New object instance, does not keep references to old array */ inline JsObject* copy(ScriptMutex* mtx, const ExpOperation& oper) const { return copy(mtx,0,oper); } /** * Deep copy method with given name * @param mtx Pointer to the mutex that serializes the copied array * @param name Name for the copied function * @param oper ExpOperation that required the copy * @return New object instance, does not keep references to old array */ virtual JsObject* copy(ScriptMutex* mtx, const char* name, const ExpOperation& oper) const; /** * Clone function * @param oper ExpOperation that required the clone * @param mtx Pointer to the mutex that serializes the cloning * @return A copy of JsFunction if not already cloned, a reference to itself is function is already a clone */ virtual JsFunction* cloneFunction(const ExpOperation& oper, ScriptMutex* mtx = 0); protected: /** * Try to evaluate a single native method * @param stack Evaluation stack in use, parameters are popped off this stack * and results are pushed back on stack * @param oper Function to evaluate * @param context Pointer to arbitrary object passed from evaluation methods * @return True if evaluation succeeded */ virtual bool runNative(ObjList& stack, const ExpOperation& oper, GenObject* context); private: void init(); ObjList m_formal; long int m_label; ScriptCode* m_code; ExpFunction m_func; }; /** * Javascript Array class, implements arrays of items * @short Javascript Array */ class YSCRIPT_API JsArray : public JsObject { friend class JsObject; YCLASS(JsArray,JsObject) public: /** * Constructor for an empty array with prototype * @param context Script context from which Array prototype is obtained * @param line Code line where this object was created * @param mtx Pointer to the mutex that serializes this object */ JsArray(GenObject* context, unsigned int line, ScriptMutex* mtx = 0); /** * Constructor for an empty array * @param mtx Pointer to the mutex that serializes this object * @param name Full name of the object * @param line Code line where this object was created * @param frozen True if the object is to be frozen from creation */ inline JsArray(ScriptMutex* mtx, const char* name, unsigned int line, bool frozen = false) : JsObject(mtx,name,line,frozen), m_length(0) { } /** * Retrieve the length of the array * @return Number of numerically indexed objects in array */ inline int32_t length() const { return m_length; } /** * Set the internal length to a specific value * @param len Length of array to set */ inline void setLength(int32_t len) { m_length = len; } /** * Add an item at the end of the array * @param item Item to add to array */ void push(ExpOperation* item); /** * Add string items at the end of the array * @param lst List with items to push */ inline void push(const ObjList& lst) { for (const ObjList* o = lst.skipNull(); o; o = o->skipNext()) push(new ExpOperation(o->get()->toString())); } /** * Deep copy method * @param mtx Pointer to the mutex that serializes the copied array * @param oper ExpOperation that required the copy operation * @return New object instance, does not keep references to old array */ virtual JsObject* copy(ScriptMutex* mtx, const ExpOperation& oper) const; /** * Try to assign a value to a single field if object is not frozen and update array length. * Reimplemented from JsObject * @param stack Evaluation stack in use * @param oper Field to assign to, contains the field name and new value * @param context Pointer to arbitrary object passed from evaluation methods * @return True if assignment succeeded */ virtual bool runAssign(ObjList& stack, const ExpOperation& oper, GenObject* context); /** * Try to evaluate a single field * @param stack Evaluation stack in use, field value must be pushed on it * @param oper Field to evaluate * @param context Pointer to arbitrary object passed from evaluation methods * @return True if evaluation succeeded */ virtual bool runField(ObjList& stack, const ExpOperation& oper, GenObject* context); /** * Array constructor initialization * @param construct The Array function */ virtual void initConstructor(JsFunction* construct); /** * Array object constructor, it's run on the prototype * @param stack Evaluation stack in use * @param oper Constructor function to evaluate * @param context Pointer to arbitrary object passed from evaluation methods * @return New created and populated Javascript Array object */ virtual JsObject* runConstructor(ObjList& stack, const ExpOperation& oper, GenObject* context); protected: /** * Clone and rename method * @param name Name of the cloned object * @param oper ExpOperation that requested the cloning * @return New object instance */ virtual JsObject* clone(const char* name, const ExpOperation& oper) const { return new JsArray(mutex(),name,oper.lineNumber()); } /** * Try to evaluate a single native method * @param stack Evaluation stack in use, parameters are popped off this stack * and results are pushed back on stack * @param oper Function to evaluate * @param context Pointer to arbitrary object passed from evaluation methods * @return True if evaluation succeeded */ bool runNative(ObjList& stack, const ExpOperation& oper, GenObject* context); private: /** * Private constructor * @param mtx Pointer to the mutex that serializes this object */ JsArray(ScriptMutex* mtx = 0); bool runNativeSlice(ObjList& stack, const ExpOperation& oper, GenObject* context); bool runNativeSplice(ObjList& stack, const ExpOperation& oper, GenObject* context); bool runNativeSort(ObjList& stack, const ExpOperation& oper, GenObject* context); int32_t m_length; }; /** * Javascript RegExp class, implements regular expression matching * @short Javascript RegExp */ class YSCRIPT_API JsRegExp : public JsObject { YCLASS(JsRegExp,JsObject) public: /** * Constructor for a RegExp constructor * @param mtx Pointer to the mutex that serializes this object */ JsRegExp(ScriptMutex* mtx = 0); /** * Constructor for a RegExp object * @param mtx Pointer to the mutex that serializes this object * @param name Full name of the object * @param line Code line where this object was created * @param rexp Regular expression text * @param insensitive True to not differentiate case * @param extended True to use POSIX Extended Regular Expression syntax * @param frozen True to create an initially frozen object */ JsRegExp(ScriptMutex* mtx, const char* name, unsigned int line, const char* rexp = 0, bool insensitive = false, bool extended = true, bool frozen = false); /** * Constructor from existing Regexp * @param mtx Pointer to the mutex that serializes this object * @param line Code line where this object was created * @param rexp Regular expression to copy * @param frozen True to create an initially frozen object */ JsRegExp(ScriptMutex* mtx, unsigned int line, const Regexp& rexp, bool frozen = false); /** * Access the internal Regexp object that does the matching * @return Const reference to the internal Regexp object */ inline const Regexp& regexp() const { return m_regexp; } /** * Access the internal Regexp object that does the matching * @return Reference to the internal Regexp object */ inline Regexp& regexp() { return m_regexp; } /** * Try to assign a value to a single field * @param stack Evaluation stack in use * @param oper Field to assign to, contains the field name and new value * @param context Pointer to arbitrary object passed from evaluation methods * @return True if assignment succeeded */ virtual bool runAssign(ObjList& stack, const ExpOperation& oper, GenObject* context); /** * RegExp object constructor, it's run on the prototype * @param stack Evaluation stack in use * @param oper Constructor function to evaluate * @param context Pointer to arbitrary object passed from evaluation methods * @return New created and populated Javascript RegExp object */ virtual JsObject* runConstructor(ObjList& stack, const ExpOperation& oper, GenObject* context); /** * Deep copy method * @param mtx Pointer to the mutex that serializes the copied regexp * @param oper ExpOperation that required the copy * @return New object instance, does not keep references to old regexp */ virtual JsObject* copy(ScriptMutex* mtx, const ExpOperation& oper) const; protected: /** * Clone and rename method * @param name Name of the cloned object * @param oper ExpOperation that required the clone * @return New object instance */ virtual JsObject* clone(const char* name, const ExpOperation& oper) const { return new JsRegExp(mutex(),name,oper.lineNumber(),m_regexp.c_str(), m_regexp.isCaseInsensitive(),m_regexp.isExtended()); } /** * Try to evaluate a single native method * @param stack Evaluation stack in use, parameters are popped off this stack * and results are pushed back on stack * @param oper Function to evaluate * @param context Pointer to arbitrary object passed from evaluation methods * @return True if evaluation succeeded */ bool runNative(ObjList& stack, const ExpOperation& oper, GenObject* context); private: Regexp m_regexp; }; /** * Javascript JSON path class * @short Javascript JSON path */ class JsJPath : public JsObject { public: /** * Constructor * @param mtx Pointer to the mutex that serializes this object */ JsJPath(ScriptMutex* mtx); /** * JPath object constructor, it's run on the prototype * @param stack Evaluation stack in use * @param oper Constructor function to evaluate * @param context Pointer to arbitrary object passed from evaluation methods * @return Newly created and populated Javascript JSON path */ virtual JsObject* runConstructor(ObjList& stack, const ExpOperation& oper, GenObject* context); /** * Retrieve held path * @return Held path reference */ virtual const JPath& path() const { return m_path; } /** * Retrieve path string * @return Held path value */ virtual const String& toString() const { return m_path; } /** * Get a pointer to a derived class given that class name * @param name Name of the class we are asking for * @return Pointer to the requested class or NULL if this object doesn't implement it */ virtual void* getObject(const String& name) const; protected: /** * Constructor from existing path * @param mtx Pointer to the mutex that serializes this object * @param line Code line where this object was created * @param path Path to copy */ inline JsJPath(ScriptMutex* mtx, unsigned int line, const char* path) : JsObject(mtx,path,line), m_path(path) { } /** * Constructor for a JPath object * @param mtx Pointer to the mutex that serializes this object * @param name Full name of the object * @param line Code line where this object was created * @param path JSON path */ inline JsJPath(ScriptMutex* mtx, const char* name, unsigned int line, const JPath& path) : JsObject(mtx,name,line), m_path(path) { } /** * Clone and rename method * @param name Name of the cloned object * @param oper ExpOperation that required the clone * @return New object instance */ virtual JsObject* clone(const char* name, const ExpOperation& oper) const { return new JsJPath(mutex(),name,oper.lineNumber(),m_path); } /** * Try to evaluate a single native method * @param stack Evaluation stack in use, parameters are popped off this stack * and results are pushed back on stack * @param oper Function to evaluate * @param context Pointer to arbitrary object passed from evaluation methods * @return True if evaluation succeeded */ bool runNative(ObjList& stack, const ExpOperation& oper, GenObject* context); private: JPath m_path; // Held JPath }; /** * Javascript parser, takes source code and generates preparsed code * @short Javascript parser */ class YSCRIPT_API JsParser : public ScriptParser { YCLASS(JsParser,ScriptParser) public: /** * Constructor * @param allowLink True to allow linking of the code, false otherwise. * @param allowTrace True to allow the script to enable performance tracing */ inline JsParser(bool allowLink = true, bool allowTrace = false) : m_allowLink(allowLink), m_allowTrace(allowTrace) { } /** * Parse a string as Javascript source code * @param text Source code text * @param fragment True if the code is just an included fragment * @param file Name of the file that is being parsed * @param len Length of text, negative if unknown * @return True if the text was successfully parsed */ virtual bool parse(const char* text, bool fragment = false, const char* file = 0, int len = -1); /** * Create a context adequate for Javascript code * @return A new Javascript context */ virtual ScriptContext* createContext(unsigned int instIdx = 0, unsigned int maxInst = 1) const; /** * Create a runner adequate for a block of parsed Javascript code * @param code Parsed code block * @param context Javascript context, an empty one will be allocated if NULL * @param title An optional name for the runner * @return A new Javascript runner, NULL if code is NULL */ virtual ScriptRun* createRunner(ScriptCode* code, ScriptContext* context = 0, const char* title = 0, unsigned int instIdx = 0, unsigned int maxInst = 1) const; /** * Create a runner adequate for the parsed Javascript code * @param context Javascript context, an empty one will be allocated if NULL * @param title An optional name for the runner * @param instIdx Javascript context instance * @param maxInst Number of context instances * @return A new Javascript runner, NULL if code is not yet parsed */ inline ScriptRun* createRunner(ScriptContext* context = 0, const char* title = 0, unsigned int instIdx = 0, unsigned int maxInst = 1) const { return createRunner(code(),context,title,instIdx,maxInst); } /** * Check if a script has a certain function or method * @param name Name of the function to check * @return True if function exists in code */ virtual bool callable(const String& name); /** * Adjust a file script path to include default if needed * @param script File path to adjust * @param extraInc True to check the extra include path first */ void adjustPath(String& script, bool extraInc = false) const; /** * Retrieve the base script path * @return Base path added to relative script paths */ inline const String& basePath() const { return m_basePath; } /** * Retrieve the extra include script path * @return Include path added to relative script paths */ inline const String& includePath() const { return m_includePath; } /** * Set the base script path * @param path Base path to add to relative script paths * @param incPath Extra include path to add to relative script paths */ inline void basePath(const char* path, const char* incPath = 0) { m_basePath = path; m_includePath = incPath; } /** * Retrieve the last parsed file name * @return Name of the successfully parsed file or an empty String */ inline const String& parsedFile() const { return m_parsedFile; } /** * Check if the script or any includes have changed * @param file Name of the file to check * @return True if the script may have changed, false if not changed */ bool scriptChanged(const char* file) const; /** * Check if the script or any includes have changed * @param file Name of the file to check * @param path New base path to check * @param incPath New extra include path to check * @return True if the script may have changed, false if not changed */ inline bool scriptChanged(const char* file, const String& path, const String& incPath = String::empty()) const { return (path != m_basePath) || (incPath != m_includePath) || scriptChanged(file); } /** * Set whether the Javascript code should be linked or not * @param allowed True to allow linking, false otherwise */ inline void link(bool allowed = true) { m_allowLink = allowed; } /** * Set whether the Javascript code can be traced or not * @param allowed True to allow tracing, false otherwise */ inline void trace(bool allowed = true) { m_allowTrace = allowed; } /** * Parse and run a piece of Javascript code * @param text Source code fragment to execute * @param result Pointer to an optional pointer to store returned value * @param context Script context, an empty one will be allocated if NULL * @return Status of the runtime after code execution */ static ScriptRun::Status eval(const String& text, ExpOperation** result = 0, ScriptContext* context = 0); /** * Parse a complete block of JSON text * @param text JSON text to parse * @param mtx Pointer to the mutex that serializes this object * @param stack Pointer to an execution stack, required for adding prototypes * @param context Pointer to an execution context, required for adding prototypes * @param op ExpOperation that determined calling of this method * @return ExpOperation holding the content of JSON, must be dereferenced after use, NULL if parse error */ static ExpOperation* parseJSON(const char* text, ScriptMutex* mtx = 0, ObjList* stack = 0, GenObject* context = 0, const ExpOperation* op = 0); /** * Get a "null" object wrapper that will identity match another "null" * @param name Name of the new wrapper, "null" if empty * @return ExpWrapper for the "null" object */ static ExpOperation* nullClone(const char* name = 0); /** * Obtain the "null" object * @return Referenced "null" object (0 if ref() fails) */ static JsObject* nullObject(); /** * Check if an operation holds a null value * @return True if the operation holds a null object */ static bool isNull(const ExpOperation& oper); /** * Check if an operation holds an undefined value * @return True if the operation holds an undefined value */ static bool isUndefined(const ExpOperation& oper); /** * Check if an operation is null or undefined * @return True if the operation holds an undefined value or a null object */ static bool isMissing(const ExpOperation& oper); /** * Check if an operation is missing, holds a null or undefined * @return True if the operation is null or holds an undefined value or a null object */ inline static bool isMissing(const ExpOperation* oper) { return !oper || isMissing(*oper); } /** * Check if an operation is not null or undefined * @return True if the operation holds a value or non-null object */ inline static bool isPresent(const ExpOperation& oper) { return !isMissing(oper); } /** * Check if an operation is present and not null or undefined * @return True if the operation holds a value or non-null object */ inline static bool isPresent(const ExpOperation* oper) { return oper && !isMissing(*oper); } /** * Check if an operation holds a null or undefined value or empty string * @return True if the operation is an undefined value or a null object or empty string */ inline static bool isEmpty(const ExpOperation& oper) { return oper.null() || isMissing(oper); } /** * Check if an operation is missing, holds a null or undefined value or empty string * @return True if the operation is an undefined value or a null object or empty string */ inline static bool isEmpty(const ExpOperation* oper) { return TelEngine::null(oper) || isMissing(*oper); } /** * Check if an operation is not null or undefined or empty string * @return True if the operation holds a non-empty value or non-null object */ inline static bool isFilled(const ExpOperation& oper) { return oper && !isMissing(oper); } /** * Check if an operation is not null or undefined or empty string * @return True if the operation holds a non-empty value or non-null object */ inline static bool isFilled(const ExpOperation* oper) { return !isEmpty(oper); } private: String m_basePath; String m_includePath; String m_parsedFile; bool m_allowLink; bool m_allowTrace; }; }; // namespace TelEngine #endif /* __YATESCRIPT_H */ /* vi: set ts=8 sw=4 sts=4 noet: */