1 /*
2   ==============================================================================
3 
4    This file is part of the JUCE library.
5    Copyright (c) 2017 - ROLI Ltd.
6 
7    JUCE is an open source library subject to commercial or open-source
8    licensing.
9 
10    The code included in this file is provided under the terms of the ISC license
11    http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12    To use, copy, modify, and/or distribute this software for any purpose with or
13    without fee is hereby granted provided that the above copyright notice and
14    this permission notice appear in all copies.
15 
16    JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17    EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18    DISCLAIMED.
19 
20   ==============================================================================
21 */
22 
23 namespace juce
24 {
25 
26 #define JUCE_JS_OPERATORS(X) \
27     X(semicolon,     ";")        X(dot,          ".")       X(comma,        ",") \
28     X(openParen,     "(")        X(closeParen,   ")")       X(openBrace,    "{")    X(closeBrace, "}") \
29     X(openBracket,   "[")        X(closeBracket, "]")       X(colon,        ":")    X(question,   "?") \
30     X(typeEquals,    "===")      X(equals,       "==")      X(assign,       "=") \
31     X(typeNotEquals, "!==")      X(notEquals,    "!=")      X(logicalNot,   "!") \
32     X(plusEquals,    "+=")       X(plusplus,     "++")      X(plus,         "+") \
33     X(minusEquals,   "-=")       X(minusminus,   "--")      X(minus,        "-") \
34     X(timesEquals,   "*=")       X(times,        "*")       X(divideEquals, "/=")   X(divide,     "/") \
35     X(moduloEquals,  "%=")       X(modulo,       "%")       X(xorEquals,    "^=")   X(bitwiseXor, "^") \
36     X(andEquals,     "&=")       X(logicalAnd,   "&&")      X(bitwiseAnd,   "&") \
37     X(orEquals,      "|=")       X(logicalOr,    "||")      X(bitwiseOr,    "|") \
38     X(leftShiftEquals,    "<<=") X(lessThanOrEqual,  "<=")  X(leftShift,    "<<")   X(lessThan,   "<") \
39     X(rightShiftUnsigned, ">>>") X(rightShiftEquals, ">>=") X(rightShift,   ">>")   X(greaterThanOrEqual, ">=")  X(greaterThan,  ">")
40 
41 #define JUCE_JS_KEYWORDS(X) \
42     X(var,      "var")      X(if_,     "if")     X(else_,  "else")   X(do_,       "do")       X(null_,     "null") \
43     X(while_,   "while")    X(for_,    "for")    X(break_, "break")  X(continue_, "continue") X(undefined, "undefined") \
44     X(function, "function") X(return_, "return") X(true_,  "true")   X(false_,    "false")    X(new_,      "new") \
45     X(typeof_,  "typeof")
46 
47 namespace TokenTypes
48 {
49     #define JUCE_DECLARE_JS_TOKEN(name, str)  static const char* const name = str;
50     JUCE_JS_KEYWORDS  (JUCE_DECLARE_JS_TOKEN)
51     JUCE_JS_OPERATORS (JUCE_DECLARE_JS_TOKEN)
52     JUCE_DECLARE_JS_TOKEN (eof,        "$eof")
53     JUCE_DECLARE_JS_TOKEN (literal,    "$literal")
54     JUCE_DECLARE_JS_TOKEN (identifier, "$identifier")
55 }
56 
57 #if JUCE_MSVC
58  #pragma warning (push)
59  #pragma warning (disable: 4702)
60 #endif
61 
62 //==============================================================================
63 struct JavascriptEngine::RootObject   : public DynamicObject
64 {
RootObjectjuce::JavascriptEngine::RootObject65     RootObject()
66     {
67         setMethod ("exec",       exec);
68         setMethod ("eval",       eval);
69         setMethod ("trace",      trace);
70         setMethod ("charToInt",  charToInt);
71         setMethod ("parseInt",   IntegerClass::parseInt);
72         setMethod ("typeof",     typeof_internal);
73         setMethod ("parseFloat", parseFloat);
74     }
75 
76     Time timeout;
77 
78     using Args = const var::NativeFunctionArgs&;
79     using TokenType = const char*;
80 
executejuce::JavascriptEngine::RootObject81     void execute (const String& code)
82     {
83         ExpressionTreeBuilder tb (code);
84         std::unique_ptr<BlockStatement> (tb.parseStatementList())->perform (Scope ({}, *this, *this), nullptr);
85     }
86 
evaluatejuce::JavascriptEngine::RootObject87     var evaluate (const String& code)
88     {
89         ExpressionTreeBuilder tb (code);
90         return ExpPtr (tb.parseExpression())->getResult (Scope ({}, *this, *this));
91     }
92 
93     //==============================================================================
areTypeEqualjuce::JavascriptEngine::RootObject94     static bool areTypeEqual (const var& a, const var& b)
95     {
96         return a.hasSameTypeAs (b) && isFunction (a) == isFunction (b)
97                 && (((a.isUndefined() || a.isVoid()) && (b.isUndefined() || b.isVoid())) || a == b);
98     }
99 
getTokenNamejuce::JavascriptEngine::RootObject100     static String getTokenName (TokenType t)                  { return t[0] == '$' ? String (t + 1) : ("'" + String (t) + "'"); }
isFunctionjuce::JavascriptEngine::RootObject101     static bool isFunction (const var& v) noexcept            { return dynamic_cast<FunctionObject*> (v.getObject()) != nullptr; }
isNumericjuce::JavascriptEngine::RootObject102     static bool isNumeric (const var& v) noexcept             { return v.isInt() || v.isDouble() || v.isInt64() || v.isBool(); }
isNumericOrUndefinedjuce::JavascriptEngine::RootObject103     static bool isNumericOrUndefined (const var& v) noexcept  { return isNumeric (v) || v.isUndefined(); }
getOctalValuejuce::JavascriptEngine::RootObject104     static int64 getOctalValue (const String& s)              { BigInteger b; b.parseString (s.initialSectionContainingOnly ("01234567"), 8); return b.toInt64(); }
getPrototypeIdentifierjuce::JavascriptEngine::RootObject105     static Identifier getPrototypeIdentifier()                { static const Identifier i ("prototype"); return i; }
getPropertyPointerjuce::JavascriptEngine::RootObject106     static var* getPropertyPointer (DynamicObject& o, const Identifier& i) noexcept   { return o.getProperties().getVarPointer (i); }
107 
108     //==============================================================================
109     struct CodeLocation
110     {
CodeLocationjuce::JavascriptEngine::RootObject::CodeLocation111         CodeLocation (const String& code) noexcept        : program (code), location (program.getCharPointer()) {}
CodeLocationjuce::JavascriptEngine::RootObject::CodeLocation112         CodeLocation (const CodeLocation& other) noexcept : program (other.program), location (other.location) {}
113 
throwErrorjuce::JavascriptEngine::RootObject::CodeLocation114         void throwError (const String& message) const
115         {
116             int col = 1, line = 1;
117 
118             for (auto i = program.getCharPointer(); i < location && ! i.isEmpty(); ++i)
119             {
120                 ++col;
121                 if (*i == '\n')  { col = 1; ++line; }
122             }
123 
124             throw "Line " + String (line) + ", column " + String (col) + " : " + message;
125         }
126 
127         String program;
128         String::CharPointerType location;
129     };
130 
131     //==============================================================================
132     struct Scope
133     {
Scopejuce::JavascriptEngine::RootObject::Scope134         Scope (const Scope* p, ReferenceCountedObjectPtr<RootObject> rt, DynamicObject::Ptr scp) noexcept
135             : parent (p), root (std::move (rt)),
136               scope (std::move (scp)) {}
137 
138         const Scope* const parent;
139         ReferenceCountedObjectPtr<RootObject> root;
140         DynamicObject::Ptr scope;
141 
findFunctionCalljuce::JavascriptEngine::RootObject::Scope142         var findFunctionCall (const CodeLocation& location, const var& targetObject, const Identifier& functionName) const
143         {
144             if (auto* o = targetObject.getDynamicObject())
145             {
146                 if (auto* prop = getPropertyPointer (*o, functionName))
147                     return *prop;
148 
149                 for (auto* p = o->getProperty (getPrototypeIdentifier()).getDynamicObject(); p != nullptr;
150                      p = p->getProperty (getPrototypeIdentifier()).getDynamicObject())
151                 {
152                     if (auto* prop = getPropertyPointer (*p, functionName))
153                         return *prop;
154                 }
155 
156                 // if there's a class with an overridden DynamicObject::hasMethod, this avoids an error
157                 if (o->hasMethod (functionName))
158                     return {};
159             }
160 
161             if (targetObject.isString())
162                 if (auto* m = findRootClassProperty (StringClass::getClassName(), functionName))
163                     return *m;
164 
165             if (targetObject.isArray())
166                 if (auto* m = findRootClassProperty (ArrayClass::getClassName(), functionName))
167                     return *m;
168 
169             if (auto* m = findRootClassProperty (ObjectClass::getClassName(), functionName))
170                 return *m;
171 
172             location.throwError ("Unknown function '" + functionName.toString() + "'");
173             return {};
174         }
175 
findRootClassPropertyjuce::JavascriptEngine::RootObject::Scope176         var* findRootClassProperty (const Identifier& className, const Identifier& propName) const
177         {
178             if (auto* cls = root->getProperty (className).getDynamicObject())
179                 return getPropertyPointer (*cls, propName);
180 
181             return nullptr;
182         }
183 
findSymbolInParentScopesjuce::JavascriptEngine::RootObject::Scope184         var findSymbolInParentScopes (const Identifier& name) const
185         {
186             if (auto v = getPropertyPointer (*scope, name))
187                 return *v;
188 
189             return parent != nullptr ? parent->findSymbolInParentScopes (name)
190                                      : var::undefined();
191         }
192 
findAndInvokeMethodjuce::JavascriptEngine::RootObject::Scope193         bool findAndInvokeMethod (const Identifier& function, const var::NativeFunctionArgs& args, var& result) const
194         {
195             auto* target = args.thisObject.getDynamicObject();
196 
197             if (target == nullptr || target == scope.get())
198             {
199                 if (auto* m = getPropertyPointer (*scope, function))
200                 {
201                     if (auto fo = dynamic_cast<FunctionObject*> (m->getObject()))
202                     {
203                         result = fo->invoke (*this, args);
204                         return true;
205                     }
206                 }
207             }
208 
209             const auto& props = scope->getProperties();
210 
211             for (int i = 0; i < props.size(); ++i)
212                 if (auto* o = props.getValueAt (i).getDynamicObject())
213                     if (Scope (this, *root, *o).findAndInvokeMethod (function, args, result))
214                         return true;
215 
216             return false;
217         }
218 
invokeMethodjuce::JavascriptEngine::RootObject::Scope219         bool invokeMethod (const var& m, const var::NativeFunctionArgs& args, var& result) const
220         {
221             if (isFunction (m))
222             {
223                 auto* target = args.thisObject.getDynamicObject();
224 
225                 if (target == nullptr || target == scope.get())
226                 {
227                     if (auto fo = dynamic_cast<FunctionObject*> (m.getObject()))
228                     {
229                         result = fo->invoke (*this, args);
230                         return true;
231                     }
232                 }
233             }
234 
235             return false;
236         }
237 
checkTimeOutjuce::JavascriptEngine::RootObject::Scope238         void checkTimeOut (const CodeLocation& location) const
239         {
240             if (Time::getCurrentTime() > root->timeout)
241                 location.throwError (root->timeout == Time() ? "Interrupted" : "Execution timed-out");
242         }
243 
244         JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Scope)
245     };
246 
247     //==============================================================================
248     struct Statement
249     {
Statementjuce::JavascriptEngine::RootObject::Statement250         Statement (const CodeLocation& l) noexcept : location (l) {}
~Statementjuce::JavascriptEngine::RootObject::Statement251         virtual ~Statement() {}
252 
253         enum ResultCode  { ok = 0, returnWasHit, breakWasHit, continueWasHit };
performjuce::JavascriptEngine::RootObject::Statement254         virtual ResultCode perform (const Scope&, var*) const  { return ok; }
255 
256         CodeLocation location;
257         JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Statement)
258     };
259 
260     struct Expression  : public Statement
261     {
Expressionjuce::JavascriptEngine::RootObject::Expression262         Expression (const CodeLocation& l) noexcept : Statement (l) {}
263 
getResultjuce::JavascriptEngine::RootObject::Expression264         virtual var getResult (const Scope&) const            { return var::undefined(); }
assignjuce::JavascriptEngine::RootObject::Expression265         virtual void assign (const Scope&, const var&) const  { location.throwError ("Cannot assign to this expression!"); }
266 
performjuce::JavascriptEngine::RootObject::Expression267         ResultCode perform (const Scope& s, var*) const override  { getResult (s); return ok; }
268     };
269 
270     using ExpPtr = std::unique_ptr<Expression>;
271 
272     struct BlockStatement  : public Statement
273     {
BlockStatementjuce::JavascriptEngine::RootObject::BlockStatement274         BlockStatement (const CodeLocation& l) noexcept : Statement (l) {}
275 
performjuce::JavascriptEngine::RootObject::BlockStatement276         ResultCode perform (const Scope& s, var* returnedValue) const override
277         {
278             for (auto* statement : statements)
279                 if (auto r = statement->perform (s, returnedValue))
280                     return r;
281 
282             return ok;
283         }
284 
285         OwnedArray<Statement> statements;
286     };
287 
288     struct IfStatement  : public Statement
289     {
IfStatementjuce::JavascriptEngine::RootObject::IfStatement290         IfStatement (const CodeLocation& l) noexcept : Statement (l) {}
291 
performjuce::JavascriptEngine::RootObject::IfStatement292         ResultCode perform (const Scope& s, var* returnedValue) const override
293         {
294             return (condition->getResult(s) ? trueBranch : falseBranch)->perform (s, returnedValue);
295         }
296 
297         ExpPtr condition;
298         std::unique_ptr<Statement> trueBranch, falseBranch;
299     };
300 
301     struct VarStatement  : public Statement
302     {
VarStatementjuce::JavascriptEngine::RootObject::VarStatement303         VarStatement (const CodeLocation& l) noexcept : Statement (l) {}
304 
performjuce::JavascriptEngine::RootObject::VarStatement305         ResultCode perform (const Scope& s, var*) const override
306         {
307             s.scope->setProperty (name, initialiser->getResult (s));
308             return ok;
309         }
310 
311         Identifier name;
312         ExpPtr initialiser;
313     };
314 
315     struct LoopStatement  : public Statement
316     {
LoopStatementjuce::JavascriptEngine::RootObject::LoopStatement317         LoopStatement (const CodeLocation& l, bool isDo) noexcept : Statement (l), isDoLoop (isDo) {}
318 
performjuce::JavascriptEngine::RootObject::LoopStatement319         ResultCode perform (const Scope& s, var* returnedValue) const override
320         {
321             initialiser->perform (s, nullptr);
322 
323             while (isDoLoop || condition->getResult (s))
324             {
325                 s.checkTimeOut (location);
326                 auto r = body->perform (s, returnedValue);
327 
328                 if (r == returnWasHit)   return r;
329                 if (r == breakWasHit)    break;
330 
331                 iterator->perform (s, nullptr);
332 
333                 if (isDoLoop && r != continueWasHit && ! condition->getResult (s))
334                     break;
335             }
336 
337             return ok;
338         }
339 
340         std::unique_ptr<Statement> initialiser, iterator, body;
341         ExpPtr condition;
342         bool isDoLoop;
343     };
344 
345     struct ReturnStatement  : public Statement
346     {
ReturnStatementjuce::JavascriptEngine::RootObject::ReturnStatement347         ReturnStatement (const CodeLocation& l, Expression* v) noexcept : Statement (l), returnValue (v) {}
348 
performjuce::JavascriptEngine::RootObject::ReturnStatement349         ResultCode perform (const Scope& s, var* ret) const override
350         {
351             if (ret != nullptr)  *ret = returnValue->getResult (s);
352             return returnWasHit;
353         }
354 
355         ExpPtr returnValue;
356     };
357 
358     struct BreakStatement  : public Statement
359     {
BreakStatementjuce::JavascriptEngine::RootObject::BreakStatement360         BreakStatement (const CodeLocation& l) noexcept : Statement (l) {}
performjuce::JavascriptEngine::RootObject::BreakStatement361         ResultCode perform (const Scope&, var*) const override  { return breakWasHit; }
362     };
363 
364     struct ContinueStatement  : public Statement
365     {
ContinueStatementjuce::JavascriptEngine::RootObject::ContinueStatement366         ContinueStatement (const CodeLocation& l) noexcept : Statement (l) {}
performjuce::JavascriptEngine::RootObject::ContinueStatement367         ResultCode perform (const Scope&, var*) const override  { return continueWasHit; }
368     };
369 
370     struct LiteralValue  : public Expression
371     {
LiteralValuejuce::JavascriptEngine::RootObject::LiteralValue372         LiteralValue (const CodeLocation& l, const var& v) noexcept : Expression (l), value (v) {}
getResultjuce::JavascriptEngine::RootObject::LiteralValue373         var getResult (const Scope&) const override   { return value; }
374         var value;
375     };
376 
377     struct UnqualifiedName  : public Expression
378     {
UnqualifiedNamejuce::JavascriptEngine::RootObject::UnqualifiedName379         UnqualifiedName (const CodeLocation& l, const Identifier& n) noexcept : Expression (l), name (n) {}
380 
getResultjuce::JavascriptEngine::RootObject::UnqualifiedName381         var getResult (const Scope& s) const override  { return s.findSymbolInParentScopes (name); }
382 
assignjuce::JavascriptEngine::RootObject::UnqualifiedName383         void assign (const Scope& s, const var& newValue) const override
384         {
385             if (auto* v = getPropertyPointer (*s.scope, name))
386                 *v = newValue;
387             else
388                 s.root->setProperty (name, newValue);
389         }
390 
391         Identifier name;
392     };
393 
394     struct DotOperator  : public Expression
395     {
DotOperatorjuce::JavascriptEngine::RootObject::DotOperator396         DotOperator (const CodeLocation& l, ExpPtr& p, const Identifier& c) noexcept : Expression (l), parent (p.release()), child (c) {}
397 
getResultjuce::JavascriptEngine::RootObject::DotOperator398         var getResult (const Scope& s) const override
399         {
400             auto p = parent->getResult (s);
401             static const Identifier lengthID ("length");
402 
403             if (child == lengthID)
404             {
405                 if (auto* array = p.getArray())   return array->size();
406                 if (p.isString())                 return p.toString().length();
407             }
408 
409             if (auto* o = p.getDynamicObject())
410                 if (auto* v = getPropertyPointer (*o, child))
411                     return *v;
412 
413             return var::undefined();
414         }
415 
assignjuce::JavascriptEngine::RootObject::DotOperator416         void assign (const Scope& s, const var& newValue) const override
417         {
418             if (auto* o = parent->getResult (s).getDynamicObject())
419                 o->setProperty (child, newValue);
420             else
421                 Expression::assign (s, newValue);
422         }
423 
424         ExpPtr parent;
425         Identifier child;
426     };
427 
428     struct ArraySubscript  : public Expression
429     {
ArraySubscriptjuce::JavascriptEngine::RootObject::ArraySubscript430         ArraySubscript (const CodeLocation& l) noexcept : Expression (l) {}
431 
getResultjuce::JavascriptEngine::RootObject::ArraySubscript432         var getResult (const Scope& s) const override
433         {
434             auto arrayVar = object->getResult (s); // must stay alive for the scope of this method
435             auto key = index->getResult (s);
436 
437             if (const auto* array = arrayVar.getArray())
438                 if (key.isInt() || key.isInt64() || key.isDouble())
439                     return (*array) [static_cast<int> (key)];
440 
441             if (auto* o = arrayVar.getDynamicObject())
442                 if (key.isString())
443                     if (auto* v = getPropertyPointer (*o, Identifier (key)))
444                         return *v;
445 
446             return var::undefined();
447         }
448 
assignjuce::JavascriptEngine::RootObject::ArraySubscript449         void assign (const Scope& s, const var& newValue) const override
450         {
451             auto arrayVar = object->getResult (s); // must stay alive for the scope of this method
452             auto key = index->getResult (s);
453 
454             if (auto* array = arrayVar.getArray())
455             {
456                 if (key.isInt() || key.isInt64() || key.isDouble())
457                 {
458                     const int i = key;
459                     while (array->size() < i)
460                         array->add (var::undefined());
461 
462                     array->set (i, newValue);
463                     return;
464                 }
465             }
466 
467             if (auto* o = arrayVar.getDynamicObject())
468             {
469                 if (key.isString())
470                 {
471                     o->setProperty (Identifier (key), newValue);
472                     return;
473                 }
474             }
475 
476             Expression::assign (s, newValue);
477         }
478 
479         ExpPtr object, index;
480     };
481 
482     struct BinaryOperatorBase  : public Expression
483     {
BinaryOperatorBasejuce::JavascriptEngine::RootObject::BinaryOperatorBase484         BinaryOperatorBase (const CodeLocation& l, ExpPtr& a, ExpPtr& b, TokenType op) noexcept
485             : Expression (l), lhs (a.release()), rhs (b.release()), operation (op) {}
486 
487         ExpPtr lhs, rhs;
488         TokenType operation;
489     };
490 
491     struct BinaryOperator  : public BinaryOperatorBase
492     {
BinaryOperatorjuce::JavascriptEngine::RootObject::BinaryOperator493         BinaryOperator (const CodeLocation& l, ExpPtr& a, ExpPtr& b, TokenType op) noexcept
494             : BinaryOperatorBase (l, a, b, op) {}
495 
getWithUndefinedArgjuce::JavascriptEngine::RootObject::BinaryOperator496         virtual var getWithUndefinedArg() const                           { return var::undefined(); }
getWithDoublesjuce::JavascriptEngine::RootObject::BinaryOperator497         virtual var getWithDoubles (double, double) const                 { return throwError ("Double"); }
getWithIntsjuce::JavascriptEngine::RootObject::BinaryOperator498         virtual var getWithInts (int64, int64) const                      { return throwError ("Integer"); }
getWithArrayOrObjectjuce::JavascriptEngine::RootObject::BinaryOperator499         virtual var getWithArrayOrObject (const var& a, const var&) const { return throwError (a.isArray() ? "Array" : "Object"); }
getWithStringsjuce::JavascriptEngine::RootObject::BinaryOperator500         virtual var getWithStrings (const String&, const String&) const   { return throwError ("String"); }
501 
getResultjuce::JavascriptEngine::RootObject::BinaryOperator502         var getResult (const Scope& s) const override
503         {
504             var a (lhs->getResult (s)), b (rhs->getResult (s));
505 
506             if ((a.isUndefined() || a.isVoid()) && (b.isUndefined() || b.isVoid()))
507                 return getWithUndefinedArg();
508 
509             if (isNumericOrUndefined (a) && isNumericOrUndefined (b))
510                 return (a.isDouble() || b.isDouble()) ? getWithDoubles (a, b) : getWithInts (a, b);
511 
512             if (a.isArray() || a.isObject())
513                 return getWithArrayOrObject (a, b);
514 
515             return getWithStrings (a.toString(), b.toString());
516         }
517 
throwErrorjuce::JavascriptEngine::RootObject::BinaryOperator518         var throwError (const char* typeName) const
519             { location.throwError (getTokenName (operation) + " is not allowed on the " + typeName + " type"); return {}; }
520     };
521 
522     struct EqualsOp  : public BinaryOperator
523     {
EqualsOpjuce::JavascriptEngine::RootObject::EqualsOp524         EqualsOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::equals) {}
getWithUndefinedArgjuce::JavascriptEngine::RootObject::EqualsOp525         var getWithUndefinedArg() const override                               { return true; }
getWithDoublesjuce::JavascriptEngine::RootObject::EqualsOp526         var getWithDoubles (double a, double b) const override                 { return a == b; }
getWithIntsjuce::JavascriptEngine::RootObject::EqualsOp527         var getWithInts (int64 a, int64 b) const override                      { return a == b; }
getWithStringsjuce::JavascriptEngine::RootObject::EqualsOp528         var getWithStrings (const String& a, const String& b) const override   { return a == b; }
getWithArrayOrObjectjuce::JavascriptEngine::RootObject::EqualsOp529         var getWithArrayOrObject (const var& a, const var& b) const override   { return a == b; }
530     };
531 
532     struct NotEqualsOp  : public BinaryOperator
533     {
NotEqualsOpjuce::JavascriptEngine::RootObject::NotEqualsOp534         NotEqualsOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::notEquals) {}
getWithUndefinedArgjuce::JavascriptEngine::RootObject::NotEqualsOp535         var getWithUndefinedArg() const override                               { return false; }
getWithDoublesjuce::JavascriptEngine::RootObject::NotEqualsOp536         var getWithDoubles (double a, double b) const override                 { return a != b; }
getWithIntsjuce::JavascriptEngine::RootObject::NotEqualsOp537         var getWithInts (int64 a, int64 b) const override                      { return a != b; }
getWithStringsjuce::JavascriptEngine::RootObject::NotEqualsOp538         var getWithStrings (const String& a, const String& b) const override   { return a != b; }
getWithArrayOrObjectjuce::JavascriptEngine::RootObject::NotEqualsOp539         var getWithArrayOrObject (const var& a, const var& b) const override   { return a != b; }
540     };
541 
542     struct LessThanOp  : public BinaryOperator
543     {
LessThanOpjuce::JavascriptEngine::RootObject::LessThanOp544         LessThanOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::lessThan) {}
getWithDoublesjuce::JavascriptEngine::RootObject::LessThanOp545         var getWithDoubles (double a, double b) const override                 { return a < b; }
getWithIntsjuce::JavascriptEngine::RootObject::LessThanOp546         var getWithInts (int64 a, int64 b) const override                      { return a < b; }
getWithStringsjuce::JavascriptEngine::RootObject::LessThanOp547         var getWithStrings (const String& a, const String& b) const override   { return a < b; }
548     };
549 
550     struct LessThanOrEqualOp  : public BinaryOperator
551     {
LessThanOrEqualOpjuce::JavascriptEngine::RootObject::LessThanOrEqualOp552         LessThanOrEqualOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::lessThanOrEqual) {}
getWithDoublesjuce::JavascriptEngine::RootObject::LessThanOrEqualOp553         var getWithDoubles (double a, double b) const override                 { return a <= b; }
getWithIntsjuce::JavascriptEngine::RootObject::LessThanOrEqualOp554         var getWithInts (int64 a, int64 b) const override                      { return a <= b; }
getWithStringsjuce::JavascriptEngine::RootObject::LessThanOrEqualOp555         var getWithStrings (const String& a, const String& b) const override   { return a <= b; }
556     };
557 
558     struct GreaterThanOp  : public BinaryOperator
559     {
GreaterThanOpjuce::JavascriptEngine::RootObject::GreaterThanOp560         GreaterThanOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::greaterThan) {}
getWithDoublesjuce::JavascriptEngine::RootObject::GreaterThanOp561         var getWithDoubles (double a, double b) const override                 { return a > b; }
getWithIntsjuce::JavascriptEngine::RootObject::GreaterThanOp562         var getWithInts (int64 a, int64 b) const override                      { return a > b; }
getWithStringsjuce::JavascriptEngine::RootObject::GreaterThanOp563         var getWithStrings (const String& a, const String& b) const override   { return a > b; }
564     };
565 
566     struct GreaterThanOrEqualOp  : public BinaryOperator
567     {
GreaterThanOrEqualOpjuce::JavascriptEngine::RootObject::GreaterThanOrEqualOp568         GreaterThanOrEqualOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::greaterThanOrEqual) {}
getWithDoublesjuce::JavascriptEngine::RootObject::GreaterThanOrEqualOp569         var getWithDoubles (double a, double b) const override                 { return a >= b; }
getWithIntsjuce::JavascriptEngine::RootObject::GreaterThanOrEqualOp570         var getWithInts (int64 a, int64 b) const override                      { return a >= b; }
getWithStringsjuce::JavascriptEngine::RootObject::GreaterThanOrEqualOp571         var getWithStrings (const String& a, const String& b) const override   { return a >= b; }
572     };
573 
574     struct AdditionOp  : public BinaryOperator
575     {
AdditionOpjuce::JavascriptEngine::RootObject::AdditionOp576         AdditionOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::plus) {}
getWithDoublesjuce::JavascriptEngine::RootObject::AdditionOp577         var getWithDoubles (double a, double b) const override                 { return a + b; }
getWithIntsjuce::JavascriptEngine::RootObject::AdditionOp578         var getWithInts (int64 a, int64 b) const override                      { return a + b; }
getWithStringsjuce::JavascriptEngine::RootObject::AdditionOp579         var getWithStrings (const String& a, const String& b) const override   { return a + b; }
580     };
581 
582     struct SubtractionOp  : public BinaryOperator
583     {
SubtractionOpjuce::JavascriptEngine::RootObject::SubtractionOp584         SubtractionOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::minus) {}
getWithDoublesjuce::JavascriptEngine::RootObject::SubtractionOp585         var getWithDoubles (double a, double b) const override { return a - b; }
getWithIntsjuce::JavascriptEngine::RootObject::SubtractionOp586         var getWithInts (int64 a, int64 b) const override      { return a - b; }
587     };
588 
589     struct MultiplyOp  : public BinaryOperator
590     {
MultiplyOpjuce::JavascriptEngine::RootObject::MultiplyOp591         MultiplyOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::times) {}
getWithDoublesjuce::JavascriptEngine::RootObject::MultiplyOp592         var getWithDoubles (double a, double b) const override { return a * b; }
getWithIntsjuce::JavascriptEngine::RootObject::MultiplyOp593         var getWithInts (int64 a, int64 b) const override      { return a * b; }
594     };
595 
596     struct DivideOp  : public BinaryOperator
597     {
DivideOpjuce::JavascriptEngine::RootObject::DivideOp598         DivideOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::divide) {}
getWithDoublesjuce::JavascriptEngine::RootObject::DivideOp599         var getWithDoubles (double a, double b) const override  { return b != 0 ? a / b : std::numeric_limits<double>::infinity(); }
getWithIntsjuce::JavascriptEngine::RootObject::DivideOp600         var getWithInts (int64 a, int64 b) const override       { return b != 0 ? var (a / (double) b) : var (std::numeric_limits<double>::infinity()); }
601     };
602 
603     struct ModuloOp  : public BinaryOperator
604     {
ModuloOpjuce::JavascriptEngine::RootObject::ModuloOp605         ModuloOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::modulo) {}
getWithDoublesjuce::JavascriptEngine::RootObject::ModuloOp606         var getWithDoubles (double a, double b) const override  { return b != 0 ? fmod (a, b) : std::numeric_limits<double>::infinity(); }
getWithIntsjuce::JavascriptEngine::RootObject::ModuloOp607         var getWithInts (int64 a, int64 b) const override       { return b != 0 ? var (a % b) : var (std::numeric_limits<double>::infinity()); }
608     };
609 
610     struct BitwiseOrOp  : public BinaryOperator
611     {
BitwiseOrOpjuce::JavascriptEngine::RootObject::BitwiseOrOp612         BitwiseOrOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::bitwiseOr) {}
getWithIntsjuce::JavascriptEngine::RootObject::BitwiseOrOp613         var getWithInts (int64 a, int64 b) const override   { return a | b; }
614     };
615 
616     struct BitwiseAndOp  : public BinaryOperator
617     {
BitwiseAndOpjuce::JavascriptEngine::RootObject::BitwiseAndOp618         BitwiseAndOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::bitwiseAnd) {}
getWithIntsjuce::JavascriptEngine::RootObject::BitwiseAndOp619         var getWithInts (int64 a, int64 b) const override   { return a & b; }
620     };
621 
622     struct BitwiseXorOp  : public BinaryOperator
623     {
BitwiseXorOpjuce::JavascriptEngine::RootObject::BitwiseXorOp624         BitwiseXorOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::bitwiseXor) {}
getWithIntsjuce::JavascriptEngine::RootObject::BitwiseXorOp625         var getWithInts (int64 a, int64 b) const override   { return a ^ b; }
626     };
627 
628     struct LeftShiftOp  : public BinaryOperator
629     {
LeftShiftOpjuce::JavascriptEngine::RootObject::LeftShiftOp630         LeftShiftOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::leftShift) {}
getWithIntsjuce::JavascriptEngine::RootObject::LeftShiftOp631         var getWithInts (int64 a, int64 b) const override   { return ((int) a) << (int) b; }
632     };
633 
634     struct RightShiftOp  : public BinaryOperator
635     {
RightShiftOpjuce::JavascriptEngine::RootObject::RightShiftOp636         RightShiftOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::rightShift) {}
getWithIntsjuce::JavascriptEngine::RootObject::RightShiftOp637         var getWithInts (int64 a, int64 b) const override   { return ((int) a) >> (int) b; }
638     };
639 
640     struct RightShiftUnsignedOp  : public BinaryOperator
641     {
RightShiftUnsignedOpjuce::JavascriptEngine::RootObject::RightShiftUnsignedOp642         RightShiftUnsignedOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::rightShiftUnsigned) {}
getWithIntsjuce::JavascriptEngine::RootObject::RightShiftUnsignedOp643         var getWithInts (int64 a, int64 b) const override   { return (int) (((uint32) a) >> (int) b); }
644     };
645 
646     struct LogicalAndOp  : public BinaryOperatorBase
647     {
LogicalAndOpjuce::JavascriptEngine::RootObject::LogicalAndOp648         LogicalAndOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperatorBase (l, a, b, TokenTypes::logicalAnd) {}
getResultjuce::JavascriptEngine::RootObject::LogicalAndOp649         var getResult (const Scope& s) const override       { return lhs->getResult (s) && rhs->getResult (s); }
650     };
651 
652     struct LogicalOrOp  : public BinaryOperatorBase
653     {
LogicalOrOpjuce::JavascriptEngine::RootObject::LogicalOrOp654         LogicalOrOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperatorBase (l, a, b, TokenTypes::logicalOr) {}
getResultjuce::JavascriptEngine::RootObject::LogicalOrOp655         var getResult (const Scope& s) const override       { return lhs->getResult (s) || rhs->getResult (s); }
656     };
657 
658     struct TypeEqualsOp  : public BinaryOperatorBase
659     {
TypeEqualsOpjuce::JavascriptEngine::RootObject::TypeEqualsOp660         TypeEqualsOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperatorBase (l, a, b, TokenTypes::typeEquals) {}
getResultjuce::JavascriptEngine::RootObject::TypeEqualsOp661         var getResult (const Scope& s) const override       { return areTypeEqual (lhs->getResult (s), rhs->getResult (s)); }
662     };
663 
664     struct TypeNotEqualsOp  : public BinaryOperatorBase
665     {
TypeNotEqualsOpjuce::JavascriptEngine::RootObject::TypeNotEqualsOp666         TypeNotEqualsOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperatorBase (l, a, b, TokenTypes::typeNotEquals) {}
getResultjuce::JavascriptEngine::RootObject::TypeNotEqualsOp667         var getResult (const Scope& s) const override       { return ! areTypeEqual (lhs->getResult (s), rhs->getResult (s)); }
668     };
669 
670     struct ConditionalOp  : public Expression
671     {
ConditionalOpjuce::JavascriptEngine::RootObject::ConditionalOp672         ConditionalOp (const CodeLocation& l) noexcept : Expression (l) {}
673 
getResultjuce::JavascriptEngine::RootObject::ConditionalOp674         var getResult (const Scope& s) const override              { return (condition->getResult (s) ? trueBranch : falseBranch)->getResult (s); }
assignjuce::JavascriptEngine::RootObject::ConditionalOp675         void assign (const Scope& s, const var& v) const override  { (condition->getResult (s) ? trueBranch : falseBranch)->assign (s, v); }
676 
677         ExpPtr condition, trueBranch, falseBranch;
678     };
679 
680     struct Assignment  : public Expression
681     {
Assignmentjuce::JavascriptEngine::RootObject::Assignment682         Assignment (const CodeLocation& l, ExpPtr& dest, ExpPtr& source) noexcept : Expression (l), target (dest.release()), newValue (source.release()) {}
683 
getResultjuce::JavascriptEngine::RootObject::Assignment684         var getResult (const Scope& s) const override
685         {
686             auto value = newValue->getResult (s);
687             target->assign (s, value);
688             return value;
689         }
690 
691         ExpPtr target, newValue;
692     };
693 
694     struct SelfAssignment  : public Expression
695     {
SelfAssignmentjuce::JavascriptEngine::RootObject::SelfAssignment696         SelfAssignment (const CodeLocation& l, Expression* dest, Expression* source) noexcept
697             : Expression (l), target (dest), newValue (source) {}
698 
getResultjuce::JavascriptEngine::RootObject::SelfAssignment699         var getResult (const Scope& s) const override
700         {
701             auto value = newValue->getResult (s);
702             target->assign (s, value);
703             return value;
704         }
705 
706         Expression* target; // Careful! this pointer aliases a sub-term of newValue!
707         ExpPtr newValue;
708         TokenType op;
709     };
710 
711     struct PostAssignment  : public SelfAssignment
712     {
PostAssignmentjuce::JavascriptEngine::RootObject::PostAssignment713         PostAssignment (const CodeLocation& l, Expression* dest, Expression* source) noexcept : SelfAssignment (l, dest, source) {}
714 
getResultjuce::JavascriptEngine::RootObject::PostAssignment715         var getResult (const Scope& s) const override
716         {
717             auto oldValue = target->getResult (s);
718             target->assign (s, newValue->getResult (s));
719             return oldValue;
720         }
721     };
722 
723     struct FunctionCall  : public Expression
724     {
FunctionCalljuce::JavascriptEngine::RootObject::FunctionCall725         FunctionCall (const CodeLocation& l) noexcept : Expression (l) {}
726 
getResultjuce::JavascriptEngine::RootObject::FunctionCall727         var getResult (const Scope& s) const override
728         {
729             if (auto* dot = dynamic_cast<DotOperator*> (object.get()))
730             {
731                 auto thisObject = dot->parent->getResult (s);
732                 return invokeFunction (s, s.findFunctionCall (location, thisObject, dot->child), thisObject);
733             }
734 
735             auto function = object->getResult (s);
736             return invokeFunction (s, function, var (s.scope.get()));
737         }
738 
invokeFunctionjuce::JavascriptEngine::RootObject::FunctionCall739         var invokeFunction (const Scope& s, const var& function, const var& thisObject) const
740         {
741             s.checkTimeOut (location);
742             Array<var> argVars;
743 
744             for (auto* a : arguments)
745                 argVars.add (a->getResult (s));
746 
747             const var::NativeFunctionArgs args (thisObject, argVars.begin(), argVars.size());
748 
749             if (var::NativeFunction nativeFunction = function.getNativeFunction())
750                 return nativeFunction (args);
751 
752             if (auto* fo = dynamic_cast<FunctionObject*> (function.getObject()))
753                 return fo->invoke (s, args);
754 
755             if (auto* dot = dynamic_cast<DotOperator*> (object.get()))
756                 if (auto* o = thisObject.getDynamicObject())
757                     if (o->hasMethod (dot->child)) // allow an overridden DynamicObject::invokeMethod to accept a method call.
758                         return o->invokeMethod (dot->child, args);
759 
760             location.throwError ("This expression is not a function!"); return {};
761         }
762 
763         ExpPtr object;
764         OwnedArray<Expression> arguments;
765     };
766 
767     struct NewOperator  : public FunctionCall
768     {
NewOperatorjuce::JavascriptEngine::RootObject::NewOperator769         NewOperator (const CodeLocation& l) noexcept : FunctionCall (l) {}
770 
getResultjuce::JavascriptEngine::RootObject::NewOperator771         var getResult (const Scope& s) const override
772         {
773             var classOrFunc = object->getResult (s);
774             const bool isFunc = isFunction (classOrFunc);
775 
776             if (! (isFunc || classOrFunc.getDynamicObject() != nullptr))
777                 return var::undefined();
778 
779             DynamicObject::Ptr newObject (new DynamicObject());
780 
781             if (isFunc)
782                 invokeFunction (s, classOrFunc, newObject.get());
783             else
784                 newObject->setProperty (getPrototypeIdentifier(), classOrFunc);
785 
786             return newObject.get();
787         }
788     };
789 
790     struct ObjectDeclaration  : public Expression
791     {
ObjectDeclarationjuce::JavascriptEngine::RootObject::ObjectDeclaration792         ObjectDeclaration (const CodeLocation& l) noexcept : Expression (l) {}
793 
getResultjuce::JavascriptEngine::RootObject::ObjectDeclaration794         var getResult (const Scope& s) const override
795         {
796             DynamicObject::Ptr newObject (new DynamicObject());
797 
798             for (int i = 0; i < names.size(); ++i)
799                 newObject->setProperty (names.getUnchecked(i), initialisers.getUnchecked(i)->getResult (s));
800 
801             return newObject.get();
802         }
803 
804         Array<Identifier> names;
805         OwnedArray<Expression> initialisers;
806     };
807 
808     struct ArrayDeclaration  : public Expression
809     {
ArrayDeclarationjuce::JavascriptEngine::RootObject::ArrayDeclaration810         ArrayDeclaration (const CodeLocation& l) noexcept : Expression (l) {}
811 
getResultjuce::JavascriptEngine::RootObject::ArrayDeclaration812         var getResult (const Scope& s) const override
813         {
814             Array<var> a;
815 
816             for (int i = 0; i < values.size(); ++i)
817                 a.add (values.getUnchecked(i)->getResult (s));
818 
819             // std::move() needed here for older compilers
820             return std::move (a);
821         }
822 
823         OwnedArray<Expression> values;
824     };
825 
826     //==============================================================================
827     struct FunctionObject  : public DynamicObject
828     {
FunctionObjectjuce::JavascriptEngine::RootObject::FunctionObject829         FunctionObject() noexcept {}
830 
FunctionObjectjuce::JavascriptEngine::RootObject::FunctionObject831         FunctionObject (const FunctionObject& other)  : DynamicObject(), functionCode (other.functionCode)
832         {
833             ExpressionTreeBuilder tb (functionCode);
834             tb.parseFunctionParamsAndBody (*this);
835         }
836 
clonejuce::JavascriptEngine::RootObject::FunctionObject837         DynamicObject::Ptr clone() override    { return *new FunctionObject (*this); }
838 
writeAsJSONjuce::JavascriptEngine::RootObject::FunctionObject839         void writeAsJSON (OutputStream& out, int /*indentLevel*/, bool /*allOnOneLine*/, int /*maximumDecimalPlaces*/) override
840         {
841             out << "function " << functionCode;
842         }
843 
invokejuce::JavascriptEngine::RootObject::FunctionObject844         var invoke (const Scope& s, const var::NativeFunctionArgs& args) const
845         {
846             DynamicObject::Ptr functionRoot (new DynamicObject());
847 
848             static const Identifier thisIdent ("this");
849             functionRoot->setProperty (thisIdent, args.thisObject);
850 
851             for (int i = 0; i < parameters.size(); ++i)
852                 functionRoot->setProperty (parameters.getReference(i),
853                                            i < args.numArguments ? args.arguments[i] : var::undefined());
854 
855             var result;
856             body->perform (Scope (&s, s.root, functionRoot), &result);
857             return result;
858         }
859 
860         String functionCode;
861         Array<Identifier> parameters;
862         std::unique_ptr<Statement> body;
863     };
864 
865     //==============================================================================
866     struct TokenIterator
867     {
TokenIteratorjuce::JavascriptEngine::RootObject::TokenIterator868         TokenIterator (const String& code) : location (code), p (code.getCharPointer()) { skip(); }
869 
skipjuce::JavascriptEngine::RootObject::TokenIterator870         void skip()
871         {
872             skipWhitespaceAndComments();
873             location.location = p;
874             currentType = matchNextToken();
875         }
876 
matchjuce::JavascriptEngine::RootObject::TokenIterator877         void match (TokenType expected)
878         {
879             if (currentType != expected)
880                 location.throwError ("Found " + getTokenName (currentType) + " when expecting " + getTokenName (expected));
881 
882             skip();
883         }
884 
matchIfjuce::JavascriptEngine::RootObject::TokenIterator885         bool matchIf (TokenType expected)                                 { if (currentType == expected)  { skip(); return true; } return false; }
matchesAnyjuce::JavascriptEngine::RootObject::TokenIterator886         bool matchesAny (TokenType t1, TokenType t2) const                { return currentType == t1 || currentType == t2; }
matchesAnyjuce::JavascriptEngine::RootObject::TokenIterator887         bool matchesAny (TokenType t1, TokenType t2, TokenType t3) const  { return matchesAny (t1, t2) || currentType == t3; }
888 
889         CodeLocation location;
890         TokenType currentType;
891         var currentValue;
892 
893     private:
894         String::CharPointerType p;
895 
isIdentifierStartjuce::JavascriptEngine::RootObject::TokenIterator896         static bool isIdentifierStart (juce_wchar c) noexcept   { return CharacterFunctions::isLetter (c)        || c == '_'; }
isIdentifierBodyjuce::JavascriptEngine::RootObject::TokenIterator897         static bool isIdentifierBody  (juce_wchar c) noexcept   { return CharacterFunctions::isLetterOrDigit (c) || c == '_'; }
898 
matchNextTokenjuce::JavascriptEngine::RootObject::TokenIterator899         TokenType matchNextToken()
900         {
901             if (isIdentifierStart (*p))
902             {
903                 auto end = p;
904                 while (isIdentifierBody (*++end)) {}
905 
906                 auto len = (size_t) (end - p);
907                 #define JUCE_JS_COMPARE_KEYWORD(name, str) if (len == sizeof (str) - 1 && matchToken (TokenTypes::name, len)) return TokenTypes::name;
908                 JUCE_JS_KEYWORDS (JUCE_JS_COMPARE_KEYWORD)
909 
910                 currentValue = String (p, end); p = end;
911                 return TokenTypes::identifier;
912             }
913 
914             if (p.isDigit())
915             {
916                 if (parseHexLiteral() || parseFloatLiteral() || parseOctalLiteral() || parseDecimalLiteral())
917                     return TokenTypes::literal;
918 
919                 location.throwError ("Syntax error in numeric constant");
920             }
921 
922             if (parseStringLiteral (*p) || (*p == '.' && parseFloatLiteral()))
923                 return TokenTypes::literal;
924 
925             #define JUCE_JS_COMPARE_OPERATOR(name, str) if (matchToken (TokenTypes::name, sizeof (str) - 1)) return TokenTypes::name;
926             JUCE_JS_OPERATORS (JUCE_JS_COMPARE_OPERATOR)
927 
928             if (! p.isEmpty())
929                 location.throwError ("Unexpected character '" + String::charToString (*p) + "' in source");
930 
931             return TokenTypes::eof;
932         }
933 
matchTokenjuce::JavascriptEngine::RootObject::TokenIterator934         bool matchToken (TokenType name, size_t len) noexcept
935         {
936             if (p.compareUpTo (CharPointer_ASCII (name), (int) len) != 0) return false;
937             p += (int) len;  return true;
938         }
939 
skipWhitespaceAndCommentsjuce::JavascriptEngine::RootObject::TokenIterator940         void skipWhitespaceAndComments()
941         {
942             for (;;)
943             {
944                 p = p.findEndOfWhitespace();
945 
946                 if (*p == '/')
947                 {
948                     auto c2 = p[1];
949 
950                     if (c2 == '/')  { p = CharacterFunctions::find (p, (juce_wchar) '\n'); continue; }
951 
952                     if (c2 == '*')
953                     {
954                         location.location = p;
955                         p = CharacterFunctions::find (p + 2, CharPointer_ASCII ("*/"));
956                         if (p.isEmpty()) location.throwError ("Unterminated '/*' comment");
957                         p += 2; continue;
958                     }
959                 }
960 
961                 break;
962             }
963         }
964 
parseStringLiteraljuce::JavascriptEngine::RootObject::TokenIterator965         bool parseStringLiteral (juce_wchar quoteType)
966         {
967             if (quoteType != '"' && quoteType != '\'')
968                 return false;
969 
970             auto r = JSON::parseQuotedString (p, currentValue);
971             if (r.failed()) location.throwError (r.getErrorMessage());
972             return true;
973         }
974 
parseHexLiteraljuce::JavascriptEngine::RootObject::TokenIterator975         bool parseHexLiteral()
976         {
977             if (*p != '0' || (p[1] != 'x' && p[1] != 'X')) return false;
978 
979             auto t = ++p;
980             int64 v = CharacterFunctions::getHexDigitValue (*++t);
981             if (v < 0) return false;
982 
983             for (;;)
984             {
985                 auto digit = CharacterFunctions::getHexDigitValue (*++t);
986                 if (digit < 0) break;
987                 v = v * 16 + digit;
988             }
989 
990             currentValue = v; p = t;
991             return true;
992         }
993 
parseFloatLiteraljuce::JavascriptEngine::RootObject::TokenIterator994         bool parseFloatLiteral()
995         {
996             int numDigits = 0;
997             auto t = p;
998             while (t.isDigit())  { ++t; ++numDigits; }
999 
1000             const bool hasPoint = (*t == '.');
1001 
1002             if (hasPoint)
1003                 while ((++t).isDigit())  ++numDigits;
1004 
1005             if (numDigits == 0)
1006                 return false;
1007 
1008             auto c = *t;
1009             const bool hasExponent = (c == 'e' || c == 'E');
1010 
1011             if (hasExponent)
1012             {
1013                 c = *++t;
1014                 if (c == '+' || c == '-')  ++t;
1015                 if (! t.isDigit()) return false;
1016                 while ((++t).isDigit()) {}
1017             }
1018 
1019             if (! (hasExponent || hasPoint)) return false;
1020 
1021             currentValue = CharacterFunctions::getDoubleValue (p);  p = t;
1022             return true;
1023         }
1024 
parseOctalLiteraljuce::JavascriptEngine::RootObject::TokenIterator1025         bool parseOctalLiteral()
1026         {
1027             auto t = p;
1028             int64 v = *t - '0';
1029             if (v != 0) return false;  // first digit of octal must be 0
1030 
1031             for (;;)
1032             {
1033                 auto digit = (int) (*++t - '0');
1034                 if (isPositiveAndBelow (digit, 8))        v = v * 8 + digit;
1035                 else if (isPositiveAndBelow (digit, 10))  location.throwError ("Decimal digit in octal constant");
1036                 else break;
1037             }
1038 
1039             currentValue = v;  p = t;
1040             return true;
1041         }
1042 
parseDecimalLiteraljuce::JavascriptEngine::RootObject::TokenIterator1043         bool parseDecimalLiteral()
1044         {
1045             int64 v = 0;
1046 
1047             for (;; ++p)
1048             {
1049                 auto digit = (int) (*p - '0');
1050                 if (isPositiveAndBelow (digit, 10))  v = v * 10 + digit;
1051                 else break;
1052             }
1053 
1054             currentValue = v;
1055             return true;
1056         }
1057     };
1058 
1059     //==============================================================================
1060     struct ExpressionTreeBuilder  : private TokenIterator
1061     {
ExpressionTreeBuilderjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1062         ExpressionTreeBuilder (const String code)  : TokenIterator (code) {}
1063 
parseStatementListjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1064         BlockStatement* parseStatementList()
1065         {
1066             std::unique_ptr<BlockStatement> b (new BlockStatement (location));
1067 
1068             while (currentType != TokenTypes::closeBrace && currentType != TokenTypes::eof)
1069                 b->statements.add (parseStatement());
1070 
1071             return b.release();
1072         }
1073 
parseFunctionParamsAndBodyjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1074         void parseFunctionParamsAndBody (FunctionObject& fo)
1075         {
1076             match (TokenTypes::openParen);
1077 
1078             while (currentType != TokenTypes::closeParen)
1079             {
1080                 auto paramName = currentValue.toString();
1081                 match (TokenTypes::identifier);
1082                 fo.parameters.add (paramName);
1083 
1084                 if (currentType != TokenTypes::closeParen)
1085                     match (TokenTypes::comma);
1086             }
1087 
1088             match (TokenTypes::closeParen);
1089             fo.body.reset (parseBlock());
1090         }
1091 
parseExpressionjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1092         Expression* parseExpression()
1093         {
1094             ExpPtr lhs (parseLogicOperator());
1095 
1096             if (matchIf (TokenTypes::question))          return parseTernaryOperator (lhs);
1097             if (matchIf (TokenTypes::assign))            { ExpPtr rhs (parseExpression()); return new Assignment (location, lhs, rhs); }
1098             if (matchIf (TokenTypes::plusEquals))        return parseInPlaceOpExpression<AdditionOp> (lhs);
1099             if (matchIf (TokenTypes::minusEquals))       return parseInPlaceOpExpression<SubtractionOp> (lhs);
1100             if (matchIf (TokenTypes::timesEquals))       return parseInPlaceOpExpression<MultiplyOp> (lhs);
1101             if (matchIf (TokenTypes::divideEquals))      return parseInPlaceOpExpression<DivideOp> (lhs);
1102             if (matchIf (TokenTypes::moduloEquals))      return parseInPlaceOpExpression<ModuloOp> (lhs);
1103             if (matchIf (TokenTypes::leftShiftEquals))   return parseInPlaceOpExpression<LeftShiftOp> (lhs);
1104             if (matchIf (TokenTypes::rightShiftEquals))  return parseInPlaceOpExpression<RightShiftOp> (lhs);
1105 
1106             return lhs.release();
1107         }
1108 
1109     private:
throwErrorjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1110         void throwError (const String& err) const  { location.throwError (err); }
1111 
1112         template <typename OpType>
parseInPlaceOpExpressionjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1113         Expression* parseInPlaceOpExpression (ExpPtr& lhs)
1114         {
1115             ExpPtr rhs (parseExpression());
1116             Expression* bareLHS = lhs.get(); // careful - bare pointer is deliberately aliased
1117             return new SelfAssignment (location, bareLHS, new OpType (location, lhs, rhs));
1118         }
1119 
parseBlockjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1120         BlockStatement* parseBlock()
1121         {
1122             match (TokenTypes::openBrace);
1123             std::unique_ptr<BlockStatement> b (parseStatementList());
1124             match (TokenTypes::closeBrace);
1125             return b.release();
1126         }
1127 
parseStatementjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1128         Statement* parseStatement()
1129         {
1130             if (currentType == TokenTypes::openBrace)   return parseBlock();
1131             if (matchIf (TokenTypes::var))              return parseVar();
1132             if (matchIf (TokenTypes::if_))              return parseIf();
1133             if (matchIf (TokenTypes::while_))           return parseDoOrWhileLoop (false);
1134             if (matchIf (TokenTypes::do_))              return parseDoOrWhileLoop (true);
1135             if (matchIf (TokenTypes::for_))             return parseForLoop();
1136             if (matchIf (TokenTypes::return_))          return parseReturn();
1137             if (matchIf (TokenTypes::break_))           return new BreakStatement (location);
1138             if (matchIf (TokenTypes::continue_))        return new ContinueStatement (location);
1139             if (matchIf (TokenTypes::function))         return parseFunction();
1140             if (matchIf (TokenTypes::semicolon))        return new Statement (location);
1141             if (matchIf (TokenTypes::plusplus))         return parsePreIncDec<AdditionOp>();
1142             if (matchIf (TokenTypes::minusminus))       return parsePreIncDec<SubtractionOp>();
1143 
1144             if (matchesAny (TokenTypes::openParen, TokenTypes::openBracket))
1145                 return matchEndOfStatement (parseFactor());
1146 
1147             if (matchesAny (TokenTypes::identifier, TokenTypes::literal, TokenTypes::minus))
1148                 return matchEndOfStatement (parseExpression());
1149 
1150             throwError ("Found " + getTokenName (currentType) + " when expecting a statement");
1151             return nullptr;
1152         }
1153 
matchEndOfStatementjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1154         Expression* matchEndOfStatement (Expression* ex)  { ExpPtr e (ex); if (currentType != TokenTypes::eof) match (TokenTypes::semicolon); return e.release(); }
matchCloseParenjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1155         Expression* matchCloseParen (Expression* ex)      { ExpPtr e (ex); match (TokenTypes::closeParen); return e.release(); }
1156 
parseIfjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1157         Statement* parseIf()
1158         {
1159             std::unique_ptr<IfStatement> s (new IfStatement (location));
1160             match (TokenTypes::openParen);
1161             s->condition.reset (parseExpression());
1162             match (TokenTypes::closeParen);
1163             s->trueBranch.reset (parseStatement());
1164             s->falseBranch.reset (matchIf (TokenTypes::else_) ? parseStatement() : new Statement (location));
1165             return s.release();
1166         }
1167 
parseReturnjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1168         Statement* parseReturn()
1169         {
1170             if (matchIf (TokenTypes::semicolon))
1171                 return new ReturnStatement (location, new Expression (location));
1172 
1173             auto* r = new ReturnStatement (location, parseExpression());
1174             matchIf (TokenTypes::semicolon);
1175             return r;
1176         }
1177 
parseVarjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1178         Statement* parseVar()
1179         {
1180             std::unique_ptr<VarStatement> s (new VarStatement (location));
1181             s->name = parseIdentifier();
1182             s->initialiser.reset (matchIf (TokenTypes::assign) ? parseExpression() : new Expression (location));
1183 
1184             if (matchIf (TokenTypes::comma))
1185             {
1186                 std::unique_ptr<BlockStatement> block (new BlockStatement (location));
1187                 block->statements.add (std::move (s));
1188                 block->statements.add (parseVar());
1189                 return block.release();
1190             }
1191 
1192             match (TokenTypes::semicolon);
1193             return s.release();
1194         }
1195 
parseFunctionjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1196         Statement* parseFunction()
1197         {
1198             Identifier name;
1199             auto fn = parseFunctionDefinition (name);
1200 
1201             if (name.isNull())
1202                 throwError ("Functions defined at statement-level must have a name");
1203 
1204             ExpPtr nm (new UnqualifiedName (location, name)), value (new LiteralValue (location, fn));
1205             return new Assignment (location, nm, value);
1206         }
1207 
parseForLoopjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1208         Statement* parseForLoop()
1209         {
1210             std::unique_ptr<LoopStatement> s (new LoopStatement (location, false));
1211             match (TokenTypes::openParen);
1212             s->initialiser.reset (parseStatement());
1213 
1214             if (matchIf (TokenTypes::semicolon))
1215                 s->condition.reset (new LiteralValue (location, true));
1216             else
1217             {
1218                 s->condition.reset (parseExpression());
1219                 match (TokenTypes::semicolon);
1220             }
1221 
1222             if (matchIf (TokenTypes::closeParen))
1223                 s->iterator.reset (new Statement (location));
1224             else
1225             {
1226                 s->iterator.reset (parseExpression());
1227                 match (TokenTypes::closeParen);
1228             }
1229 
1230             s->body.reset (parseStatement());
1231             return s.release();
1232         }
1233 
parseDoOrWhileLoopjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1234         Statement* parseDoOrWhileLoop (bool isDoLoop)
1235         {
1236             std::unique_ptr<LoopStatement> s (new LoopStatement (location, isDoLoop));
1237             s->initialiser.reset (new Statement (location));
1238             s->iterator.reset (new Statement (location));
1239 
1240             if (isDoLoop)
1241             {
1242                 s->body.reset (parseBlock());
1243                 match (TokenTypes::while_);
1244             }
1245 
1246             match (TokenTypes::openParen);
1247             s->condition.reset (parseExpression());
1248             match (TokenTypes::closeParen);
1249 
1250             if (! isDoLoop)
1251                 s->body.reset (parseStatement());
1252 
1253             return s.release();
1254         }
1255 
parseIdentifierjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1256         Identifier parseIdentifier()
1257         {
1258             Identifier i;
1259             if (currentType == TokenTypes::identifier)
1260                 i = currentValue.toString();
1261 
1262             match (TokenTypes::identifier);
1263             return i;
1264         }
1265 
parseFunctionDefinitionjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1266         var parseFunctionDefinition (Identifier& functionName)
1267         {
1268             auto functionStart = location.location;
1269 
1270             if (currentType == TokenTypes::identifier)
1271                 functionName = parseIdentifier();
1272 
1273             std::unique_ptr<FunctionObject> fo (new FunctionObject());
1274             parseFunctionParamsAndBody (*fo);
1275             fo->functionCode = String (functionStart, location.location);
1276             return var (fo.release());
1277         }
1278 
parseFunctionCalljuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1279         Expression* parseFunctionCall (FunctionCall* call, ExpPtr& function)
1280         {
1281             std::unique_ptr<FunctionCall> s (call);
1282             s->object.reset (function.release());
1283             match (TokenTypes::openParen);
1284 
1285             while (currentType != TokenTypes::closeParen)
1286             {
1287                 s->arguments.add (parseExpression());
1288                 if (currentType != TokenTypes::closeParen)
1289                     match (TokenTypes::comma);
1290             }
1291 
1292             return matchCloseParen (s.release());
1293         }
1294 
parseSuffixesjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1295         Expression* parseSuffixes (Expression* e)
1296         {
1297             ExpPtr input (e);
1298 
1299             if (matchIf (TokenTypes::dot))
1300                 return parseSuffixes (new DotOperator (location, input, parseIdentifier()));
1301 
1302             if (currentType == TokenTypes::openParen)
1303                 return parseSuffixes (parseFunctionCall (new FunctionCall (location), input));
1304 
1305             if (matchIf (TokenTypes::openBracket))
1306             {
1307                 std::unique_ptr<ArraySubscript> s (new ArraySubscript (location));
1308                 s->object.reset (input.release());
1309                 s->index.reset (parseExpression());
1310                 match (TokenTypes::closeBracket);
1311                 return parseSuffixes (s.release());
1312             }
1313 
1314             if (matchIf (TokenTypes::plusplus))   return parsePostIncDec<AdditionOp> (input);
1315             if (matchIf (TokenTypes::minusminus)) return parsePostIncDec<SubtractionOp> (input);
1316 
1317             return input.release();
1318         }
1319 
parseFactorjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1320         Expression* parseFactor()
1321         {
1322             if (currentType == TokenTypes::identifier)  return parseSuffixes (new UnqualifiedName (location, parseIdentifier()));
1323             if (matchIf (TokenTypes::openParen))        return parseSuffixes (matchCloseParen (parseExpression()));
1324             if (matchIf (TokenTypes::true_))            return parseSuffixes (new LiteralValue (location, (int) 1));
1325             if (matchIf (TokenTypes::false_))           return parseSuffixes (new LiteralValue (location, (int) 0));
1326             if (matchIf (TokenTypes::null_))            return parseSuffixes (new LiteralValue (location, var()));
1327             if (matchIf (TokenTypes::undefined))        return parseSuffixes (new Expression (location));
1328 
1329             if (currentType == TokenTypes::literal)
1330             {
1331                 var v (currentValue); skip();
1332                 return parseSuffixes (new LiteralValue (location, v));
1333             }
1334 
1335             if (matchIf (TokenTypes::openBrace))
1336             {
1337                 std::unique_ptr<ObjectDeclaration> e (new ObjectDeclaration (location));
1338 
1339                 while (currentType != TokenTypes::closeBrace)
1340                 {
1341                     auto memberName = currentValue.toString();
1342                     match ((currentType == TokenTypes::literal && currentValue.isString())
1343                              ? TokenTypes::literal : TokenTypes::identifier);
1344                     match (TokenTypes::colon);
1345 
1346                     e->names.add (memberName);
1347                     e->initialisers.add (parseExpression());
1348 
1349                     if (currentType != TokenTypes::closeBrace)
1350                         match (TokenTypes::comma);
1351                 }
1352 
1353                 match (TokenTypes::closeBrace);
1354                 return parseSuffixes (e.release());
1355             }
1356 
1357             if (matchIf (TokenTypes::openBracket))
1358             {
1359                 std::unique_ptr<ArrayDeclaration> e (new ArrayDeclaration (location));
1360 
1361                 while (currentType != TokenTypes::closeBracket)
1362                 {
1363                     e->values.add (parseExpression());
1364 
1365                     if (currentType != TokenTypes::closeBracket)
1366                         match (TokenTypes::comma);
1367                 }
1368 
1369                 match (TokenTypes::closeBracket);
1370                 return parseSuffixes (e.release());
1371             }
1372 
1373             if (matchIf (TokenTypes::function))
1374             {
1375                 Identifier name;
1376                 var fn = parseFunctionDefinition (name);
1377 
1378                 if (name.isValid())
1379                     throwError ("Inline functions definitions cannot have a name");
1380 
1381                 return new LiteralValue (location, fn);
1382             }
1383 
1384             if (matchIf (TokenTypes::new_))
1385             {
1386                 ExpPtr name (new UnqualifiedName (location, parseIdentifier()));
1387 
1388                 while (matchIf (TokenTypes::dot))
1389                     name.reset (new DotOperator (location, name, parseIdentifier()));
1390 
1391                 return parseFunctionCall (new NewOperator (location), name);
1392             }
1393 
1394             throwError ("Found " + getTokenName (currentType) + " when expecting an expression");
1395             return nullptr;
1396         }
1397 
1398         template <typename OpType>
parsePreIncDecjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1399         Expression* parsePreIncDec()
1400         {
1401             Expression* e = parseFactor(); // careful - bare pointer is deliberately aliased
1402             ExpPtr lhs (e), one (new LiteralValue (location, (int) 1));
1403             return new SelfAssignment (location, e, new OpType (location, lhs, one));
1404         }
1405 
1406         template <typename OpType>
parsePostIncDecjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1407         Expression* parsePostIncDec (ExpPtr& lhs)
1408         {
1409             Expression* e = lhs.release(); // careful - bare pointer is deliberately aliased
1410             ExpPtr lhs2 (e), one (new LiteralValue (location, (int) 1));
1411             return new PostAssignment (location, e, new OpType (location, lhs2, one));
1412         }
1413 
parseTypeofjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1414         Expression* parseTypeof()
1415         {
1416             std::unique_ptr<FunctionCall> f (new FunctionCall (location));
1417             f->object.reset (new UnqualifiedName (location, "typeof"));
1418             f->arguments.add (parseUnary());
1419             return f.release();
1420         }
1421 
parseUnaryjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1422         Expression* parseUnary()
1423         {
1424             if (matchIf (TokenTypes::minus))       { ExpPtr a (new LiteralValue (location, (int) 0)), b (parseUnary()); return new SubtractionOp   (location, a, b); }
1425             if (matchIf (TokenTypes::logicalNot))  { ExpPtr a (new LiteralValue (location, (int) 0)), b (parseUnary()); return new EqualsOp        (location, a, b); }
1426             if (matchIf (TokenTypes::plusplus))    return parsePreIncDec<AdditionOp>();
1427             if (matchIf (TokenTypes::minusminus))  return parsePreIncDec<SubtractionOp>();
1428             if (matchIf (TokenTypes::typeof_))     return parseTypeof();
1429 
1430             return parseFactor();
1431         }
1432 
parseMultiplyDividejuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1433         Expression* parseMultiplyDivide()
1434         {
1435             ExpPtr a (parseUnary());
1436 
1437             for (;;)
1438             {
1439                 if (matchIf (TokenTypes::times))        { ExpPtr b (parseUnary()); a.reset (new MultiplyOp (location, a, b)); }
1440                 else if (matchIf (TokenTypes::divide))  { ExpPtr b (parseUnary()); a.reset (new DivideOp   (location, a, b)); }
1441                 else if (matchIf (TokenTypes::modulo))  { ExpPtr b (parseUnary()); a.reset (new ModuloOp   (location, a, b)); }
1442                 else break;
1443             }
1444 
1445             return a.release();
1446         }
1447 
parseAdditionSubtractionjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1448         Expression* parseAdditionSubtraction()
1449         {
1450             ExpPtr a (parseMultiplyDivide());
1451 
1452             for (;;)
1453             {
1454                 if (matchIf (TokenTypes::plus))            { ExpPtr b (parseMultiplyDivide()); a.reset (new AdditionOp    (location, a, b)); }
1455                 else if (matchIf (TokenTypes::minus))      { ExpPtr b (parseMultiplyDivide()); a.reset (new SubtractionOp (location, a, b)); }
1456                 else break;
1457             }
1458 
1459             return a.release();
1460         }
1461 
parseShiftOperatorjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1462         Expression* parseShiftOperator()
1463         {
1464             ExpPtr a (parseAdditionSubtraction());
1465 
1466             for (;;)
1467             {
1468                 if (matchIf (TokenTypes::leftShift))                { ExpPtr b (parseExpression()); a.reset (new LeftShiftOp          (location, a, b)); }
1469                 else if (matchIf (TokenTypes::rightShift))          { ExpPtr b (parseExpression()); a.reset (new RightShiftOp         (location, a, b)); }
1470                 else if (matchIf (TokenTypes::rightShiftUnsigned))  { ExpPtr b (parseExpression()); a.reset (new RightShiftUnsignedOp (location, a, b)); }
1471                 else break;
1472             }
1473 
1474             return a.release();
1475         }
1476 
parseComparatorjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1477         Expression* parseComparator()
1478         {
1479             ExpPtr a (parseShiftOperator());
1480 
1481             for (;;)
1482             {
1483                 if (matchIf (TokenTypes::equals))                  { ExpPtr b (parseShiftOperator()); a.reset (new EqualsOp             (location, a, b)); }
1484                 else if (matchIf (TokenTypes::notEquals))          { ExpPtr b (parseShiftOperator()); a.reset (new NotEqualsOp          (location, a, b)); }
1485                 else if (matchIf (TokenTypes::typeEquals))         { ExpPtr b (parseShiftOperator()); a.reset (new TypeEqualsOp         (location, a, b)); }
1486                 else if (matchIf (TokenTypes::typeNotEquals))      { ExpPtr b (parseShiftOperator()); a.reset (new TypeNotEqualsOp      (location, a, b)); }
1487                 else if (matchIf (TokenTypes::lessThan))           { ExpPtr b (parseShiftOperator()); a.reset (new LessThanOp           (location, a, b)); }
1488                 else if (matchIf (TokenTypes::lessThanOrEqual))    { ExpPtr b (parseShiftOperator()); a.reset (new LessThanOrEqualOp    (location, a, b)); }
1489                 else if (matchIf (TokenTypes::greaterThan))        { ExpPtr b (parseShiftOperator()); a.reset (new GreaterThanOp        (location, a, b)); }
1490                 else if (matchIf (TokenTypes::greaterThanOrEqual)) { ExpPtr b (parseShiftOperator()); a.reset (new GreaterThanOrEqualOp (location, a, b)); }
1491                 else break;
1492             }
1493 
1494             return a.release();
1495         }
1496 
parseLogicOperatorjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1497         Expression* parseLogicOperator()
1498         {
1499             ExpPtr a (parseComparator());
1500 
1501             for (;;)
1502             {
1503                 if (matchIf (TokenTypes::logicalAnd))       { ExpPtr b (parseComparator()); a.reset (new LogicalAndOp (location, a, b)); }
1504                 else if (matchIf (TokenTypes::logicalOr))   { ExpPtr b (parseComparator()); a.reset (new LogicalOrOp  (location, a, b)); }
1505                 else if (matchIf (TokenTypes::bitwiseAnd))  { ExpPtr b (parseComparator()); a.reset (new BitwiseAndOp (location, a, b)); }
1506                 else if (matchIf (TokenTypes::bitwiseOr))   { ExpPtr b (parseComparator()); a.reset (new BitwiseOrOp  (location, a, b)); }
1507                 else if (matchIf (TokenTypes::bitwiseXor))  { ExpPtr b (parseComparator()); a.reset (new BitwiseXorOp (location, a, b)); }
1508                 else break;
1509             }
1510 
1511             return a.release();
1512         }
1513 
parseTernaryOperatorjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1514         Expression* parseTernaryOperator (ExpPtr& condition)
1515         {
1516             std::unique_ptr<ConditionalOp> e (new ConditionalOp (location));
1517             e->condition.reset (condition.release());
1518             e->trueBranch.reset (parseExpression());
1519             match (TokenTypes::colon);
1520             e->falseBranch.reset (parseExpression());
1521             return e.release();
1522         }
1523 
1524         JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ExpressionTreeBuilder)
1525     };
1526 
1527     //==============================================================================
getjuce::JavascriptEngine::RootObject1528     static var get (Args a, int index) noexcept            { return index < a.numArguments ? a.arguments[index] : var(); }
isIntjuce::JavascriptEngine::RootObject1529     static bool isInt (Args a, int index) noexcept         { return get (a, index).isInt() || get (a, index).isInt64(); }
getIntjuce::JavascriptEngine::RootObject1530     static int getInt (Args a, int index) noexcept         { return get (a, index); }
getDoublejuce::JavascriptEngine::RootObject1531     static double getDouble (Args a, int index) noexcept   { return get (a, index); }
getStringjuce::JavascriptEngine::RootObject1532     static String getString (Args a, int index) noexcept   { return get (a, index).toString(); }
1533 
1534     //==============================================================================
1535     struct ObjectClass  : public DynamicObject
1536     {
ObjectClassjuce::JavascriptEngine::RootObject::ObjectClass1537         ObjectClass()
1538         {
1539             setMethod ("dump",  dump);
1540             setMethod ("clone", cloneFn);
1541         }
1542 
getClassNamejuce::JavascriptEngine::RootObject::ObjectClass1543         static Identifier getClassName()   { static const Identifier i ("Object"); return i; }
dumpjuce::JavascriptEngine::RootObject::ObjectClass1544         static var dump  (Args a)          { DBG (JSON::toString (a.thisObject)); ignoreUnused (a); return var::undefined(); }
cloneFnjuce::JavascriptEngine::RootObject::ObjectClass1545         static var cloneFn (Args a)        { return a.thisObject.clone(); }
1546     };
1547 
1548     //==============================================================================
1549     struct ArrayClass  : public DynamicObject
1550     {
ArrayClassjuce::JavascriptEngine::RootObject::ArrayClass1551         ArrayClass()
1552         {
1553             setMethod ("contains", contains);
1554             setMethod ("remove",   remove);
1555             setMethod ("join",     join);
1556             setMethod ("push",     push);
1557             setMethod ("splice",   splice);
1558             setMethod ("indexOf",  indexOf);
1559         }
1560 
getClassNamejuce::JavascriptEngine::RootObject::ArrayClass1561         static Identifier getClassName()   { static const Identifier i ("Array"); return i; }
1562 
containsjuce::JavascriptEngine::RootObject::ArrayClass1563         static var contains (Args a)
1564         {
1565             if (auto* array = a.thisObject.getArray())
1566                 return array->contains (get (a, 0));
1567 
1568             return false;
1569         }
1570 
removejuce::JavascriptEngine::RootObject::ArrayClass1571         static var remove (Args a)
1572         {
1573             if (auto* array = a.thisObject.getArray())
1574                 array->removeAllInstancesOf (get (a, 0));
1575 
1576             return var::undefined();
1577         }
1578 
joinjuce::JavascriptEngine::RootObject::ArrayClass1579         static var join (Args a)
1580         {
1581             StringArray strings;
1582 
1583             if (auto* array = a.thisObject.getArray())
1584                 for (auto& v : *array)
1585                     strings.add (v.toString());
1586 
1587             return strings.joinIntoString (getString (a, 0));
1588         }
1589 
pushjuce::JavascriptEngine::RootObject::ArrayClass1590         static var push (Args a)
1591         {
1592             if (auto* array = a.thisObject.getArray())
1593             {
1594                 for (int i = 0; i < a.numArguments; ++i)
1595                     array->add (a.arguments[i]);
1596 
1597                 return array->size();
1598             }
1599 
1600             return var::undefined();
1601         }
1602 
splicejuce::JavascriptEngine::RootObject::ArrayClass1603         static var splice (Args a)
1604         {
1605             if (auto* array = a.thisObject.getArray())
1606             {
1607                 auto arraySize = array->size();
1608                 int start = get (a, 0);
1609 
1610                 if (start < 0)
1611                     start = jmax (0, arraySize + start);
1612                 else if (start > arraySize)
1613                     start = arraySize;
1614 
1615                 const int num = a.numArguments > 1 ? jlimit (0, arraySize - start, getInt (a, 1))
1616                                                    : arraySize - start;
1617 
1618                 Array<var> itemsRemoved;
1619                 itemsRemoved.ensureStorageAllocated (num);
1620 
1621                 for (int i = 0; i < num; ++i)
1622                     itemsRemoved.add (array->getReference (start + i));
1623 
1624                 array->removeRange (start, num);
1625 
1626                 for (int i = 2; i < a.numArguments; ++i)
1627                     array->insert (start++, get (a, i));
1628 
1629                 // std::move() needed here for older compilers
1630                 return std::move (itemsRemoved);
1631             }
1632 
1633             return var::undefined();
1634         }
1635 
indexOfjuce::JavascriptEngine::RootObject::ArrayClass1636         static var indexOf (Args a)
1637         {
1638             if (auto* array = a.thisObject.getArray())
1639             {
1640                 auto target = get (a, 0);
1641 
1642                 for (int i = (a.numArguments > 1 ? getInt (a, 1) : 0); i < array->size(); ++i)
1643                     if (array->getReference(i) == target)
1644                         return i;
1645             }
1646 
1647             return -1;
1648         }
1649     };
1650 
1651     //==============================================================================
1652     struct StringClass  : public DynamicObject
1653     {
StringClassjuce::JavascriptEngine::RootObject::StringClass1654         StringClass()
1655         {
1656             setMethod ("substring",     substring);
1657             setMethod ("indexOf",       indexOf);
1658             setMethod ("charAt",        charAt);
1659             setMethod ("charCodeAt",    charCodeAt);
1660             setMethod ("fromCharCode",  fromCharCode);
1661             setMethod ("split",         split);
1662         }
1663 
getClassNamejuce::JavascriptEngine::RootObject::StringClass1664         static Identifier getClassName()  { static const Identifier i ("String"); return i; }
1665 
fromCharCodejuce::JavascriptEngine::RootObject::StringClass1666         static var fromCharCode (Args a)  { return String::charToString (static_cast<juce_wchar> (getInt (a, 0))); }
substringjuce::JavascriptEngine::RootObject::StringClass1667         static var substring (Args a)     { return a.thisObject.toString().substring (getInt (a, 0), getInt (a, 1)); }
indexOfjuce::JavascriptEngine::RootObject::StringClass1668         static var indexOf (Args a)       { return a.thisObject.toString().indexOf (getString (a, 0)); }
charCodeAtjuce::JavascriptEngine::RootObject::StringClass1669         static var charCodeAt (Args a)    { return (int) a.thisObject.toString() [getInt (a, 0)]; }
charAtjuce::JavascriptEngine::RootObject::StringClass1670         static var charAt (Args a)        { int p = getInt (a, 0); return a.thisObject.toString().substring (p, p + 1); }
1671 
splitjuce::JavascriptEngine::RootObject::StringClass1672         static var split (Args a)
1673         {
1674             auto str = a.thisObject.toString();
1675             auto sep = getString (a, 0);
1676             StringArray strings;
1677 
1678             if (sep.isNotEmpty())
1679                 strings.addTokens (str, sep.substring (0, 1), {});
1680             else // special-case for empty separator: split all chars separately
1681                 for (auto pos = str.getCharPointer(); ! pos.isEmpty(); ++pos)
1682                     strings.add (String::charToString (*pos));
1683 
1684             var array;
1685 
1686             for (auto& s : strings)
1687                 array.append (s);
1688 
1689             return array;
1690         }
1691     };
1692 
1693     //==============================================================================
1694     struct MathClass  : public DynamicObject
1695     {
MathClassjuce::JavascriptEngine::RootObject::MathClass1696         MathClass()
1697         {
1698             setMethod ("abs",       Math_abs);              setMethod ("round",     Math_round);
1699             setMethod ("random",    Math_random);           setMethod ("randInt",   Math_randInt);
1700             setMethod ("min",       Math_min);              setMethod ("max",       Math_max);
1701             setMethod ("range",     Math_range);            setMethod ("sign",      Math_sign);
1702             setMethod ("toDegrees", Math_toDegrees);        setMethod ("toRadians", Math_toRadians);
1703             setMethod ("sin",       Math_sin);              setMethod ("asin",      Math_asin);
1704             setMethod ("sinh",      Math_sinh);             setMethod ("asinh",     Math_asinh);
1705             setMethod ("cos",       Math_cos);              setMethod ("acos",      Math_acos);
1706             setMethod ("cosh",      Math_cosh);             setMethod ("acosh",     Math_acosh);
1707             setMethod ("tan",       Math_tan);              setMethod ("atan",      Math_atan);
1708             setMethod ("tanh",      Math_tanh);             setMethod ("atanh",     Math_atanh);
1709             setMethod ("log",       Math_log);              setMethod ("log10",     Math_log10);
1710             setMethod ("exp",       Math_exp);              setMethod ("pow",       Math_pow);
1711             setMethod ("sqr",       Math_sqr);              setMethod ("sqrt",      Math_sqrt);
1712             setMethod ("ceil",      Math_ceil);             setMethod ("floor",     Math_floor);
1713 
1714             setProperty ("PI",      MathConstants<double>::pi);
1715             setProperty ("E",       MathConstants<double>::euler);
1716             setProperty ("SQRT2",   MathConstants<double>::sqrt2);
1717             setProperty ("SQRT1_2", std::sqrt (0.5));
1718             setProperty ("LN2",     std::log (2.0));
1719             setProperty ("LN10",    std::log (10.0));
1720             setProperty ("LOG2E",   std::log (MathConstants<double>::euler) / std::log (2.0));
1721             setProperty ("LOG10E",  std::log (MathConstants<double>::euler) / std::log (10.0));
1722         }
1723 
Math_randomjuce::JavascriptEngine::RootObject::MathClass1724         static var Math_random    (Args)   { return Random::getSystemRandom().nextDouble(); }
Math_randIntjuce::JavascriptEngine::RootObject::MathClass1725         static var Math_randInt   (Args a) { return Random::getSystemRandom().nextInt (Range<int> (getInt (a, 0), getInt (a, 1))); }
Math_absjuce::JavascriptEngine::RootObject::MathClass1726         static var Math_abs       (Args a) { return isInt (a, 0) ? var (std::abs   (getInt (a, 0))) : var (std::abs   (getDouble (a, 0))); }
Math_roundjuce::JavascriptEngine::RootObject::MathClass1727         static var Math_round     (Args a) { return isInt (a, 0) ? var (roundToInt (getInt (a, 0))) : var (roundToInt (getDouble (a, 0))); }
Math_signjuce::JavascriptEngine::RootObject::MathClass1728         static var Math_sign      (Args a) { return isInt (a, 0) ? var (sign       (getInt (a, 0))) : var (sign       (getDouble (a, 0))); }
Math_rangejuce::JavascriptEngine::RootObject::MathClass1729         static var Math_range     (Args a) { return isInt (a, 0) ? var (jlimit (getInt (a, 1), getInt (a, 2), getInt (a, 0))) : var (jlimit (getDouble (a, 1), getDouble (a, 2), getDouble (a, 0))); }
Math_minjuce::JavascriptEngine::RootObject::MathClass1730         static var Math_min       (Args a) { return (isInt (a, 0) && isInt (a, 1)) ? var (jmin (getInt (a, 0), getInt (a, 1))) : var (jmin (getDouble (a, 0), getDouble (a, 1))); }
Math_maxjuce::JavascriptEngine::RootObject::MathClass1731         static var Math_max       (Args a) { return (isInt (a, 0) && isInt (a, 1)) ? var (jmax (getInt (a, 0), getInt (a, 1))) : var (jmax (getDouble (a, 0), getDouble (a, 1))); }
Math_toDegreesjuce::JavascriptEngine::RootObject::MathClass1732         static var Math_toDegrees (Args a) { return radiansToDegrees (getDouble (a, 0)); }
Math_toRadiansjuce::JavascriptEngine::RootObject::MathClass1733         static var Math_toRadians (Args a) { return degreesToRadians (getDouble (a, 0)); }
Math_sinjuce::JavascriptEngine::RootObject::MathClass1734         static var Math_sin       (Args a) { return std::sin   (getDouble (a, 0)); }
Math_asinjuce::JavascriptEngine::RootObject::MathClass1735         static var Math_asin      (Args a) { return std::asin  (getDouble (a, 0)); }
Math_cosjuce::JavascriptEngine::RootObject::MathClass1736         static var Math_cos       (Args a) { return std::cos   (getDouble (a, 0)); }
Math_acosjuce::JavascriptEngine::RootObject::MathClass1737         static var Math_acos      (Args a) { return std::acos  (getDouble (a, 0)); }
Math_sinhjuce::JavascriptEngine::RootObject::MathClass1738         static var Math_sinh      (Args a) { return std::sinh  (getDouble (a, 0)); }
Math_coshjuce::JavascriptEngine::RootObject::MathClass1739         static var Math_cosh      (Args a) { return std::cosh  (getDouble (a, 0)); }
Math_tanjuce::JavascriptEngine::RootObject::MathClass1740         static var Math_tan       (Args a) { return std::tan   (getDouble (a, 0)); }
Math_tanhjuce::JavascriptEngine::RootObject::MathClass1741         static var Math_tanh      (Args a) { return std::tanh  (getDouble (a, 0)); }
Math_atanjuce::JavascriptEngine::RootObject::MathClass1742         static var Math_atan      (Args a) { return std::atan  (getDouble (a, 0)); }
Math_logjuce::JavascriptEngine::RootObject::MathClass1743         static var Math_log       (Args a) { return std::log   (getDouble (a, 0)); }
Math_log10juce::JavascriptEngine::RootObject::MathClass1744         static var Math_log10     (Args a) { return std::log10 (getDouble (a, 0)); }
Math_expjuce::JavascriptEngine::RootObject::MathClass1745         static var Math_exp       (Args a) { return std::exp   (getDouble (a, 0)); }
Math_powjuce::JavascriptEngine::RootObject::MathClass1746         static var Math_pow       (Args a) { return std::pow   (getDouble (a, 0), getDouble (a, 1)); }
Math_sqrjuce::JavascriptEngine::RootObject::MathClass1747         static var Math_sqr       (Args a) { return square (getDouble (a, 0)); }
Math_sqrtjuce::JavascriptEngine::RootObject::MathClass1748         static var Math_sqrt      (Args a) { return std::sqrt  (getDouble (a, 0)); }
Math_ceiljuce::JavascriptEngine::RootObject::MathClass1749         static var Math_ceil      (Args a) { return std::ceil  (getDouble (a, 0)); }
Math_floorjuce::JavascriptEngine::RootObject::MathClass1750         static var Math_floor     (Args a) { return std::floor (getDouble (a, 0)); }
1751 
1752         // We can't use the std namespace equivalents of these functions without breaking
1753         // compatibility with older versions of OS X.
Math_asinhjuce::JavascriptEngine::RootObject::MathClass1754         static var Math_asinh     (Args a) { return asinh (getDouble (a, 0)); }
Math_acoshjuce::JavascriptEngine::RootObject::MathClass1755         static var Math_acosh     (Args a) { return acosh (getDouble (a, 0)); }
Math_atanhjuce::JavascriptEngine::RootObject::MathClass1756         static var Math_atanh     (Args a) { return atanh (getDouble (a, 0)); }
1757 
getClassNamejuce::JavascriptEngine::RootObject::MathClass1758         static Identifier getClassName()   { static const Identifier i ("Math"); return i; }
signjuce::JavascriptEngine::RootObject::MathClass1759         template <typename Type> static Type sign (Type n) noexcept  { return n > 0 ? (Type) 1 : (n < 0 ? (Type) -1 : 0); }
1760     };
1761 
1762     //==============================================================================
1763     struct JSONClass  : public DynamicObject
1764     {
JSONClassjuce::JavascriptEngine::RootObject::JSONClass1765         JSONClass()                        { setMethod ("stringify", stringify); }
getClassNamejuce::JavascriptEngine::RootObject::JSONClass1766         static Identifier getClassName()   { static const Identifier i ("JSON"); return i; }
stringifyjuce::JavascriptEngine::RootObject::JSONClass1767         static var stringify (Args a)      { return JSON::toString (get (a, 0)); }
1768     };
1769 
1770     //==============================================================================
1771     struct IntegerClass  : public DynamicObject
1772     {
IntegerClassjuce::JavascriptEngine::RootObject::IntegerClass1773         IntegerClass()                     { setMethod ("parseInt",  parseInt); }
getClassNamejuce::JavascriptEngine::RootObject::IntegerClass1774         static Identifier getClassName()   { static const Identifier i ("Integer"); return i; }
1775 
parseIntjuce::JavascriptEngine::RootObject::IntegerClass1776         static var parseInt (Args a)
1777         {
1778             auto s = getString (a, 0).trim();
1779 
1780             return s[0] == '0' ? (s[1] == 'x' ? s.substring(2).getHexValue64() : getOctalValue (s))
1781                                : s.getLargeIntValue();
1782         }
1783     };
1784 
1785     //==============================================================================
tracejuce::JavascriptEngine::RootObject1786     static var trace (Args a)        { Logger::outputDebugString (JSON::toString (a.thisObject)); return var::undefined(); }
charToIntjuce::JavascriptEngine::RootObject1787     static var charToInt (Args a)    { return (int) (getString (a, 0)[0]); }
parseFloatjuce::JavascriptEngine::RootObject1788     static var parseFloat (Args a)   { return getDouble (a, 0); }
1789 
typeof_internaljuce::JavascriptEngine::RootObject1790     static var typeof_internal (Args a)
1791     {
1792         var v (get (a, 0));
1793 
1794         if (v.isVoid())                      return "void";
1795         if (v.isString())                    return "string";
1796         if (isNumeric (v))                   return "number";
1797         if (isFunction (v) || v.isMethod())  return "function";
1798         if (v.isObject())                    return "object";
1799 
1800         return "undefined";
1801     }
1802 
execjuce::JavascriptEngine::RootObject1803     static var exec (Args a)
1804     {
1805         if (auto* root = dynamic_cast<RootObject*> (a.thisObject.getObject()))
1806             root->execute (getString (a, 0));
1807 
1808         return var::undefined();
1809     }
1810 
evaljuce::JavascriptEngine::RootObject1811     static var eval (Args a)
1812     {
1813         if (auto* root = dynamic_cast<RootObject*> (a.thisObject.getObject()))
1814             return root->evaluate (getString (a, 0));
1815 
1816         return var::undefined();
1817     }
1818 };
1819 
1820 //==============================================================================
JavascriptEngine()1821 JavascriptEngine::JavascriptEngine()  : maximumExecutionTime (15.0), root (new RootObject())
1822 {
1823     registerNativeObject (RootObject::ObjectClass  ::getClassName(),  new RootObject::ObjectClass());
1824     registerNativeObject (RootObject::ArrayClass   ::getClassName(),  new RootObject::ArrayClass());
1825     registerNativeObject (RootObject::StringClass  ::getClassName(),  new RootObject::StringClass());
1826     registerNativeObject (RootObject::MathClass    ::getClassName(),  new RootObject::MathClass());
1827     registerNativeObject (RootObject::JSONClass    ::getClassName(),  new RootObject::JSONClass());
1828     registerNativeObject (RootObject::IntegerClass ::getClassName(),  new RootObject::IntegerClass());
1829 }
1830 
~JavascriptEngine()1831 JavascriptEngine::~JavascriptEngine() {}
1832 
prepareTimeout() const1833 void JavascriptEngine::prepareTimeout() const noexcept   { root->timeout = Time::getCurrentTime() + maximumExecutionTime; }
stop()1834 void JavascriptEngine::stop() noexcept                   { root->timeout = {}; }
1835 
registerNativeObject(const Identifier & name,DynamicObject * object)1836 void JavascriptEngine::registerNativeObject (const Identifier& name, DynamicObject* object)
1837 {
1838     root->setProperty (name, object);
1839 }
1840 
execute(const String & code)1841 Result JavascriptEngine::execute (const String& code)
1842 {
1843     try
1844     {
1845         prepareTimeout();
1846         root->execute (code);
1847     }
1848     catch (String& error)
1849     {
1850         return Result::fail (error);
1851     }
1852 
1853     return Result::ok();
1854 }
1855 
evaluate(const String & code,Result * result)1856 var JavascriptEngine::evaluate (const String& code, Result* result)
1857 {
1858     try
1859     {
1860         prepareTimeout();
1861         if (result != nullptr) *result = Result::ok();
1862         return root->evaluate (code);
1863     }
1864     catch (String& error)
1865     {
1866         if (result != nullptr) *result = Result::fail (error);
1867     }
1868 
1869     return var::undefined();
1870 }
1871 
callFunction(const Identifier & function,const var::NativeFunctionArgs & args,Result * result)1872 var JavascriptEngine::callFunction (const Identifier& function, const var::NativeFunctionArgs& args, Result* result)
1873 {
1874     auto returnVal = var::undefined();
1875 
1876     try
1877     {
1878         prepareTimeout();
1879         if (result != nullptr) *result = Result::ok();
1880         RootObject::Scope ({}, *root, *root).findAndInvokeMethod (function, args, returnVal);
1881     }
1882     catch (String& error)
1883     {
1884         if (result != nullptr) *result = Result::fail (error);
1885     }
1886 
1887     return returnVal;
1888 }
1889 
callFunctionObject(DynamicObject * objectScope,const var & functionObject,const var::NativeFunctionArgs & args,Result * result)1890 var JavascriptEngine::callFunctionObject (DynamicObject* objectScope, const var& functionObject,
1891                                           const var::NativeFunctionArgs& args, Result* result)
1892 {
1893     auto returnVal = var::undefined();
1894 
1895     try
1896     {
1897         prepareTimeout();
1898         if (result != nullptr) *result = Result::ok();
1899         RootObject::Scope rootScope ({}, *root, *root);
1900         RootObject::Scope (&rootScope, *root, DynamicObject::Ptr (objectScope))
1901             .invokeMethod (functionObject, args, returnVal);
1902     }
1903     catch (String& error)
1904     {
1905         if (result != nullptr) *result = Result::fail (error);
1906     }
1907 
1908     return returnVal;
1909 }
1910 
getRootObjectProperties() const1911 const NamedValueSet& JavascriptEngine::getRootObjectProperties() const noexcept
1912 {
1913     return root->getProperties();
1914 }
1915 
1916 #if JUCE_MSVC
1917  #pragma warning (pop)
1918 #endif
1919 
1920 } // namespace juce
1921