1 /*
2   ==============================================================================
3 
4    This file is part of the JUCE library.
5    Copyright (c) 2020 - Raw Material Software Limited
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 JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4702)
58 
59 //==============================================================================
60 struct JavascriptEngine::RootObject   : public DynamicObject
61 {
RootObjectjuce::JavascriptEngine::RootObject62     RootObject()
63     {
64         setMethod ("exec",       exec);
65         setMethod ("eval",       eval);
66         setMethod ("trace",      trace);
67         setMethod ("charToInt",  charToInt);
68         setMethod ("parseInt",   IntegerClass::parseInt);
69         setMethod ("typeof",     typeof_internal);
70         setMethod ("parseFloat", parseFloat);
71     }
72 
73     Time timeout;
74 
75     using Args = const var::NativeFunctionArgs&;
76     using TokenType = const char*;
77 
executejuce::JavascriptEngine::RootObject78     void execute (const String& code)
79     {
80         ExpressionTreeBuilder tb (code);
81         std::unique_ptr<BlockStatement> (tb.parseStatementList())->perform (Scope ({}, *this, *this), nullptr);
82     }
83 
evaluatejuce::JavascriptEngine::RootObject84     var evaluate (const String& code)
85     {
86         ExpressionTreeBuilder tb (code);
87         return ExpPtr (tb.parseExpression())->getResult (Scope ({}, *this, *this));
88     }
89 
90     //==============================================================================
areTypeEqualjuce::JavascriptEngine::RootObject91     static bool areTypeEqual (const var& a, const var& b)
92     {
93         return a.hasSameTypeAs (b) && isFunction (a) == isFunction (b)
94                 && (((a.isUndefined() || a.isVoid()) && (b.isUndefined() || b.isVoid())) || a == b);
95     }
96 
getTokenNamejuce::JavascriptEngine::RootObject97     static String getTokenName (TokenType t)                  { return t[0] == '$' ? String (t + 1) : ("'" + String (t) + "'"); }
isFunctionjuce::JavascriptEngine::RootObject98     static bool isFunction (const var& v) noexcept            { return dynamic_cast<FunctionObject*> (v.getObject()) != nullptr; }
isNumericjuce::JavascriptEngine::RootObject99     static bool isNumeric (const var& v) noexcept             { return v.isInt() || v.isDouble() || v.isInt64() || v.isBool(); }
isNumericOrUndefinedjuce::JavascriptEngine::RootObject100     static bool isNumericOrUndefined (const var& v) noexcept  { return isNumeric (v) || v.isUndefined(); }
getOctalValuejuce::JavascriptEngine::RootObject101     static int64 getOctalValue (const String& s)              { BigInteger b; b.parseString (s.initialSectionContainingOnly ("01234567"), 8); return b.toInt64(); }
getPrototypeIdentifierjuce::JavascriptEngine::RootObject102     static Identifier getPrototypeIdentifier()                { static const Identifier i ("prototype"); return i; }
getPropertyPointerjuce::JavascriptEngine::RootObject103     static var* getPropertyPointer (DynamicObject& o, const Identifier& i) noexcept   { return o.getProperties().getVarPointer (i); }
104 
105     //==============================================================================
106     struct CodeLocation
107     {
CodeLocationjuce::JavascriptEngine::RootObject::CodeLocation108         CodeLocation (const String& code) noexcept        : program (code), location (program.getCharPointer()) {}
CodeLocationjuce::JavascriptEngine::RootObject::CodeLocation109         CodeLocation (const CodeLocation& other) noexcept : program (other.program), location (other.location) {}
110 
throwErrorjuce::JavascriptEngine::RootObject::CodeLocation111         void throwError (const String& message) const
112         {
113             int col = 1, line = 1;
114 
115             for (auto i = program.getCharPointer(); i < location && ! i.isEmpty(); ++i)
116             {
117                 ++col;
118                 if (*i == '\n')  { col = 1; ++line; }
119             }
120 
121             throw "Line " + String (line) + ", column " + String (col) + " : " + message;
122         }
123 
124         String program;
125         String::CharPointerType location;
126     };
127 
128     //==============================================================================
129     struct Scope
130     {
Scopejuce::JavascriptEngine::RootObject::Scope131         Scope (const Scope* p, ReferenceCountedObjectPtr<RootObject> rt, DynamicObject::Ptr scp) noexcept
132             : parent (p), root (std::move (rt)),
133               scope (std::move (scp)) {}
134 
135         const Scope* const parent;
136         ReferenceCountedObjectPtr<RootObject> root;
137         DynamicObject::Ptr scope;
138 
findFunctionCalljuce::JavascriptEngine::RootObject::Scope139         var findFunctionCall (const CodeLocation& location, const var& targetObject, const Identifier& functionName) const
140         {
141             if (auto* o = targetObject.getDynamicObject())
142             {
143                 if (auto* prop = getPropertyPointer (*o, functionName))
144                     return *prop;
145 
146                 for (auto* p = o->getProperty (getPrototypeIdentifier()).getDynamicObject(); p != nullptr;
147                      p = p->getProperty (getPrototypeIdentifier()).getDynamicObject())
148                 {
149                     if (auto* prop = getPropertyPointer (*p, functionName))
150                         return *prop;
151                 }
152 
153                 // if there's a class with an overridden DynamicObject::hasMethod, this avoids an error
154                 if (o->hasMethod (functionName))
155                     return {};
156             }
157 
158             if (targetObject.isString())
159                 if (auto* m = findRootClassProperty (StringClass::getClassName(), functionName))
160                     return *m;
161 
162             if (targetObject.isArray())
163                 if (auto* m = findRootClassProperty (ArrayClass::getClassName(), functionName))
164                     return *m;
165 
166             if (auto* m = findRootClassProperty (ObjectClass::getClassName(), functionName))
167                 return *m;
168 
169             location.throwError ("Unknown function '" + functionName.toString() + "'");
170             return {};
171         }
172 
findRootClassPropertyjuce::JavascriptEngine::RootObject::Scope173         var* findRootClassProperty (const Identifier& className, const Identifier& propName) const
174         {
175             if (auto* cls = root->getProperty (className).getDynamicObject())
176                 return getPropertyPointer (*cls, propName);
177 
178             return nullptr;
179         }
180 
findSymbolInParentScopesjuce::JavascriptEngine::RootObject::Scope181         var findSymbolInParentScopes (const Identifier& name) const
182         {
183             if (auto v = getPropertyPointer (*scope, name))
184                 return *v;
185 
186             return parent != nullptr ? parent->findSymbolInParentScopes (name)
187                                      : var::undefined();
188         }
189 
findAndInvokeMethodjuce::JavascriptEngine::RootObject::Scope190         bool findAndInvokeMethod (const Identifier& function, const var::NativeFunctionArgs& args, var& result) const
191         {
192             auto* target = args.thisObject.getDynamicObject();
193 
194             if (target == nullptr || target == scope.get())
195             {
196                 if (auto* m = getPropertyPointer (*scope, function))
197                 {
198                     if (auto fo = dynamic_cast<FunctionObject*> (m->getObject()))
199                     {
200                         result = fo->invoke (*this, args);
201                         return true;
202                     }
203                 }
204             }
205 
206             const auto& props = scope->getProperties();
207 
208             for (int i = 0; i < props.size(); ++i)
209                 if (auto* o = props.getValueAt (i).getDynamicObject())
210                     if (Scope (this, *root, *o).findAndInvokeMethod (function, args, result))
211                         return true;
212 
213             return false;
214         }
215 
invokeMethodjuce::JavascriptEngine::RootObject::Scope216         bool invokeMethod (const var& m, const var::NativeFunctionArgs& args, var& result) const
217         {
218             if (isFunction (m))
219             {
220                 auto* target = args.thisObject.getDynamicObject();
221 
222                 if (target == nullptr || target == scope.get())
223                 {
224                     if (auto fo = dynamic_cast<FunctionObject*> (m.getObject()))
225                     {
226                         result = fo->invoke (*this, args);
227                         return true;
228                     }
229                 }
230             }
231 
232             return false;
233         }
234 
checkTimeOutjuce::JavascriptEngine::RootObject::Scope235         void checkTimeOut (const CodeLocation& location) const
236         {
237             if (Time::getCurrentTime() > root->timeout)
238                 location.throwError (root->timeout == Time() ? "Interrupted" : "Execution timed-out");
239         }
240 
241         JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Scope)
242     };
243 
244     //==============================================================================
245     struct Statement
246     {
Statementjuce::JavascriptEngine::RootObject::Statement247         Statement (const CodeLocation& l) noexcept : location (l) {}
~Statementjuce::JavascriptEngine::RootObject::Statement248         virtual ~Statement() {}
249 
250         enum ResultCode  { ok = 0, returnWasHit, breakWasHit, continueWasHit };
performjuce::JavascriptEngine::RootObject::Statement251         virtual ResultCode perform (const Scope&, var*) const  { return ok; }
252 
253         CodeLocation location;
254         JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Statement)
255     };
256 
257     struct Expression  : public Statement
258     {
Expressionjuce::JavascriptEngine::RootObject::Expression259         Expression (const CodeLocation& l) noexcept : Statement (l) {}
260 
getResultjuce::JavascriptEngine::RootObject::Expression261         virtual var getResult (const Scope&) const            { return var::undefined(); }
assignjuce::JavascriptEngine::RootObject::Expression262         virtual void assign (const Scope&, const var&) const  { location.throwError ("Cannot assign to this expression!"); }
263 
performjuce::JavascriptEngine::RootObject::Expression264         ResultCode perform (const Scope& s, var*) const override  { getResult (s); return ok; }
265     };
266 
267     using ExpPtr = std::unique_ptr<Expression>;
268 
269     struct BlockStatement  : public Statement
270     {
BlockStatementjuce::JavascriptEngine::RootObject::BlockStatement271         BlockStatement (const CodeLocation& l) noexcept : Statement (l) {}
272 
performjuce::JavascriptEngine::RootObject::BlockStatement273         ResultCode perform (const Scope& s, var* returnedValue) const override
274         {
275             for (auto* statement : statements)
276                 if (auto r = statement->perform (s, returnedValue))
277                     return r;
278 
279             return ok;
280         }
281 
282         OwnedArray<Statement> statements;
283     };
284 
285     struct IfStatement  : public Statement
286     {
IfStatementjuce::JavascriptEngine::RootObject::IfStatement287         IfStatement (const CodeLocation& l) noexcept : Statement (l) {}
288 
performjuce::JavascriptEngine::RootObject::IfStatement289         ResultCode perform (const Scope& s, var* returnedValue) const override
290         {
291             return (condition->getResult(s) ? trueBranch : falseBranch)->perform (s, returnedValue);
292         }
293 
294         ExpPtr condition;
295         std::unique_ptr<Statement> trueBranch, falseBranch;
296     };
297 
298     struct VarStatement  : public Statement
299     {
VarStatementjuce::JavascriptEngine::RootObject::VarStatement300         VarStatement (const CodeLocation& l) noexcept : Statement (l) {}
301 
performjuce::JavascriptEngine::RootObject::VarStatement302         ResultCode perform (const Scope& s, var*) const override
303         {
304             s.scope->setProperty (name, initialiser->getResult (s));
305             return ok;
306         }
307 
308         Identifier name;
309         ExpPtr initialiser;
310     };
311 
312     struct LoopStatement  : public Statement
313     {
LoopStatementjuce::JavascriptEngine::RootObject::LoopStatement314         LoopStatement (const CodeLocation& l, bool isDo) noexcept : Statement (l), isDoLoop (isDo) {}
315 
performjuce::JavascriptEngine::RootObject::LoopStatement316         ResultCode perform (const Scope& s, var* returnedValue) const override
317         {
318             initialiser->perform (s, nullptr);
319 
320             while (isDoLoop || condition->getResult (s))
321             {
322                 s.checkTimeOut (location);
323                 auto r = body->perform (s, returnedValue);
324 
325                 if (r == returnWasHit)   return r;
326                 if (r == breakWasHit)    break;
327 
328                 iterator->perform (s, nullptr);
329 
330                 if (isDoLoop && r != continueWasHit && ! condition->getResult (s))
331                     break;
332             }
333 
334             return ok;
335         }
336 
337         std::unique_ptr<Statement> initialiser, iterator, body;
338         ExpPtr condition;
339         bool isDoLoop;
340     };
341 
342     struct ReturnStatement  : public Statement
343     {
ReturnStatementjuce::JavascriptEngine::RootObject::ReturnStatement344         ReturnStatement (const CodeLocation& l, Expression* v) noexcept : Statement (l), returnValue (v) {}
345 
performjuce::JavascriptEngine::RootObject::ReturnStatement346         ResultCode perform (const Scope& s, var* ret) const override
347         {
348             if (ret != nullptr)  *ret = returnValue->getResult (s);
349             return returnWasHit;
350         }
351 
352         ExpPtr returnValue;
353     };
354 
355     struct BreakStatement  : public Statement
356     {
BreakStatementjuce::JavascriptEngine::RootObject::BreakStatement357         BreakStatement (const CodeLocation& l) noexcept : Statement (l) {}
performjuce::JavascriptEngine::RootObject::BreakStatement358         ResultCode perform (const Scope&, var*) const override  { return breakWasHit; }
359     };
360 
361     struct ContinueStatement  : public Statement
362     {
ContinueStatementjuce::JavascriptEngine::RootObject::ContinueStatement363         ContinueStatement (const CodeLocation& l) noexcept : Statement (l) {}
performjuce::JavascriptEngine::RootObject::ContinueStatement364         ResultCode perform (const Scope&, var*) const override  { return continueWasHit; }
365     };
366 
367     struct LiteralValue  : public Expression
368     {
LiteralValuejuce::JavascriptEngine::RootObject::LiteralValue369         LiteralValue (const CodeLocation& l, const var& v) noexcept : Expression (l), value (v) {}
getResultjuce::JavascriptEngine::RootObject::LiteralValue370         var getResult (const Scope&) const override   { return value; }
371         var value;
372     };
373 
374     struct UnqualifiedName  : public Expression
375     {
UnqualifiedNamejuce::JavascriptEngine::RootObject::UnqualifiedName376         UnqualifiedName (const CodeLocation& l, const Identifier& n) noexcept : Expression (l), name (n) {}
377 
getResultjuce::JavascriptEngine::RootObject::UnqualifiedName378         var getResult (const Scope& s) const override  { return s.findSymbolInParentScopes (name); }
379 
assignjuce::JavascriptEngine::RootObject::UnqualifiedName380         void assign (const Scope& s, const var& newValue) const override
381         {
382             if (auto* v = getPropertyPointer (*s.scope, name))
383                 *v = newValue;
384             else
385                 s.root->setProperty (name, newValue);
386         }
387 
388         Identifier name;
389     };
390 
391     struct DotOperator  : public Expression
392     {
DotOperatorjuce::JavascriptEngine::RootObject::DotOperator393         DotOperator (const CodeLocation& l, ExpPtr& p, const Identifier& c) noexcept : Expression (l), parent (p.release()), child (c) {}
394 
getResultjuce::JavascriptEngine::RootObject::DotOperator395         var getResult (const Scope& s) const override
396         {
397             auto p = parent->getResult (s);
398             static const Identifier lengthID ("length");
399 
400             if (child == lengthID)
401             {
402                 if (auto* array = p.getArray())   return array->size();
403                 if (p.isString())                 return p.toString().length();
404             }
405 
406             if (auto* o = p.getDynamicObject())
407                 if (auto* v = getPropertyPointer (*o, child))
408                     return *v;
409 
410             return var::undefined();
411         }
412 
assignjuce::JavascriptEngine::RootObject::DotOperator413         void assign (const Scope& s, const var& newValue) const override
414         {
415             if (auto* o = parent->getResult (s).getDynamicObject())
416                 o->setProperty (child, newValue);
417             else
418                 Expression::assign (s, newValue);
419         }
420 
421         ExpPtr parent;
422         Identifier child;
423     };
424 
425     struct ArraySubscript  : public Expression
426     {
ArraySubscriptjuce::JavascriptEngine::RootObject::ArraySubscript427         ArraySubscript (const CodeLocation& l) noexcept : Expression (l) {}
428 
getResultjuce::JavascriptEngine::RootObject::ArraySubscript429         var getResult (const Scope& s) const override
430         {
431             auto arrayVar = object->getResult (s); // must stay alive for the scope of this method
432             auto key = index->getResult (s);
433 
434             if (const auto* array = arrayVar.getArray())
435                 if (key.isInt() || key.isInt64() || key.isDouble())
436                     return (*array) [static_cast<int> (key)];
437 
438             if (auto* o = arrayVar.getDynamicObject())
439                 if (key.isString())
440                     if (auto* v = getPropertyPointer (*o, Identifier (key)))
441                         return *v;
442 
443             return var::undefined();
444         }
445 
assignjuce::JavascriptEngine::RootObject::ArraySubscript446         void assign (const Scope& s, const var& newValue) const override
447         {
448             auto arrayVar = object->getResult (s); // must stay alive for the scope of this method
449             auto key = index->getResult (s);
450 
451             if (auto* array = arrayVar.getArray())
452             {
453                 if (key.isInt() || key.isInt64() || key.isDouble())
454                 {
455                     const int i = key;
456                     while (array->size() < i)
457                         array->add (var::undefined());
458 
459                     array->set (i, newValue);
460                     return;
461                 }
462             }
463 
464             if (auto* o = arrayVar.getDynamicObject())
465             {
466                 if (key.isString())
467                 {
468                     o->setProperty (Identifier (key), newValue);
469                     return;
470                 }
471             }
472 
473             Expression::assign (s, newValue);
474         }
475 
476         ExpPtr object, index;
477     };
478 
479     struct BinaryOperatorBase  : public Expression
480     {
BinaryOperatorBasejuce::JavascriptEngine::RootObject::BinaryOperatorBase481         BinaryOperatorBase (const CodeLocation& l, ExpPtr& a, ExpPtr& b, TokenType op) noexcept
482             : Expression (l), lhs (a.release()), rhs (b.release()), operation (op) {}
483 
484         ExpPtr lhs, rhs;
485         TokenType operation;
486     };
487 
488     struct BinaryOperator  : public BinaryOperatorBase
489     {
BinaryOperatorjuce::JavascriptEngine::RootObject::BinaryOperator490         BinaryOperator (const CodeLocation& l, ExpPtr& a, ExpPtr& b, TokenType op) noexcept
491             : BinaryOperatorBase (l, a, b, op) {}
492 
getWithUndefinedArgjuce::JavascriptEngine::RootObject::BinaryOperator493         virtual var getWithUndefinedArg() const                           { return var::undefined(); }
getWithDoublesjuce::JavascriptEngine::RootObject::BinaryOperator494         virtual var getWithDoubles (double, double) const                 { return throwError ("Double"); }
getWithIntsjuce::JavascriptEngine::RootObject::BinaryOperator495         virtual var getWithInts (int64, int64) const                      { return throwError ("Integer"); }
getWithArrayOrObjectjuce::JavascriptEngine::RootObject::BinaryOperator496         virtual var getWithArrayOrObject (const var& a, const var&) const { return throwError (a.isArray() ? "Array" : "Object"); }
getWithStringsjuce::JavascriptEngine::RootObject::BinaryOperator497         virtual var getWithStrings (const String&, const String&) const   { return throwError ("String"); }
498 
getResultjuce::JavascriptEngine::RootObject::BinaryOperator499         var getResult (const Scope& s) const override
500         {
501             var a (lhs->getResult (s)), b (rhs->getResult (s));
502 
503             if ((a.isUndefined() || a.isVoid()) && (b.isUndefined() || b.isVoid()))
504                 return getWithUndefinedArg();
505 
506             if (isNumericOrUndefined (a) && isNumericOrUndefined (b))
507                 return (a.isDouble() || b.isDouble()) ? getWithDoubles (a, b) : getWithInts (a, b);
508 
509             if (a.isArray() || a.isObject())
510                 return getWithArrayOrObject (a, b);
511 
512             return getWithStrings (a.toString(), b.toString());
513         }
514 
throwErrorjuce::JavascriptEngine::RootObject::BinaryOperator515         var throwError (const char* typeName) const
516             { location.throwError (getTokenName (operation) + " is not allowed on the " + typeName + " type"); return {}; }
517     };
518 
519     struct EqualsOp  : public BinaryOperator
520     {
EqualsOpjuce::JavascriptEngine::RootObject::EqualsOp521         EqualsOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::equals) {}
getWithUndefinedArgjuce::JavascriptEngine::RootObject::EqualsOp522         var getWithUndefinedArg() const override                               { return true; }
getWithDoublesjuce::JavascriptEngine::RootObject::EqualsOp523         var getWithDoubles (double a, double b) const override                 { return a == b; }
getWithIntsjuce::JavascriptEngine::RootObject::EqualsOp524         var getWithInts (int64 a, int64 b) const override                      { return a == b; }
getWithStringsjuce::JavascriptEngine::RootObject::EqualsOp525         var getWithStrings (const String& a, const String& b) const override   { return a == b; }
getWithArrayOrObjectjuce::JavascriptEngine::RootObject::EqualsOp526         var getWithArrayOrObject (const var& a, const var& b) const override   { return a == b; }
527     };
528 
529     struct NotEqualsOp  : public BinaryOperator
530     {
NotEqualsOpjuce::JavascriptEngine::RootObject::NotEqualsOp531         NotEqualsOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::notEquals) {}
getWithUndefinedArgjuce::JavascriptEngine::RootObject::NotEqualsOp532         var getWithUndefinedArg() const override                               { return false; }
getWithDoublesjuce::JavascriptEngine::RootObject::NotEqualsOp533         var getWithDoubles (double a, double b) const override                 { return a != b; }
getWithIntsjuce::JavascriptEngine::RootObject::NotEqualsOp534         var getWithInts (int64 a, int64 b) const override                      { return a != b; }
getWithStringsjuce::JavascriptEngine::RootObject::NotEqualsOp535         var getWithStrings (const String& a, const String& b) const override   { return a != b; }
getWithArrayOrObjectjuce::JavascriptEngine::RootObject::NotEqualsOp536         var getWithArrayOrObject (const var& a, const var& b) const override   { return a != b; }
537     };
538 
539     struct LessThanOp  : public BinaryOperator
540     {
LessThanOpjuce::JavascriptEngine::RootObject::LessThanOp541         LessThanOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::lessThan) {}
getWithDoublesjuce::JavascriptEngine::RootObject::LessThanOp542         var getWithDoubles (double a, double b) const override                 { return a < b; }
getWithIntsjuce::JavascriptEngine::RootObject::LessThanOp543         var getWithInts (int64 a, int64 b) const override                      { return a < b; }
getWithStringsjuce::JavascriptEngine::RootObject::LessThanOp544         var getWithStrings (const String& a, const String& b) const override   { return a < b; }
545     };
546 
547     struct LessThanOrEqualOp  : public BinaryOperator
548     {
LessThanOrEqualOpjuce::JavascriptEngine::RootObject::LessThanOrEqualOp549         LessThanOrEqualOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::lessThanOrEqual) {}
getWithDoublesjuce::JavascriptEngine::RootObject::LessThanOrEqualOp550         var getWithDoubles (double a, double b) const override                 { return a <= b; }
getWithIntsjuce::JavascriptEngine::RootObject::LessThanOrEqualOp551         var getWithInts (int64 a, int64 b) const override                      { return a <= b; }
getWithStringsjuce::JavascriptEngine::RootObject::LessThanOrEqualOp552         var getWithStrings (const String& a, const String& b) const override   { return a <= b; }
553     };
554 
555     struct GreaterThanOp  : public BinaryOperator
556     {
GreaterThanOpjuce::JavascriptEngine::RootObject::GreaterThanOp557         GreaterThanOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::greaterThan) {}
getWithDoublesjuce::JavascriptEngine::RootObject::GreaterThanOp558         var getWithDoubles (double a, double b) const override                 { return a > b; }
getWithIntsjuce::JavascriptEngine::RootObject::GreaterThanOp559         var getWithInts (int64 a, int64 b) const override                      { return a > b; }
getWithStringsjuce::JavascriptEngine::RootObject::GreaterThanOp560         var getWithStrings (const String& a, const String& b) const override   { return a > b; }
561     };
562 
563     struct GreaterThanOrEqualOp  : public BinaryOperator
564     {
GreaterThanOrEqualOpjuce::JavascriptEngine::RootObject::GreaterThanOrEqualOp565         GreaterThanOrEqualOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::greaterThanOrEqual) {}
getWithDoublesjuce::JavascriptEngine::RootObject::GreaterThanOrEqualOp566         var getWithDoubles (double a, double b) const override                 { return a >= b; }
getWithIntsjuce::JavascriptEngine::RootObject::GreaterThanOrEqualOp567         var getWithInts (int64 a, int64 b) const override                      { return a >= b; }
getWithStringsjuce::JavascriptEngine::RootObject::GreaterThanOrEqualOp568         var getWithStrings (const String& a, const String& b) const override   { return a >= b; }
569     };
570 
571     struct AdditionOp  : public BinaryOperator
572     {
AdditionOpjuce::JavascriptEngine::RootObject::AdditionOp573         AdditionOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::plus) {}
getWithDoublesjuce::JavascriptEngine::RootObject::AdditionOp574         var getWithDoubles (double a, double b) const override                 { return a + b; }
getWithIntsjuce::JavascriptEngine::RootObject::AdditionOp575         var getWithInts (int64 a, int64 b) const override                      { return a + b; }
getWithStringsjuce::JavascriptEngine::RootObject::AdditionOp576         var getWithStrings (const String& a, const String& b) const override   { return a + b; }
577     };
578 
579     struct SubtractionOp  : public BinaryOperator
580     {
SubtractionOpjuce::JavascriptEngine::RootObject::SubtractionOp581         SubtractionOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::minus) {}
getWithDoublesjuce::JavascriptEngine::RootObject::SubtractionOp582         var getWithDoubles (double a, double b) const override { return a - b; }
getWithIntsjuce::JavascriptEngine::RootObject::SubtractionOp583         var getWithInts (int64 a, int64 b) const override      { return a - b; }
584     };
585 
586     struct MultiplyOp  : public BinaryOperator
587     {
MultiplyOpjuce::JavascriptEngine::RootObject::MultiplyOp588         MultiplyOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::times) {}
getWithDoublesjuce::JavascriptEngine::RootObject::MultiplyOp589         var getWithDoubles (double a, double b) const override { return a * b; }
getWithIntsjuce::JavascriptEngine::RootObject::MultiplyOp590         var getWithInts (int64 a, int64 b) const override      { return a * b; }
591     };
592 
593     struct DivideOp  : public BinaryOperator
594     {
DivideOpjuce::JavascriptEngine::RootObject::DivideOp595         DivideOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::divide) {}
getWithDoublesjuce::JavascriptEngine::RootObject::DivideOp596         var getWithDoubles (double a, double b) const override  { return b != 0 ? a / b : std::numeric_limits<double>::infinity(); }
getWithIntsjuce::JavascriptEngine::RootObject::DivideOp597         var getWithInts (int64 a, int64 b) const override       { return b != 0 ? var ((double) a / (double) b) : var (std::numeric_limits<double>::infinity()); }
598     };
599 
600     struct ModuloOp  : public BinaryOperator
601     {
ModuloOpjuce::JavascriptEngine::RootObject::ModuloOp602         ModuloOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::modulo) {}
getWithDoublesjuce::JavascriptEngine::RootObject::ModuloOp603         var getWithDoubles (double a, double b) const override  { return b != 0 ? fmod (a, b) : std::numeric_limits<double>::infinity(); }
getWithIntsjuce::JavascriptEngine::RootObject::ModuloOp604         var getWithInts (int64 a, int64 b) const override       { return b != 0 ? var (a % b) : var (std::numeric_limits<double>::infinity()); }
605     };
606 
607     struct BitwiseOrOp  : public BinaryOperator
608     {
BitwiseOrOpjuce::JavascriptEngine::RootObject::BitwiseOrOp609         BitwiseOrOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::bitwiseOr) {}
getWithIntsjuce::JavascriptEngine::RootObject::BitwiseOrOp610         var getWithInts (int64 a, int64 b) const override   { return a | b; }
611     };
612 
613     struct BitwiseAndOp  : public BinaryOperator
614     {
BitwiseAndOpjuce::JavascriptEngine::RootObject::BitwiseAndOp615         BitwiseAndOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::bitwiseAnd) {}
getWithIntsjuce::JavascriptEngine::RootObject::BitwiseAndOp616         var getWithInts (int64 a, int64 b) const override   { return a & b; }
617     };
618 
619     struct BitwiseXorOp  : public BinaryOperator
620     {
BitwiseXorOpjuce::JavascriptEngine::RootObject::BitwiseXorOp621         BitwiseXorOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::bitwiseXor) {}
getWithIntsjuce::JavascriptEngine::RootObject::BitwiseXorOp622         var getWithInts (int64 a, int64 b) const override   { return a ^ b; }
623     };
624 
625     struct LeftShiftOp  : public BinaryOperator
626     {
LeftShiftOpjuce::JavascriptEngine::RootObject::LeftShiftOp627         LeftShiftOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::leftShift) {}
getWithIntsjuce::JavascriptEngine::RootObject::LeftShiftOp628         var getWithInts (int64 a, int64 b) const override   { return ((int) a) << (int) b; }
629     };
630 
631     struct RightShiftOp  : public BinaryOperator
632     {
RightShiftOpjuce::JavascriptEngine::RootObject::RightShiftOp633         RightShiftOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::rightShift) {}
getWithIntsjuce::JavascriptEngine::RootObject::RightShiftOp634         var getWithInts (int64 a, int64 b) const override   { return ((int) a) >> (int) b; }
635     };
636 
637     struct RightShiftUnsignedOp  : public BinaryOperator
638     {
RightShiftUnsignedOpjuce::JavascriptEngine::RootObject::RightShiftUnsignedOp639         RightShiftUnsignedOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::rightShiftUnsigned) {}
getWithIntsjuce::JavascriptEngine::RootObject::RightShiftUnsignedOp640         var getWithInts (int64 a, int64 b) const override   { return (int) (((uint32) a) >> (int) b); }
641     };
642 
643     struct LogicalAndOp  : public BinaryOperatorBase
644     {
LogicalAndOpjuce::JavascriptEngine::RootObject::LogicalAndOp645         LogicalAndOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperatorBase (l, a, b, TokenTypes::logicalAnd) {}
getResultjuce::JavascriptEngine::RootObject::LogicalAndOp646         var getResult (const Scope& s) const override       { return lhs->getResult (s) && rhs->getResult (s); }
647     };
648 
649     struct LogicalOrOp  : public BinaryOperatorBase
650     {
LogicalOrOpjuce::JavascriptEngine::RootObject::LogicalOrOp651         LogicalOrOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperatorBase (l, a, b, TokenTypes::logicalOr) {}
getResultjuce::JavascriptEngine::RootObject::LogicalOrOp652         var getResult (const Scope& s) const override       { return lhs->getResult (s) || rhs->getResult (s); }
653     };
654 
655     struct TypeEqualsOp  : public BinaryOperatorBase
656     {
TypeEqualsOpjuce::JavascriptEngine::RootObject::TypeEqualsOp657         TypeEqualsOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperatorBase (l, a, b, TokenTypes::typeEquals) {}
getResultjuce::JavascriptEngine::RootObject::TypeEqualsOp658         var getResult (const Scope& s) const override       { return areTypeEqual (lhs->getResult (s), rhs->getResult (s)); }
659     };
660 
661     struct TypeNotEqualsOp  : public BinaryOperatorBase
662     {
TypeNotEqualsOpjuce::JavascriptEngine::RootObject::TypeNotEqualsOp663         TypeNotEqualsOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperatorBase (l, a, b, TokenTypes::typeNotEquals) {}
getResultjuce::JavascriptEngine::RootObject::TypeNotEqualsOp664         var getResult (const Scope& s) const override       { return ! areTypeEqual (lhs->getResult (s), rhs->getResult (s)); }
665     };
666 
667     struct ConditionalOp  : public Expression
668     {
ConditionalOpjuce::JavascriptEngine::RootObject::ConditionalOp669         ConditionalOp (const CodeLocation& l) noexcept : Expression (l) {}
670 
getResultjuce::JavascriptEngine::RootObject::ConditionalOp671         var getResult (const Scope& s) const override              { return (condition->getResult (s) ? trueBranch : falseBranch)->getResult (s); }
assignjuce::JavascriptEngine::RootObject::ConditionalOp672         void assign (const Scope& s, const var& v) const override  { (condition->getResult (s) ? trueBranch : falseBranch)->assign (s, v); }
673 
674         ExpPtr condition, trueBranch, falseBranch;
675     };
676 
677     struct Assignment  : public Expression
678     {
Assignmentjuce::JavascriptEngine::RootObject::Assignment679         Assignment (const CodeLocation& l, ExpPtr& dest, ExpPtr& source) noexcept : Expression (l), target (dest.release()), newValue (source.release()) {}
680 
getResultjuce::JavascriptEngine::RootObject::Assignment681         var getResult (const Scope& s) const override
682         {
683             auto value = newValue->getResult (s);
684             target->assign (s, value);
685             return value;
686         }
687 
688         ExpPtr target, newValue;
689     };
690 
691     struct SelfAssignment  : public Expression
692     {
SelfAssignmentjuce::JavascriptEngine::RootObject::SelfAssignment693         SelfAssignment (const CodeLocation& l, Expression* dest, Expression* source) noexcept
694             : Expression (l), target (dest), newValue (source) {}
695 
getResultjuce::JavascriptEngine::RootObject::SelfAssignment696         var getResult (const Scope& s) const override
697         {
698             auto value = newValue->getResult (s);
699             target->assign (s, value);
700             return value;
701         }
702 
703         Expression* target; // Careful! this pointer aliases a sub-term of newValue!
704         ExpPtr newValue;
705         TokenType op;
706     };
707 
708     struct PostAssignment  : public SelfAssignment
709     {
PostAssignmentjuce::JavascriptEngine::RootObject::PostAssignment710         PostAssignment (const CodeLocation& l, Expression* dest, Expression* source) noexcept : SelfAssignment (l, dest, source) {}
711 
getResultjuce::JavascriptEngine::RootObject::PostAssignment712         var getResult (const Scope& s) const override
713         {
714             auto oldValue = target->getResult (s);
715             target->assign (s, newValue->getResult (s));
716             return oldValue;
717         }
718     };
719 
720     struct FunctionCall  : public Expression
721     {
FunctionCalljuce::JavascriptEngine::RootObject::FunctionCall722         FunctionCall (const CodeLocation& l) noexcept : Expression (l) {}
723 
getResultjuce::JavascriptEngine::RootObject::FunctionCall724         var getResult (const Scope& s) const override
725         {
726             if (auto* dot = dynamic_cast<DotOperator*> (object.get()))
727             {
728                 auto thisObject = dot->parent->getResult (s);
729                 return invokeFunction (s, s.findFunctionCall (location, thisObject, dot->child), thisObject);
730             }
731 
732             auto function = object->getResult (s);
733             return invokeFunction (s, function, var (s.scope.get()));
734         }
735 
invokeFunctionjuce::JavascriptEngine::RootObject::FunctionCall736         var invokeFunction (const Scope& s, const var& function, const var& thisObject) const
737         {
738             s.checkTimeOut (location);
739             Array<var> argVars;
740 
741             for (auto* a : arguments)
742                 argVars.add (a->getResult (s));
743 
744             const var::NativeFunctionArgs args (thisObject, argVars.begin(), argVars.size());
745 
746             if (var::NativeFunction nativeFunction = function.getNativeFunction())
747                 return nativeFunction (args);
748 
749             if (auto* fo = dynamic_cast<FunctionObject*> (function.getObject()))
750                 return fo->invoke (s, args);
751 
752             if (auto* dot = dynamic_cast<DotOperator*> (object.get()))
753                 if (auto* o = thisObject.getDynamicObject())
754                     if (o->hasMethod (dot->child)) // allow an overridden DynamicObject::invokeMethod to accept a method call.
755                         return o->invokeMethod (dot->child, args);
756 
757             location.throwError ("This expression is not a function!"); return {};
758         }
759 
760         ExpPtr object;
761         OwnedArray<Expression> arguments;
762     };
763 
764     struct NewOperator  : public FunctionCall
765     {
NewOperatorjuce::JavascriptEngine::RootObject::NewOperator766         NewOperator (const CodeLocation& l) noexcept : FunctionCall (l) {}
767 
getResultjuce::JavascriptEngine::RootObject::NewOperator768         var getResult (const Scope& s) const override
769         {
770             var classOrFunc = object->getResult (s);
771             const bool isFunc = isFunction (classOrFunc);
772 
773             if (! (isFunc || classOrFunc.getDynamicObject() != nullptr))
774                 return var::undefined();
775 
776             DynamicObject::Ptr newObject (new DynamicObject());
777 
778             if (isFunc)
779                 invokeFunction (s, classOrFunc, newObject.get());
780             else
781                 newObject->setProperty (getPrototypeIdentifier(), classOrFunc);
782 
783             return newObject.get();
784         }
785     };
786 
787     struct ObjectDeclaration  : public Expression
788     {
ObjectDeclarationjuce::JavascriptEngine::RootObject::ObjectDeclaration789         ObjectDeclaration (const CodeLocation& l) noexcept : Expression (l) {}
790 
getResultjuce::JavascriptEngine::RootObject::ObjectDeclaration791         var getResult (const Scope& s) const override
792         {
793             DynamicObject::Ptr newObject (new DynamicObject());
794 
795             for (int i = 0; i < names.size(); ++i)
796                 newObject->setProperty (names.getUnchecked(i), initialisers.getUnchecked(i)->getResult (s));
797 
798             return newObject.get();
799         }
800 
801         Array<Identifier> names;
802         OwnedArray<Expression> initialisers;
803     };
804 
805     struct ArrayDeclaration  : public Expression
806     {
ArrayDeclarationjuce::JavascriptEngine::RootObject::ArrayDeclaration807         ArrayDeclaration (const CodeLocation& l) noexcept : Expression (l) {}
808 
getResultjuce::JavascriptEngine::RootObject::ArrayDeclaration809         var getResult (const Scope& s) const override
810         {
811             Array<var> a;
812 
813             for (int i = 0; i < values.size(); ++i)
814                 a.add (values.getUnchecked(i)->getResult (s));
815 
816             // std::move() needed here for older compilers
817             JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wredundant-move")
818             return std::move (a);
819             JUCE_END_IGNORE_WARNINGS_GCC_LIKE
820         }
821 
822         OwnedArray<Expression> values;
823     };
824 
825     //==============================================================================
826     struct FunctionObject  : public DynamicObject
827     {
FunctionObjectjuce::JavascriptEngine::RootObject::FunctionObject828         FunctionObject() noexcept {}
829 
FunctionObjectjuce::JavascriptEngine::RootObject::FunctionObject830         FunctionObject (const FunctionObject& other)  : DynamicObject(), functionCode (other.functionCode)
831         {
832             ExpressionTreeBuilder tb (functionCode);
833             tb.parseFunctionParamsAndBody (*this);
834         }
835 
clonejuce::JavascriptEngine::RootObject::FunctionObject836         DynamicObject::Ptr clone() override    { return *new FunctionObject (*this); }
837 
writeAsJSONjuce::JavascriptEngine::RootObject::FunctionObject838         void writeAsJSON (OutputStream& out, int /*indentLevel*/, bool /*allOnOneLine*/, int /*maximumDecimalPlaces*/) override
839         {
840             out << "function " << functionCode;
841         }
842 
invokejuce::JavascriptEngine::RootObject::FunctionObject843         var invoke (const Scope& s, const var::NativeFunctionArgs& args) const
844         {
845             DynamicObject::Ptr functionRoot (new DynamicObject());
846 
847             static const Identifier thisIdent ("this");
848             functionRoot->setProperty (thisIdent, args.thisObject);
849 
850             for (int i = 0; i < parameters.size(); ++i)
851                 functionRoot->setProperty (parameters.getReference(i),
852                                            i < args.numArguments ? args.arguments[i] : var::undefined());
853 
854             var result;
855             body->perform (Scope (&s, s.root, functionRoot), &result);
856             return result;
857         }
858 
859         String functionCode;
860         Array<Identifier> parameters;
861         std::unique_ptr<Statement> body;
862     };
863 
864     //==============================================================================
865     struct TokenIterator
866     {
TokenIteratorjuce::JavascriptEngine::RootObject::TokenIterator867         TokenIterator (const String& code) : location (code), p (code.getCharPointer()) { skip(); }
868 
skipjuce::JavascriptEngine::RootObject::TokenIterator869         void skip()
870         {
871             skipWhitespaceAndComments();
872             location.location = p;
873             currentType = matchNextToken();
874         }
875 
matchjuce::JavascriptEngine::RootObject::TokenIterator876         void match (TokenType expected)
877         {
878             if (currentType != expected)
879                 location.throwError ("Found " + getTokenName (currentType) + " when expecting " + getTokenName (expected));
880 
881             skip();
882         }
883 
matchIfjuce::JavascriptEngine::RootObject::TokenIterator884         bool matchIf (TokenType expected)                                 { if (currentType == expected)  { skip(); return true; } return false; }
matchesAnyjuce::JavascriptEngine::RootObject::TokenIterator885         bool matchesAny (TokenType t1, TokenType t2) const                { return currentType == t1 || currentType == t2; }
matchesAnyjuce::JavascriptEngine::RootObject::TokenIterator886         bool matchesAny (TokenType t1, TokenType t2, TokenType t3) const  { return matchesAny (t1, t2) || currentType == t3; }
887 
888         CodeLocation location;
889         TokenType currentType;
890         var currentValue;
891 
892     private:
893         String::CharPointerType p;
894 
isIdentifierStartjuce::JavascriptEngine::RootObject::TokenIterator895         static bool isIdentifierStart (juce_wchar c) noexcept   { return CharacterFunctions::isLetter (c)        || c == '_'; }
isIdentifierBodyjuce::JavascriptEngine::RootObject::TokenIterator896         static bool isIdentifierBody  (juce_wchar c) noexcept   { return CharacterFunctions::isLetterOrDigit (c) || c == '_'; }
897 
matchNextTokenjuce::JavascriptEngine::RootObject::TokenIterator898         TokenType matchNextToken()
899         {
900             if (isIdentifierStart (*p))
901             {
902                 auto end = p;
903                 while (isIdentifierBody (*++end)) {}
904 
905                 auto len = (size_t) (end - p);
906                 #define JUCE_JS_COMPARE_KEYWORD(name, str) if (len == sizeof (str) - 1 && matchToken (TokenTypes::name, len)) return TokenTypes::name;
907                 JUCE_JS_KEYWORDS (JUCE_JS_COMPARE_KEYWORD)
908 
909                 currentValue = String (p, end); p = end;
910                 return TokenTypes::identifier;
911             }
912 
913             if (p.isDigit())
914             {
915                 if (parseHexLiteral() || parseFloatLiteral() || parseOctalLiteral() || parseDecimalLiteral())
916                     return TokenTypes::literal;
917 
918                 location.throwError ("Syntax error in numeric constant");
919             }
920 
921             if (parseStringLiteral (*p) || (*p == '.' && parseFloatLiteral()))
922                 return TokenTypes::literal;
923 
924             #define JUCE_JS_COMPARE_OPERATOR(name, str) if (matchToken (TokenTypes::name, sizeof (str) - 1)) return TokenTypes::name;
925             JUCE_JS_OPERATORS (JUCE_JS_COMPARE_OPERATOR)
926 
927             if (! p.isEmpty())
928                 location.throwError ("Unexpected character '" + String::charToString (*p) + "' in source");
929 
930             return TokenTypes::eof;
931         }
932 
matchTokenjuce::JavascriptEngine::RootObject::TokenIterator933         bool matchToken (TokenType name, size_t len) noexcept
934         {
935             if (p.compareUpTo (CharPointer_ASCII (name), (int) len) != 0) return false;
936             p += (int) len;  return true;
937         }
938 
skipWhitespaceAndCommentsjuce::JavascriptEngine::RootObject::TokenIterator939         void skipWhitespaceAndComments()
940         {
941             for (;;)
942             {
943                 p.incrementToEndOfWhitespace();
944 
945                 if (*p == '/')
946                 {
947                     auto c2 = p[1];
948 
949                     if (c2 == '/')  { p = CharacterFunctions::find (p, (juce_wchar) '\n'); continue; }
950 
951                     if (c2 == '*')
952                     {
953                         location.location = p;
954                         p = CharacterFunctions::find (p + 2, CharPointer_ASCII ("*/"));
955                         if (p.isEmpty()) location.throwError ("Unterminated '/*' comment");
956                         p += 2; continue;
957                     }
958                 }
959 
960                 break;
961             }
962         }
963 
parseStringLiteraljuce::JavascriptEngine::RootObject::TokenIterator964         bool parseStringLiteral (juce_wchar quoteType)
965         {
966             if (quoteType != '"' && quoteType != '\'')
967                 return false;
968 
969             auto r = JSON::parseQuotedString (p, currentValue);
970             if (r.failed()) location.throwError (r.getErrorMessage());
971             return true;
972         }
973 
parseHexLiteraljuce::JavascriptEngine::RootObject::TokenIterator974         bool parseHexLiteral()
975         {
976             if (*p != '0' || (p[1] != 'x' && p[1] != 'X')) return false;
977 
978             auto t = ++p;
979             int64 v = CharacterFunctions::getHexDigitValue (*++t);
980             if (v < 0) return false;
981 
982             for (;;)
983             {
984                 auto digit = CharacterFunctions::getHexDigitValue (*++t);
985                 if (digit < 0) break;
986                 v = v * 16 + digit;
987             }
988 
989             currentValue = v; p = t;
990             return true;
991         }
992 
parseFloatLiteraljuce::JavascriptEngine::RootObject::TokenIterator993         bool parseFloatLiteral()
994         {
995             int numDigits = 0;
996             auto t = p;
997             while (t.isDigit())  { ++t; ++numDigits; }
998 
999             const bool hasPoint = (*t == '.');
1000 
1001             if (hasPoint)
1002                 while ((++t).isDigit())  ++numDigits;
1003 
1004             if (numDigits == 0)
1005                 return false;
1006 
1007             auto c = *t;
1008             const bool hasExponent = (c == 'e' || c == 'E');
1009 
1010             if (hasExponent)
1011             {
1012                 c = *++t;
1013                 if (c == '+' || c == '-')  ++t;
1014                 if (! t.isDigit()) return false;
1015                 while ((++t).isDigit()) {}
1016             }
1017 
1018             if (! (hasExponent || hasPoint)) return false;
1019 
1020             currentValue = CharacterFunctions::getDoubleValue (p);  p = t;
1021             return true;
1022         }
1023 
parseOctalLiteraljuce::JavascriptEngine::RootObject::TokenIterator1024         bool parseOctalLiteral()
1025         {
1026             auto t = p;
1027             int64 v = *t - '0';
1028             if (v != 0) return false;  // first digit of octal must be 0
1029 
1030             for (;;)
1031             {
1032                 auto digit = (int) (*++t - '0');
1033                 if (isPositiveAndBelow (digit, 8))        v = v * 8 + digit;
1034                 else if (isPositiveAndBelow (digit, 10))  location.throwError ("Decimal digit in octal constant");
1035                 else break;
1036             }
1037 
1038             currentValue = v;  p = t;
1039             return true;
1040         }
1041 
parseDecimalLiteraljuce::JavascriptEngine::RootObject::TokenIterator1042         bool parseDecimalLiteral()
1043         {
1044             int64 v = 0;
1045 
1046             for (;; ++p)
1047             {
1048                 auto digit = (int) (*p - '0');
1049                 if (isPositiveAndBelow (digit, 10))  v = v * 10 + digit;
1050                 else break;
1051             }
1052 
1053             currentValue = v;
1054             return true;
1055         }
1056     };
1057 
1058     //==============================================================================
1059     struct ExpressionTreeBuilder  : private TokenIterator
1060     {
ExpressionTreeBuilderjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1061         ExpressionTreeBuilder (const String code)  : TokenIterator (code) {}
1062 
parseStatementListjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1063         BlockStatement* parseStatementList()
1064         {
1065             std::unique_ptr<BlockStatement> b (new BlockStatement (location));
1066 
1067             while (currentType != TokenTypes::closeBrace && currentType != TokenTypes::eof)
1068                 b->statements.add (parseStatement());
1069 
1070             return b.release();
1071         }
1072 
parseFunctionParamsAndBodyjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1073         void parseFunctionParamsAndBody (FunctionObject& fo)
1074         {
1075             match (TokenTypes::openParen);
1076 
1077             while (currentType != TokenTypes::closeParen)
1078             {
1079                 auto paramName = currentValue.toString();
1080                 match (TokenTypes::identifier);
1081                 fo.parameters.add (paramName);
1082 
1083                 if (currentType != TokenTypes::closeParen)
1084                     match (TokenTypes::comma);
1085             }
1086 
1087             match (TokenTypes::closeParen);
1088             fo.body.reset (parseBlock());
1089         }
1090 
parseExpressionjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1091         Expression* parseExpression()
1092         {
1093             ExpPtr lhs (parseLogicOperator());
1094 
1095             if (matchIf (TokenTypes::question))          return parseTernaryOperator (lhs);
1096             if (matchIf (TokenTypes::assign))            { ExpPtr rhs (parseExpression()); return new Assignment (location, lhs, rhs); }
1097             if (matchIf (TokenTypes::plusEquals))        return parseInPlaceOpExpression<AdditionOp> (lhs);
1098             if (matchIf (TokenTypes::minusEquals))       return parseInPlaceOpExpression<SubtractionOp> (lhs);
1099             if (matchIf (TokenTypes::timesEquals))       return parseInPlaceOpExpression<MultiplyOp> (lhs);
1100             if (matchIf (TokenTypes::divideEquals))      return parseInPlaceOpExpression<DivideOp> (lhs);
1101             if (matchIf (TokenTypes::moduloEquals))      return parseInPlaceOpExpression<ModuloOp> (lhs);
1102             if (matchIf (TokenTypes::leftShiftEquals))   return parseInPlaceOpExpression<LeftShiftOp> (lhs);
1103             if (matchIf (TokenTypes::rightShiftEquals))  return parseInPlaceOpExpression<RightShiftOp> (lhs);
1104 
1105             return lhs.release();
1106         }
1107 
1108     private:
throwErrorjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1109         void throwError (const String& err) const  { location.throwError (err); }
1110 
1111         template <typename OpType>
parseInPlaceOpExpressionjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1112         Expression* parseInPlaceOpExpression (ExpPtr& lhs)
1113         {
1114             ExpPtr rhs (parseExpression());
1115             Expression* bareLHS = lhs.get(); // careful - bare pointer is deliberately aliased
1116             return new SelfAssignment (location, bareLHS, new OpType (location, lhs, rhs));
1117         }
1118 
parseBlockjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1119         BlockStatement* parseBlock()
1120         {
1121             match (TokenTypes::openBrace);
1122             std::unique_ptr<BlockStatement> b (parseStatementList());
1123             match (TokenTypes::closeBrace);
1124             return b.release();
1125         }
1126 
parseStatementjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1127         Statement* parseStatement()
1128         {
1129             if (currentType == TokenTypes::openBrace)   return parseBlock();
1130             if (matchIf (TokenTypes::var))              return parseVar();
1131             if (matchIf (TokenTypes::if_))              return parseIf();
1132             if (matchIf (TokenTypes::while_))           return parseDoOrWhileLoop (false);
1133             if (matchIf (TokenTypes::do_))              return parseDoOrWhileLoop (true);
1134             if (matchIf (TokenTypes::for_))             return parseForLoop();
1135             if (matchIf (TokenTypes::return_))          return parseReturn();
1136             if (matchIf (TokenTypes::break_))           return new BreakStatement (location);
1137             if (matchIf (TokenTypes::continue_))        return new ContinueStatement (location);
1138             if (matchIf (TokenTypes::function))         return parseFunction();
1139             if (matchIf (TokenTypes::semicolon))        return new Statement (location);
1140             if (matchIf (TokenTypes::plusplus))         return parsePreIncDec<AdditionOp>();
1141             if (matchIf (TokenTypes::minusminus))       return parsePreIncDec<SubtractionOp>();
1142 
1143             if (matchesAny (TokenTypes::openParen, TokenTypes::openBracket))
1144                 return matchEndOfStatement (parseFactor());
1145 
1146             if (matchesAny (TokenTypes::identifier, TokenTypes::literal, TokenTypes::minus))
1147                 return matchEndOfStatement (parseExpression());
1148 
1149             throwError ("Found " + getTokenName (currentType) + " when expecting a statement");
1150             return nullptr;
1151         }
1152 
matchEndOfStatementjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1153         Expression* matchEndOfStatement (Expression* ex)  { ExpPtr e (ex); if (currentType != TokenTypes::eof) match (TokenTypes::semicolon); return e.release(); }
matchCloseParenjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1154         Expression* matchCloseParen (Expression* ex)      { ExpPtr e (ex); match (TokenTypes::closeParen); return e.release(); }
1155 
parseIfjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1156         Statement* parseIf()
1157         {
1158             std::unique_ptr<IfStatement> s (new IfStatement (location));
1159             match (TokenTypes::openParen);
1160             s->condition.reset (parseExpression());
1161             match (TokenTypes::closeParen);
1162             s->trueBranch.reset (parseStatement());
1163             s->falseBranch.reset (matchIf (TokenTypes::else_) ? parseStatement() : new Statement (location));
1164             return s.release();
1165         }
1166 
parseReturnjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1167         Statement* parseReturn()
1168         {
1169             if (matchIf (TokenTypes::semicolon))
1170                 return new ReturnStatement (location, new Expression (location));
1171 
1172             auto* r = new ReturnStatement (location, parseExpression());
1173             matchIf (TokenTypes::semicolon);
1174             return r;
1175         }
1176 
parseVarjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1177         Statement* parseVar()
1178         {
1179             std::unique_ptr<VarStatement> s (new VarStatement (location));
1180             s->name = parseIdentifier();
1181             s->initialiser.reset (matchIf (TokenTypes::assign) ? parseExpression() : new Expression (location));
1182 
1183             if (matchIf (TokenTypes::comma))
1184             {
1185                 std::unique_ptr<BlockStatement> block (new BlockStatement (location));
1186                 block->statements.add (std::move (s));
1187                 block->statements.add (parseVar());
1188                 return block.release();
1189             }
1190 
1191             match (TokenTypes::semicolon);
1192             return s.release();
1193         }
1194 
parseFunctionjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1195         Statement* parseFunction()
1196         {
1197             Identifier name;
1198             auto fn = parseFunctionDefinition (name);
1199 
1200             if (name.isNull())
1201                 throwError ("Functions defined at statement-level must have a name");
1202 
1203             ExpPtr nm (new UnqualifiedName (location, name)), value (new LiteralValue (location, fn));
1204             return new Assignment (location, nm, value);
1205         }
1206 
parseForLoopjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1207         Statement* parseForLoop()
1208         {
1209             std::unique_ptr<LoopStatement> s (new LoopStatement (location, false));
1210             match (TokenTypes::openParen);
1211             s->initialiser.reset (parseStatement());
1212 
1213             if (matchIf (TokenTypes::semicolon))
1214                 s->condition.reset (new LiteralValue (location, true));
1215             else
1216             {
1217                 s->condition.reset (parseExpression());
1218                 match (TokenTypes::semicolon);
1219             }
1220 
1221             if (matchIf (TokenTypes::closeParen))
1222                 s->iterator.reset (new Statement (location));
1223             else
1224             {
1225                 s->iterator.reset (parseExpression());
1226                 match (TokenTypes::closeParen);
1227             }
1228 
1229             s->body.reset (parseStatement());
1230             return s.release();
1231         }
1232 
parseDoOrWhileLoopjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1233         Statement* parseDoOrWhileLoop (bool isDoLoop)
1234         {
1235             std::unique_ptr<LoopStatement> s (new LoopStatement (location, isDoLoop));
1236             s->initialiser.reset (new Statement (location));
1237             s->iterator.reset (new Statement (location));
1238 
1239             if (isDoLoop)
1240             {
1241                 s->body.reset (parseBlock());
1242                 match (TokenTypes::while_);
1243             }
1244 
1245             match (TokenTypes::openParen);
1246             s->condition.reset (parseExpression());
1247             match (TokenTypes::closeParen);
1248 
1249             if (! isDoLoop)
1250                 s->body.reset (parseStatement());
1251 
1252             return s.release();
1253         }
1254 
parseIdentifierjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1255         Identifier parseIdentifier()
1256         {
1257             Identifier i;
1258             if (currentType == TokenTypes::identifier)
1259                 i = currentValue.toString();
1260 
1261             match (TokenTypes::identifier);
1262             return i;
1263         }
1264 
parseFunctionDefinitionjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1265         var parseFunctionDefinition (Identifier& functionName)
1266         {
1267             auto functionStart = location.location;
1268 
1269             if (currentType == TokenTypes::identifier)
1270                 functionName = parseIdentifier();
1271 
1272             std::unique_ptr<FunctionObject> fo (new FunctionObject());
1273             parseFunctionParamsAndBody (*fo);
1274             fo->functionCode = String (functionStart, location.location);
1275             return var (fo.release());
1276         }
1277 
parseFunctionCalljuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1278         Expression* parseFunctionCall (FunctionCall* call, ExpPtr& function)
1279         {
1280             std::unique_ptr<FunctionCall> s (call);
1281             s->object.reset (function.release());
1282             match (TokenTypes::openParen);
1283 
1284             while (currentType != TokenTypes::closeParen)
1285             {
1286                 s->arguments.add (parseExpression());
1287                 if (currentType != TokenTypes::closeParen)
1288                     match (TokenTypes::comma);
1289             }
1290 
1291             return matchCloseParen (s.release());
1292         }
1293 
parseSuffixesjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1294         Expression* parseSuffixes (Expression* e)
1295         {
1296             ExpPtr input (e);
1297 
1298             if (matchIf (TokenTypes::dot))
1299                 return parseSuffixes (new DotOperator (location, input, parseIdentifier()));
1300 
1301             if (currentType == TokenTypes::openParen)
1302                 return parseSuffixes (parseFunctionCall (new FunctionCall (location), input));
1303 
1304             if (matchIf (TokenTypes::openBracket))
1305             {
1306                 std::unique_ptr<ArraySubscript> s (new ArraySubscript (location));
1307                 s->object.reset (input.release());
1308                 s->index.reset (parseExpression());
1309                 match (TokenTypes::closeBracket);
1310                 return parseSuffixes (s.release());
1311             }
1312 
1313             if (matchIf (TokenTypes::plusplus))   return parsePostIncDec<AdditionOp> (input);
1314             if (matchIf (TokenTypes::minusminus)) return parsePostIncDec<SubtractionOp> (input);
1315 
1316             return input.release();
1317         }
1318 
parseFactorjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1319         Expression* parseFactor()
1320         {
1321             if (currentType == TokenTypes::identifier)  return parseSuffixes (new UnqualifiedName (location, parseIdentifier()));
1322             if (matchIf (TokenTypes::openParen))        return parseSuffixes (matchCloseParen (parseExpression()));
1323             if (matchIf (TokenTypes::true_))            return parseSuffixes (new LiteralValue (location, (int) 1));
1324             if (matchIf (TokenTypes::false_))           return parseSuffixes (new LiteralValue (location, (int) 0));
1325             if (matchIf (TokenTypes::null_))            return parseSuffixes (new LiteralValue (location, var()));
1326             if (matchIf (TokenTypes::undefined))        return parseSuffixes (new Expression (location));
1327 
1328             if (currentType == TokenTypes::literal)
1329             {
1330                 var v (currentValue); skip();
1331                 return parseSuffixes (new LiteralValue (location, v));
1332             }
1333 
1334             if (matchIf (TokenTypes::openBrace))
1335             {
1336                 std::unique_ptr<ObjectDeclaration> e (new ObjectDeclaration (location));
1337 
1338                 while (currentType != TokenTypes::closeBrace)
1339                 {
1340                     auto memberName = currentValue.toString();
1341                     match ((currentType == TokenTypes::literal && currentValue.isString())
1342                              ? TokenTypes::literal : TokenTypes::identifier);
1343                     match (TokenTypes::colon);
1344 
1345                     e->names.add (memberName);
1346                     e->initialisers.add (parseExpression());
1347 
1348                     if (currentType != TokenTypes::closeBrace)
1349                         match (TokenTypes::comma);
1350                 }
1351 
1352                 match (TokenTypes::closeBrace);
1353                 return parseSuffixes (e.release());
1354             }
1355 
1356             if (matchIf (TokenTypes::openBracket))
1357             {
1358                 std::unique_ptr<ArrayDeclaration> e (new ArrayDeclaration (location));
1359 
1360                 while (currentType != TokenTypes::closeBracket)
1361                 {
1362                     e->values.add (parseExpression());
1363 
1364                     if (currentType != TokenTypes::closeBracket)
1365                         match (TokenTypes::comma);
1366                 }
1367 
1368                 match (TokenTypes::closeBracket);
1369                 return parseSuffixes (e.release());
1370             }
1371 
1372             if (matchIf (TokenTypes::function))
1373             {
1374                 Identifier name;
1375                 var fn = parseFunctionDefinition (name);
1376 
1377                 if (name.isValid())
1378                     throwError ("Inline functions definitions cannot have a name");
1379 
1380                 return new LiteralValue (location, fn);
1381             }
1382 
1383             if (matchIf (TokenTypes::new_))
1384             {
1385                 ExpPtr name (new UnqualifiedName (location, parseIdentifier()));
1386 
1387                 while (matchIf (TokenTypes::dot))
1388                     name.reset (new DotOperator (location, name, parseIdentifier()));
1389 
1390                 return parseFunctionCall (new NewOperator (location), name);
1391             }
1392 
1393             throwError ("Found " + getTokenName (currentType) + " when expecting an expression");
1394             return nullptr;
1395         }
1396 
1397         template <typename OpType>
parsePreIncDecjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1398         Expression* parsePreIncDec()
1399         {
1400             Expression* e = parseFactor(); // careful - bare pointer is deliberately aliased
1401             ExpPtr lhs (e), one (new LiteralValue (location, (int) 1));
1402             return new SelfAssignment (location, e, new OpType (location, lhs, one));
1403         }
1404 
1405         template <typename OpType>
parsePostIncDecjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1406         Expression* parsePostIncDec (ExpPtr& lhs)
1407         {
1408             Expression* e = lhs.release(); // careful - bare pointer is deliberately aliased
1409             ExpPtr lhs2 (e), one (new LiteralValue (location, (int) 1));
1410             return new PostAssignment (location, e, new OpType (location, lhs2, one));
1411         }
1412 
parseTypeofjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1413         Expression* parseTypeof()
1414         {
1415             std::unique_ptr<FunctionCall> f (new FunctionCall (location));
1416             f->object.reset (new UnqualifiedName (location, "typeof"));
1417             f->arguments.add (parseUnary());
1418             return f.release();
1419         }
1420 
parseUnaryjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1421         Expression* parseUnary()
1422         {
1423             if (matchIf (TokenTypes::minus))       { ExpPtr a (new LiteralValue (location, (int) 0)), b (parseUnary()); return new SubtractionOp   (location, a, b); }
1424             if (matchIf (TokenTypes::logicalNot))  { ExpPtr a (new LiteralValue (location, (int) 0)), b (parseUnary()); return new EqualsOp        (location, a, b); }
1425             if (matchIf (TokenTypes::plusplus))    return parsePreIncDec<AdditionOp>();
1426             if (matchIf (TokenTypes::minusminus))  return parsePreIncDec<SubtractionOp>();
1427             if (matchIf (TokenTypes::typeof_))     return parseTypeof();
1428 
1429             return parseFactor();
1430         }
1431 
parseMultiplyDividejuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1432         Expression* parseMultiplyDivide()
1433         {
1434             ExpPtr a (parseUnary());
1435 
1436             for (;;)
1437             {
1438                 if (matchIf (TokenTypes::times))        { ExpPtr b (parseUnary()); a.reset (new MultiplyOp (location, a, b)); }
1439                 else if (matchIf (TokenTypes::divide))  { ExpPtr b (parseUnary()); a.reset (new DivideOp   (location, a, b)); }
1440                 else if (matchIf (TokenTypes::modulo))  { ExpPtr b (parseUnary()); a.reset (new ModuloOp   (location, a, b)); }
1441                 else break;
1442             }
1443 
1444             return a.release();
1445         }
1446 
parseAdditionSubtractionjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1447         Expression* parseAdditionSubtraction()
1448         {
1449             ExpPtr a (parseMultiplyDivide());
1450 
1451             for (;;)
1452             {
1453                 if (matchIf (TokenTypes::plus))            { ExpPtr b (parseMultiplyDivide()); a.reset (new AdditionOp    (location, a, b)); }
1454                 else if (matchIf (TokenTypes::minus))      { ExpPtr b (parseMultiplyDivide()); a.reset (new SubtractionOp (location, a, b)); }
1455                 else break;
1456             }
1457 
1458             return a.release();
1459         }
1460 
parseShiftOperatorjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1461         Expression* parseShiftOperator()
1462         {
1463             ExpPtr a (parseAdditionSubtraction());
1464 
1465             for (;;)
1466             {
1467                 if (matchIf (TokenTypes::leftShift))                { ExpPtr b (parseExpression()); a.reset (new LeftShiftOp          (location, a, b)); }
1468                 else if (matchIf (TokenTypes::rightShift))          { ExpPtr b (parseExpression()); a.reset (new RightShiftOp         (location, a, b)); }
1469                 else if (matchIf (TokenTypes::rightShiftUnsigned))  { ExpPtr b (parseExpression()); a.reset (new RightShiftUnsignedOp (location, a, b)); }
1470                 else break;
1471             }
1472 
1473             return a.release();
1474         }
1475 
parseComparatorjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1476         Expression* parseComparator()
1477         {
1478             ExpPtr a (parseShiftOperator());
1479 
1480             for (;;)
1481             {
1482                 if (matchIf (TokenTypes::equals))                  { ExpPtr b (parseShiftOperator()); a.reset (new EqualsOp             (location, a, b)); }
1483                 else if (matchIf (TokenTypes::notEquals))          { ExpPtr b (parseShiftOperator()); a.reset (new NotEqualsOp          (location, a, b)); }
1484                 else if (matchIf (TokenTypes::typeEquals))         { ExpPtr b (parseShiftOperator()); a.reset (new TypeEqualsOp         (location, a, b)); }
1485                 else if (matchIf (TokenTypes::typeNotEquals))      { ExpPtr b (parseShiftOperator()); a.reset (new TypeNotEqualsOp      (location, a, b)); }
1486                 else if (matchIf (TokenTypes::lessThan))           { ExpPtr b (parseShiftOperator()); a.reset (new LessThanOp           (location, a, b)); }
1487                 else if (matchIf (TokenTypes::lessThanOrEqual))    { ExpPtr b (parseShiftOperator()); a.reset (new LessThanOrEqualOp    (location, a, b)); }
1488                 else if (matchIf (TokenTypes::greaterThan))        { ExpPtr b (parseShiftOperator()); a.reset (new GreaterThanOp        (location, a, b)); }
1489                 else if (matchIf (TokenTypes::greaterThanOrEqual)) { ExpPtr b (parseShiftOperator()); a.reset (new GreaterThanOrEqualOp (location, a, b)); }
1490                 else break;
1491             }
1492 
1493             return a.release();
1494         }
1495 
parseLogicOperatorjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1496         Expression* parseLogicOperator()
1497         {
1498             ExpPtr a (parseComparator());
1499 
1500             for (;;)
1501             {
1502                 if (matchIf (TokenTypes::logicalAnd))       { ExpPtr b (parseComparator()); a.reset (new LogicalAndOp (location, a, b)); }
1503                 else if (matchIf (TokenTypes::logicalOr))   { ExpPtr b (parseComparator()); a.reset (new LogicalOrOp  (location, a, b)); }
1504                 else if (matchIf (TokenTypes::bitwiseAnd))  { ExpPtr b (parseComparator()); a.reset (new BitwiseAndOp (location, a, b)); }
1505                 else if (matchIf (TokenTypes::bitwiseOr))   { ExpPtr b (parseComparator()); a.reset (new BitwiseOrOp  (location, a, b)); }
1506                 else if (matchIf (TokenTypes::bitwiseXor))  { ExpPtr b (parseComparator()); a.reset (new BitwiseXorOp (location, a, b)); }
1507                 else break;
1508             }
1509 
1510             return a.release();
1511         }
1512 
parseTernaryOperatorjuce::JavascriptEngine::RootObject::ExpressionTreeBuilder1513         Expression* parseTernaryOperator (ExpPtr& condition)
1514         {
1515             std::unique_ptr<ConditionalOp> e (new ConditionalOp (location));
1516             e->condition.reset (condition.release());
1517             e->trueBranch.reset (parseExpression());
1518             match (TokenTypes::colon);
1519             e->falseBranch.reset (parseExpression());
1520             return e.release();
1521         }
1522 
1523         JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ExpressionTreeBuilder)
1524     };
1525 
1526     //==============================================================================
getjuce::JavascriptEngine::RootObject1527     static var get (Args a, int index) noexcept            { return index < a.numArguments ? a.arguments[index] : var(); }
isIntjuce::JavascriptEngine::RootObject1528     static bool isInt (Args a, int index) noexcept         { return get (a, index).isInt() || get (a, index).isInt64(); }
getIntjuce::JavascriptEngine::RootObject1529     static int getInt (Args a, int index) noexcept         { return get (a, index); }
getDoublejuce::JavascriptEngine::RootObject1530     static double getDouble (Args a, int index) noexcept   { return get (a, index); }
getStringjuce::JavascriptEngine::RootObject1531     static String getString (Args a, int index) noexcept   { return get (a, index).toString(); }
1532 
1533     //==============================================================================
1534     struct ObjectClass  : public DynamicObject
1535     {
ObjectClassjuce::JavascriptEngine::RootObject::ObjectClass1536         ObjectClass()
1537         {
1538             setMethod ("dump",  dump);
1539             setMethod ("clone", cloneFn);
1540         }
1541 
getClassNamejuce::JavascriptEngine::RootObject::ObjectClass1542         static Identifier getClassName()   { static const Identifier i ("Object"); return i; }
dumpjuce::JavascriptEngine::RootObject::ObjectClass1543         static var dump  (Args a)          { DBG (JSON::toString (a.thisObject)); ignoreUnused (a); return var::undefined(); }
cloneFnjuce::JavascriptEngine::RootObject::ObjectClass1544         static var cloneFn (Args a)        { return a.thisObject.clone(); }
1545     };
1546 
1547     //==============================================================================
1548     struct ArrayClass  : public DynamicObject
1549     {
ArrayClassjuce::JavascriptEngine::RootObject::ArrayClass1550         ArrayClass()
1551         {
1552             setMethod ("contains", contains);
1553             setMethod ("remove",   remove);
1554             setMethod ("join",     join);
1555             setMethod ("push",     push);
1556             setMethod ("splice",   splice);
1557             setMethod ("indexOf",  indexOf);
1558         }
1559 
getClassNamejuce::JavascriptEngine::RootObject::ArrayClass1560         static Identifier getClassName()   { static const Identifier i ("Array"); return i; }
1561 
containsjuce::JavascriptEngine::RootObject::ArrayClass1562         static var contains (Args a)
1563         {
1564             if (auto* array = a.thisObject.getArray())
1565                 return array->contains (get (a, 0));
1566 
1567             return false;
1568         }
1569 
removejuce::JavascriptEngine::RootObject::ArrayClass1570         static var remove (Args a)
1571         {
1572             if (auto* array = a.thisObject.getArray())
1573                 array->removeAllInstancesOf (get (a, 0));
1574 
1575             return var::undefined();
1576         }
1577 
joinjuce::JavascriptEngine::RootObject::ArrayClass1578         static var join (Args a)
1579         {
1580             StringArray strings;
1581 
1582             if (auto* array = a.thisObject.getArray())
1583                 for (auto& v : *array)
1584                     strings.add (v.toString());
1585 
1586             return strings.joinIntoString (getString (a, 0));
1587         }
1588 
pushjuce::JavascriptEngine::RootObject::ArrayClass1589         static var push (Args a)
1590         {
1591             if (auto* array = a.thisObject.getArray())
1592             {
1593                 for (int i = 0; i < a.numArguments; ++i)
1594                     array->add (a.arguments[i]);
1595 
1596                 return array->size();
1597             }
1598 
1599             return var::undefined();
1600         }
1601 
splicejuce::JavascriptEngine::RootObject::ArrayClass1602         static var splice (Args a)
1603         {
1604             if (auto* array = a.thisObject.getArray())
1605             {
1606                 auto arraySize = array->size();
1607                 int start = get (a, 0);
1608 
1609                 if (start < 0)
1610                     start = jmax (0, arraySize + start);
1611                 else if (start > arraySize)
1612                     start = arraySize;
1613 
1614                 const int num = a.numArguments > 1 ? jlimit (0, arraySize - start, getInt (a, 1))
1615                                                    : arraySize - start;
1616 
1617                 Array<var> itemsRemoved;
1618                 itemsRemoved.ensureStorageAllocated (num);
1619 
1620                 for (int i = 0; i < num; ++i)
1621                     itemsRemoved.add (array->getReference (start + i));
1622 
1623                 array->removeRange (start, num);
1624 
1625                 for (int i = 2; i < a.numArguments; ++i)
1626                     array->insert (start++, get (a, i));
1627 
1628                 // std::move() needed here for older compilers
1629                 JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wredundant-move")
1630                 return std::move (itemsRemoved);
1631                 JUCE_END_IGNORE_WARNINGS_GCC_LIKE
1632             }
1633 
1634             return var::undefined();
1635         }
1636 
indexOfjuce::JavascriptEngine::RootObject::ArrayClass1637         static var indexOf (Args a)
1638         {
1639             if (auto* array = a.thisObject.getArray())
1640             {
1641                 auto target = get (a, 0);
1642 
1643                 for (int i = (a.numArguments > 1 ? getInt (a, 1) : 0); i < array->size(); ++i)
1644                     if (array->getReference(i) == target)
1645                         return i;
1646             }
1647 
1648             return -1;
1649         }
1650     };
1651 
1652     //==============================================================================
1653     struct StringClass  : public DynamicObject
1654     {
StringClassjuce::JavascriptEngine::RootObject::StringClass1655         StringClass()
1656         {
1657             setMethod ("substring",     substring);
1658             setMethod ("indexOf",       indexOf);
1659             setMethod ("charAt",        charAt);
1660             setMethod ("charCodeAt",    charCodeAt);
1661             setMethod ("fromCharCode",  fromCharCode);
1662             setMethod ("split",         split);
1663         }
1664 
getClassNamejuce::JavascriptEngine::RootObject::StringClass1665         static Identifier getClassName()  { static const Identifier i ("String"); return i; }
1666 
fromCharCodejuce::JavascriptEngine::RootObject::StringClass1667         static var fromCharCode (Args a)  { return String::charToString (static_cast<juce_wchar> (getInt (a, 0))); }
substringjuce::JavascriptEngine::RootObject::StringClass1668         static var substring (Args a)     { return a.thisObject.toString().substring (getInt (a, 0), getInt (a, 1)); }
indexOfjuce::JavascriptEngine::RootObject::StringClass1669         static var indexOf (Args a)       { return a.thisObject.toString().indexOf (getString (a, 0)); }
charCodeAtjuce::JavascriptEngine::RootObject::StringClass1670         static var charCodeAt (Args a)    { return (int) a.thisObject.toString() [getInt (a, 0)]; }
charAtjuce::JavascriptEngine::RootObject::StringClass1671         static var charAt (Args a)        { int p = getInt (a, 0); return a.thisObject.toString().substring (p, p + 1); }
1672 
splitjuce::JavascriptEngine::RootObject::StringClass1673         static var split (Args a)
1674         {
1675             auto str = a.thisObject.toString();
1676             auto sep = getString (a, 0);
1677             StringArray strings;
1678 
1679             if (sep.isNotEmpty())
1680                 strings.addTokens (str, sep.substring (0, 1), {});
1681             else // special-case for empty separator: split all chars separately
1682                 for (auto pos = str.getCharPointer(); ! pos.isEmpty(); ++pos)
1683                     strings.add (String::charToString (*pos));
1684 
1685             var array;
1686 
1687             for (auto& s : strings)
1688                 array.append (s);
1689 
1690             return array;
1691         }
1692     };
1693 
1694     //==============================================================================
1695     struct MathClass  : public DynamicObject
1696     {
MathClassjuce::JavascriptEngine::RootObject::MathClass1697         MathClass()
1698         {
1699             setMethod ("abs",       Math_abs);              setMethod ("round",     Math_round);
1700             setMethod ("random",    Math_random);           setMethod ("randInt",   Math_randInt);
1701             setMethod ("min",       Math_min);              setMethod ("max",       Math_max);
1702             setMethod ("range",     Math_range);            setMethod ("sign",      Math_sign);
1703             setMethod ("toDegrees", Math_toDegrees);        setMethod ("toRadians", Math_toRadians);
1704             setMethod ("sin",       Math_sin);              setMethod ("asin",      Math_asin);
1705             setMethod ("sinh",      Math_sinh);             setMethod ("asinh",     Math_asinh);
1706             setMethod ("cos",       Math_cos);              setMethod ("acos",      Math_acos);
1707             setMethod ("cosh",      Math_cosh);             setMethod ("acosh",     Math_acosh);
1708             setMethod ("tan",       Math_tan);              setMethod ("atan",      Math_atan);
1709             setMethod ("tanh",      Math_tanh);             setMethod ("atanh",     Math_atanh);
1710             setMethod ("log",       Math_log);              setMethod ("log10",     Math_log10);
1711             setMethod ("exp",       Math_exp);              setMethod ("pow",       Math_pow);
1712             setMethod ("sqr",       Math_sqr);              setMethod ("sqrt",      Math_sqrt);
1713             setMethod ("ceil",      Math_ceil);             setMethod ("floor",     Math_floor);
1714 
1715             setProperty ("PI",      MathConstants<double>::pi);
1716             setProperty ("E",       MathConstants<double>::euler);
1717             setProperty ("SQRT2",   MathConstants<double>::sqrt2);
1718             setProperty ("SQRT1_2", std::sqrt (0.5));
1719             setProperty ("LN2",     std::log (2.0));
1720             setProperty ("LN10",    std::log (10.0));
1721             setProperty ("LOG2E",   std::log (MathConstants<double>::euler) / std::log (2.0));
1722             setProperty ("LOG10E",  std::log (MathConstants<double>::euler) / std::log (10.0));
1723         }
1724 
Math_randomjuce::JavascriptEngine::RootObject::MathClass1725         static var Math_random    (Args)   { return Random::getSystemRandom().nextDouble(); }
Math_randIntjuce::JavascriptEngine::RootObject::MathClass1726         static var Math_randInt   (Args a) { return Random::getSystemRandom().nextInt (Range<int> (getInt (a, 0), getInt (a, 1))); }
Math_absjuce::JavascriptEngine::RootObject::MathClass1727         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::MathClass1728         static var Math_round     (Args a) { return isInt (a, 0) ? var (roundToInt (getInt (a, 0))) : var (roundToInt (getDouble (a, 0))); }
Math_signjuce::JavascriptEngine::RootObject::MathClass1729         static var Math_sign      (Args a) { return isInt (a, 0) ? var (sign       (getInt (a, 0))) : var (sign       (getDouble (a, 0))); }
Math_rangejuce::JavascriptEngine::RootObject::MathClass1730         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::MathClass1731         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::MathClass1732         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::MathClass1733         static var Math_toDegrees (Args a) { return radiansToDegrees (getDouble (a, 0)); }
Math_toRadiansjuce::JavascriptEngine::RootObject::MathClass1734         static var Math_toRadians (Args a) { return degreesToRadians (getDouble (a, 0)); }
Math_sinjuce::JavascriptEngine::RootObject::MathClass1735         static var Math_sin       (Args a) { return std::sin   (getDouble (a, 0)); }
Math_asinjuce::JavascriptEngine::RootObject::MathClass1736         static var Math_asin      (Args a) { return std::asin  (getDouble (a, 0)); }
Math_cosjuce::JavascriptEngine::RootObject::MathClass1737         static var Math_cos       (Args a) { return std::cos   (getDouble (a, 0)); }
Math_acosjuce::JavascriptEngine::RootObject::MathClass1738         static var Math_acos      (Args a) { return std::acos  (getDouble (a, 0)); }
Math_sinhjuce::JavascriptEngine::RootObject::MathClass1739         static var Math_sinh      (Args a) { return std::sinh  (getDouble (a, 0)); }
Math_coshjuce::JavascriptEngine::RootObject::MathClass1740         static var Math_cosh      (Args a) { return std::cosh  (getDouble (a, 0)); }
Math_tanjuce::JavascriptEngine::RootObject::MathClass1741         static var Math_tan       (Args a) { return std::tan   (getDouble (a, 0)); }
Math_tanhjuce::JavascriptEngine::RootObject::MathClass1742         static var Math_tanh      (Args a) { return std::tanh  (getDouble (a, 0)); }
Math_atanjuce::JavascriptEngine::RootObject::MathClass1743         static var Math_atan      (Args a) { return std::atan  (getDouble (a, 0)); }
Math_logjuce::JavascriptEngine::RootObject::MathClass1744         static var Math_log       (Args a) { return std::log   (getDouble (a, 0)); }
Math_log10juce::JavascriptEngine::RootObject::MathClass1745         static var Math_log10     (Args a) { return std::log10 (getDouble (a, 0)); }
Math_expjuce::JavascriptEngine::RootObject::MathClass1746         static var Math_exp       (Args a) { return std::exp   (getDouble (a, 0)); }
Math_powjuce::JavascriptEngine::RootObject::MathClass1747         static var Math_pow       (Args a) { return std::pow   (getDouble (a, 0), getDouble (a, 1)); }
Math_sqrjuce::JavascriptEngine::RootObject::MathClass1748         static var Math_sqr       (Args a) { return square (getDouble (a, 0)); }
Math_sqrtjuce::JavascriptEngine::RootObject::MathClass1749         static var Math_sqrt      (Args a) { return std::sqrt  (getDouble (a, 0)); }
Math_ceiljuce::JavascriptEngine::RootObject::MathClass1750         static var Math_ceil      (Args a) { return std::ceil  (getDouble (a, 0)); }
Math_floorjuce::JavascriptEngine::RootObject::MathClass1751         static var Math_floor     (Args a) { return std::floor (getDouble (a, 0)); }
1752 
1753         // We can't use the std namespace equivalents of these functions without breaking
1754         // compatibility with older versions of OS X.
Math_asinhjuce::JavascriptEngine::RootObject::MathClass1755         static var Math_asinh     (Args a) { return asinh (getDouble (a, 0)); }
Math_acoshjuce::JavascriptEngine::RootObject::MathClass1756         static var Math_acosh     (Args a) { return acosh (getDouble (a, 0)); }
Math_atanhjuce::JavascriptEngine::RootObject::MathClass1757         static var Math_atanh     (Args a) { return atanh (getDouble (a, 0)); }
1758 
getClassNamejuce::JavascriptEngine::RootObject::MathClass1759         static Identifier getClassName()   { static const Identifier i ("Math"); return i; }
signjuce::JavascriptEngine::RootObject::MathClass1760         template <typename Type> static Type sign (Type n) noexcept  { return n > 0 ? (Type) 1 : (n < 0 ? (Type) -1 : 0); }
1761     };
1762 
1763     //==============================================================================
1764     struct JSONClass  : public DynamicObject
1765     {
JSONClassjuce::JavascriptEngine::RootObject::JSONClass1766         JSONClass()                        { setMethod ("stringify", stringify); }
getClassNamejuce::JavascriptEngine::RootObject::JSONClass1767         static Identifier getClassName()   { static const Identifier i ("JSON"); return i; }
stringifyjuce::JavascriptEngine::RootObject::JSONClass1768         static var stringify (Args a)      { return JSON::toString (get (a, 0)); }
1769     };
1770 
1771     //==============================================================================
1772     struct IntegerClass  : public DynamicObject
1773     {
IntegerClassjuce::JavascriptEngine::RootObject::IntegerClass1774         IntegerClass()                     { setMethod ("parseInt",  parseInt); }
getClassNamejuce::JavascriptEngine::RootObject::IntegerClass1775         static Identifier getClassName()   { static const Identifier i ("Integer"); return i; }
1776 
parseIntjuce::JavascriptEngine::RootObject::IntegerClass1777         static var parseInt (Args a)
1778         {
1779             auto s = getString (a, 0).trim();
1780 
1781             return s[0] == '0' ? (s[1] == 'x' ? s.substring(2).getHexValue64() : getOctalValue (s))
1782                                : s.getLargeIntValue();
1783         }
1784     };
1785 
1786     //==============================================================================
tracejuce::JavascriptEngine::RootObject1787     static var trace (Args a)        { Logger::outputDebugString (JSON::toString (a.thisObject)); return var::undefined(); }
charToIntjuce::JavascriptEngine::RootObject1788     static var charToInt (Args a)    { return (int) (getString (a, 0)[0]); }
parseFloatjuce::JavascriptEngine::RootObject1789     static var parseFloat (Args a)   { return getDouble (a, 0); }
1790 
typeof_internaljuce::JavascriptEngine::RootObject1791     static var typeof_internal (Args a)
1792     {
1793         var v (get (a, 0));
1794 
1795         if (v.isVoid())                      return "void";
1796         if (v.isString())                    return "string";
1797         if (isNumeric (v))                   return "number";
1798         if (isFunction (v) || v.isMethod())  return "function";
1799         if (v.isObject())                    return "object";
1800 
1801         return "undefined";
1802     }
1803 
execjuce::JavascriptEngine::RootObject1804     static var exec (Args a)
1805     {
1806         if (auto* root = dynamic_cast<RootObject*> (a.thisObject.getObject()))
1807             root->execute (getString (a, 0));
1808 
1809         return var::undefined();
1810     }
1811 
evaljuce::JavascriptEngine::RootObject1812     static var eval (Args a)
1813     {
1814         if (auto* root = dynamic_cast<RootObject*> (a.thisObject.getObject()))
1815             return root->evaluate (getString (a, 0));
1816 
1817         return var::undefined();
1818     }
1819 };
1820 
1821 //==============================================================================
JavascriptEngine()1822 JavascriptEngine::JavascriptEngine()  : maximumExecutionTime (15.0), root (new RootObject())
1823 {
1824     registerNativeObject (RootObject::ObjectClass  ::getClassName(),  new RootObject::ObjectClass());
1825     registerNativeObject (RootObject::ArrayClass   ::getClassName(),  new RootObject::ArrayClass());
1826     registerNativeObject (RootObject::StringClass  ::getClassName(),  new RootObject::StringClass());
1827     registerNativeObject (RootObject::MathClass    ::getClassName(),  new RootObject::MathClass());
1828     registerNativeObject (RootObject::JSONClass    ::getClassName(),  new RootObject::JSONClass());
1829     registerNativeObject (RootObject::IntegerClass ::getClassName(),  new RootObject::IntegerClass());
1830 }
1831 
~JavascriptEngine()1832 JavascriptEngine::~JavascriptEngine() {}
1833 
prepareTimeout() const1834 void JavascriptEngine::prepareTimeout() const noexcept   { root->timeout = Time::getCurrentTime() + maximumExecutionTime; }
stop()1835 void JavascriptEngine::stop() noexcept                   { root->timeout = {}; }
1836 
registerNativeObject(const Identifier & name,DynamicObject * object)1837 void JavascriptEngine::registerNativeObject (const Identifier& name, DynamicObject* object)
1838 {
1839     root->setProperty (name, object);
1840 }
1841 
execute(const String & code)1842 Result JavascriptEngine::execute (const String& code)
1843 {
1844     try
1845     {
1846         prepareTimeout();
1847         root->execute (code);
1848     }
1849     catch (String& error)
1850     {
1851         return Result::fail (error);
1852     }
1853 
1854     return Result::ok();
1855 }
1856 
evaluate(const String & code,Result * result)1857 var JavascriptEngine::evaluate (const String& code, Result* result)
1858 {
1859     try
1860     {
1861         prepareTimeout();
1862         if (result != nullptr) *result = Result::ok();
1863         return root->evaluate (code);
1864     }
1865     catch (String& error)
1866     {
1867         if (result != nullptr) *result = Result::fail (error);
1868     }
1869 
1870     return var::undefined();
1871 }
1872 
callFunction(const Identifier & function,const var::NativeFunctionArgs & args,Result * result)1873 var JavascriptEngine::callFunction (const Identifier& function, const var::NativeFunctionArgs& args, Result* result)
1874 {
1875     auto returnVal = var::undefined();
1876 
1877     try
1878     {
1879         prepareTimeout();
1880         if (result != nullptr) *result = Result::ok();
1881         RootObject::Scope ({}, *root, *root).findAndInvokeMethod (function, args, returnVal);
1882     }
1883     catch (String& error)
1884     {
1885         if (result != nullptr) *result = Result::fail (error);
1886     }
1887 
1888     return returnVal;
1889 }
1890 
callFunctionObject(DynamicObject * objectScope,const var & functionObject,const var::NativeFunctionArgs & args,Result * result)1891 var JavascriptEngine::callFunctionObject (DynamicObject* objectScope, const var& functionObject,
1892                                           const var::NativeFunctionArgs& args, Result* result)
1893 {
1894     auto returnVal = var::undefined();
1895 
1896     try
1897     {
1898         prepareTimeout();
1899         if (result != nullptr) *result = Result::ok();
1900         RootObject::Scope rootScope ({}, *root, *root);
1901         RootObject::Scope (&rootScope, *root, DynamicObject::Ptr (objectScope))
1902             .invokeMethod (functionObject, args, returnVal);
1903     }
1904     catch (String& error)
1905     {
1906         if (result != nullptr) *result = Result::fail (error);
1907     }
1908 
1909     return returnVal;
1910 }
1911 
getRootObjectProperties() const1912 const NamedValueSet& JavascriptEngine::getRootObjectProperties() const noexcept
1913 {
1914     return root->getProperties();
1915 }
1916 
1917 JUCE_END_IGNORE_WARNINGS_MSVC
1918 
1919 } // namespace juce
1920