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