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