ExpressionIntepreter.cs 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167
  1. using System;
  2. using System.Collections.Generic;
  3. using Esprima;
  4. using Esprima.Ast;
  5. using Jint.Native;
  6. using Jint.Native.Function;
  7. using Jint.Native.Number;
  8. using Jint.Runtime.Descriptors;
  9. using Jint.Runtime.Descriptors.Specialized;
  10. using Jint.Runtime.Environments;
  11. using Jint.Runtime.Interop;
  12. using Jint.Runtime.References;
  13. namespace Jint.Runtime
  14. {
  15. public class ExpressionInterpreter
  16. {
  17. private readonly Engine _engine;
  18. private readonly bool _isDebugMode;
  19. private readonly int _maxRecursionDepth;
  20. private readonly IReferenceResolver _referenceResolver;
  21. public ExpressionInterpreter(Engine engine)
  22. {
  23. _engine = engine;
  24. // gather some options as fields for faster checks
  25. _isDebugMode = engine.Options.IsDebugMode;
  26. _maxRecursionDepth = engine.Options.MaxRecursionDepth;
  27. _referenceResolver = engine.Options.ReferenceResolver;
  28. }
  29. private object EvaluateExpression(Expression expression)
  30. {
  31. return _engine.EvaluateExpression(expression);
  32. }
  33. public JsValue EvaluateConditionalExpression(ConditionalExpression conditionalExpression)
  34. {
  35. var lref = _engine.EvaluateExpression(conditionalExpression.Test);
  36. if (TypeConverter.ToBoolean(_engine.GetValue(lref, true)))
  37. {
  38. var trueRef = _engine.EvaluateExpression(conditionalExpression.Consequent);
  39. return _engine.GetValue(trueRef, true);
  40. }
  41. else
  42. {
  43. var falseRef = _engine.EvaluateExpression(conditionalExpression.Alternate);
  44. return _engine.GetValue(falseRef, true);
  45. }
  46. }
  47. public JsValue EvaluateAssignmentExpression(AssignmentExpression assignmentExpression)
  48. {
  49. var lref = EvaluateExpression((Expression) assignmentExpression.Left) as Reference;
  50. JsValue rval = _engine.GetValue(EvaluateExpression(assignmentExpression.Right), true);
  51. if (lref == null)
  52. {
  53. throw new JavaScriptException(_engine.ReferenceError);
  54. }
  55. if (assignmentExpression.Operator == AssignmentOperator.Assign) // "="
  56. {
  57. if(lref.IsStrict()
  58. && lref.GetBase() is EnvironmentRecord
  59. && (lref.GetReferencedName() == "eval" || lref.GetReferencedName() == "arguments"))
  60. {
  61. throw new JavaScriptException(_engine.SyntaxError);
  62. }
  63. _engine.PutValue(lref, rval);
  64. _engine.ReferencePool.Return(lref);
  65. return rval;
  66. }
  67. JsValue lval = _engine.GetValue(lref);
  68. switch (assignmentExpression.Operator)
  69. {
  70. case AssignmentOperator.PlusAssign:
  71. var lprim = TypeConverter.ToPrimitive(lval);
  72. var rprim = TypeConverter.ToPrimitive(rval);
  73. if (lprim.IsString() || rprim.IsString())
  74. {
  75. if (!(lprim is JsString jsString))
  76. {
  77. jsString = new JsString.ConcatenatedString(TypeConverter.ToString(lprim));
  78. }
  79. lval = jsString.Append(rprim);
  80. }
  81. else
  82. {
  83. lval = TypeConverter.ToNumber(lprim) + TypeConverter.ToNumber(rprim);
  84. }
  85. break;
  86. case AssignmentOperator.MinusAssign:
  87. lval = TypeConverter.ToNumber(lval) - TypeConverter.ToNumber(rval);
  88. break;
  89. case AssignmentOperator.TimesAssign:
  90. if (lval.IsUndefined() || rval.IsUndefined())
  91. {
  92. lval = Undefined.Instance;
  93. }
  94. else
  95. {
  96. lval = TypeConverter.ToNumber(lval) * TypeConverter.ToNumber(rval);
  97. }
  98. break;
  99. case AssignmentOperator.DivideAssign:
  100. lval = Divide(lval, rval);
  101. break;
  102. case AssignmentOperator.ModuloAssign:
  103. if (lval.IsUndefined() || rval.IsUndefined())
  104. {
  105. lval = Undefined.Instance;
  106. }
  107. else
  108. {
  109. lval = TypeConverter.ToNumber(lval) % TypeConverter.ToNumber(rval);
  110. }
  111. break;
  112. case AssignmentOperator.BitwiseAndAssign:
  113. lval = TypeConverter.ToInt32(lval) & TypeConverter.ToInt32(rval);
  114. break;
  115. case AssignmentOperator.BitwiseOrAssign:
  116. lval = TypeConverter.ToInt32(lval) | TypeConverter.ToInt32(rval);
  117. break;
  118. case AssignmentOperator.BitwiseXOrAssign:
  119. lval = TypeConverter.ToInt32(lval) ^ TypeConverter.ToInt32(rval);
  120. break;
  121. case AssignmentOperator.LeftShiftAssign:
  122. lval = TypeConverter.ToInt32(lval) << (int)(TypeConverter.ToUint32(rval) & 0x1F);
  123. break;
  124. case AssignmentOperator.RightShiftAssign:
  125. lval = TypeConverter.ToInt32(lval) >> (int)(TypeConverter.ToUint32(rval) & 0x1F);
  126. break;
  127. case AssignmentOperator.UnsignedRightShiftAssign:
  128. lval = (uint)TypeConverter.ToInt32(lval) >> (int)(TypeConverter.ToUint32(rval) & 0x1F);
  129. break;
  130. default:
  131. throw new NotImplementedException();
  132. }
  133. _engine.PutValue(lref, lval);
  134. _engine.ReferencePool.Return(lref);
  135. return lval;
  136. }
  137. private JsValue Divide(JsValue lval, JsValue rval)
  138. {
  139. if (lval.IsUndefined() || rval.IsUndefined())
  140. {
  141. return Undefined.Instance;
  142. }
  143. else
  144. {
  145. var lN = TypeConverter.ToNumber(lval);
  146. var rN = TypeConverter.ToNumber(rval);
  147. if (double.IsNaN(rN) || double.IsNaN(lN))
  148. {
  149. return double.NaN;
  150. }
  151. if (double.IsInfinity(lN) && double.IsInfinity(rN))
  152. {
  153. return double.NaN;
  154. }
  155. if (double.IsInfinity(lN) && rN.Equals(0))
  156. {
  157. if (NumberInstance.IsNegativeZero(rN))
  158. {
  159. return -lN;
  160. }
  161. return lN;
  162. }
  163. if (lN.Equals(0) && rN.Equals(0))
  164. {
  165. return double.NaN;
  166. }
  167. if (rN.Equals(0))
  168. {
  169. if (NumberInstance.IsNegativeZero(rN))
  170. {
  171. return lN > 0 ? -double.PositiveInfinity : -double.NegativeInfinity;
  172. }
  173. return lN > 0 ? double.PositiveInfinity : double.NegativeInfinity;
  174. }
  175. return lN/rN;
  176. }
  177. }
  178. public JsValue EvaluateBinaryExpression(BinaryExpression expression)
  179. {
  180. JsValue left;
  181. if (expression.Left.Type == Nodes.Literal)
  182. {
  183. left = EvaluateLiteral((Literal) expression.Left);
  184. }
  185. else
  186. {
  187. left = _engine.GetValue(EvaluateExpression(expression.Left), true);
  188. }
  189. JsValue right;
  190. if (expression.Right.Type == Nodes.Literal)
  191. {
  192. right = EvaluateLiteral((Literal) expression.Right);
  193. }
  194. else
  195. {
  196. right = _engine.GetValue(EvaluateExpression(expression.Right), true);
  197. }
  198. JsValue value;
  199. switch (expression.Operator)
  200. {
  201. case BinaryOperator.Plus:
  202. var lprim = TypeConverter.ToPrimitive(left);
  203. var rprim = TypeConverter.ToPrimitive(right);
  204. if (lprim.IsString() || rprim.IsString())
  205. {
  206. value = TypeConverter.ToString(lprim) + TypeConverter.ToString(rprim);
  207. }
  208. else
  209. {
  210. value = TypeConverter.ToNumber(lprim) + TypeConverter.ToNumber(rprim);
  211. }
  212. break;
  213. case BinaryOperator.Minus:
  214. value = TypeConverter.ToNumber(left) - TypeConverter.ToNumber(right);
  215. break;
  216. case BinaryOperator.Times:
  217. if (left.IsUndefined() || right.IsUndefined())
  218. {
  219. value = Undefined.Instance;
  220. }
  221. else
  222. {
  223. value = TypeConverter.ToNumber(left) * TypeConverter.ToNumber(right);
  224. }
  225. break;
  226. case BinaryOperator.Divide:
  227. value = Divide(left, right);
  228. break;
  229. case BinaryOperator.Modulo:
  230. if (left.IsUndefined() || right.IsUndefined())
  231. {
  232. value = Undefined.Instance;
  233. }
  234. else
  235. {
  236. value = TypeConverter.ToNumber(left) % TypeConverter.ToNumber(right);
  237. }
  238. break;
  239. case BinaryOperator.Equal:
  240. value = Equal(left, right);
  241. break;
  242. case BinaryOperator.NotEqual:
  243. value = !Equal(left, right);
  244. break;
  245. case BinaryOperator.Greater:
  246. value = Compare(right, left, false);
  247. if (value.IsUndefined())
  248. {
  249. value = false;
  250. }
  251. break;
  252. case BinaryOperator.GreaterOrEqual:
  253. value = Compare(left, right);
  254. if (value.IsUndefined() || ((JsBoolean) value)._value)
  255. {
  256. value = false;
  257. }
  258. else
  259. {
  260. value = true;
  261. }
  262. break;
  263. case BinaryOperator.Less:
  264. value = Compare(left, right);
  265. if (value.IsUndefined())
  266. {
  267. value = false;
  268. }
  269. break;
  270. case BinaryOperator.LessOrEqual:
  271. value = Compare(right, left, false);
  272. if (value.IsUndefined() || ((JsBoolean) value)._value)
  273. {
  274. value = false;
  275. }
  276. else
  277. {
  278. value = true;
  279. }
  280. break;
  281. case BinaryOperator.StrictlyEqual:
  282. return StrictlyEqual(left, right);
  283. case BinaryOperator.StricltyNotEqual:
  284. return !StrictlyEqual(left, right);
  285. case BinaryOperator.BitwiseAnd:
  286. return TypeConverter.ToInt32(left) & TypeConverter.ToInt32(right);
  287. case BinaryOperator.BitwiseOr:
  288. return TypeConverter.ToInt32(left) | TypeConverter.ToInt32(right);
  289. case BinaryOperator.BitwiseXOr:
  290. return TypeConverter.ToInt32(left) ^ TypeConverter.ToInt32(right);
  291. case BinaryOperator.LeftShift:
  292. return TypeConverter.ToInt32(left) << (int)(TypeConverter.ToUint32(right) & 0x1F);
  293. case BinaryOperator.RightShift:
  294. return TypeConverter.ToInt32(left) >> (int)(TypeConverter.ToUint32(right) & 0x1F);
  295. case BinaryOperator.UnsignedRightShift:
  296. return (uint)TypeConverter.ToInt32(left) >> (int)(TypeConverter.ToUint32(right) & 0x1F);
  297. case BinaryOperator.InstanceOf:
  298. var f = right.TryCast<FunctionInstance>();
  299. if (ReferenceEquals(f, null))
  300. {
  301. throw new JavaScriptException(_engine.TypeError, "instanceof can only be used with a function object");
  302. }
  303. value = f.HasInstance(left);
  304. break;
  305. case BinaryOperator.In:
  306. if (!right.IsObject())
  307. {
  308. throw new JavaScriptException(_engine.TypeError, "in can only be used with an object");
  309. }
  310. value = right.AsObject().HasProperty(TypeConverter.ToString(left));
  311. break;
  312. default:
  313. throw new NotImplementedException();
  314. }
  315. return value;
  316. }
  317. public JsValue EvaluateLogicalExpression(BinaryExpression binaryExpression)
  318. {
  319. var left = _engine.GetValue(EvaluateExpression(binaryExpression.Left), true);
  320. switch (binaryExpression.Operator)
  321. {
  322. case BinaryOperator.LogicalAnd:
  323. if (!TypeConverter.ToBoolean(left))
  324. {
  325. return left;
  326. }
  327. return _engine.GetValue(EvaluateExpression(binaryExpression.Right), true);
  328. case BinaryOperator.LogicalOr:
  329. if (TypeConverter.ToBoolean(left))
  330. {
  331. return left;
  332. }
  333. return _engine.GetValue(EvaluateExpression(binaryExpression.Right), true);
  334. default:
  335. throw new NotImplementedException();
  336. }
  337. }
  338. private static bool Equal(JsValue x, JsValue y)
  339. {
  340. var typex = x.Type;
  341. var typey = y.Type;
  342. if (typex == typey)
  343. {
  344. return StrictlyEqual(x, y);
  345. }
  346. if (x.IsNull() && y.IsUndefined())
  347. {
  348. return true;
  349. }
  350. if (x.IsUndefined() && y.IsNull())
  351. {
  352. return true;
  353. }
  354. if (typex == Types.Number && typey == Types.String)
  355. {
  356. return Equal(x, TypeConverter.ToNumber(y));
  357. }
  358. if (typex == Types.String && typey == Types.Number)
  359. {
  360. return Equal(TypeConverter.ToNumber(x), y);
  361. }
  362. if (typex == Types.Boolean)
  363. {
  364. return Equal(TypeConverter.ToNumber(x), y);
  365. }
  366. if (typey == Types.Boolean)
  367. {
  368. return Equal(x, TypeConverter.ToNumber(y));
  369. }
  370. if (typey == Types.Object && (typex == Types.String || typex == Types.Number))
  371. {
  372. return Equal(x, TypeConverter.ToPrimitive(y));
  373. }
  374. if (typex == Types.Object && (typey == Types.String || typey == Types.Number))
  375. {
  376. return Equal(TypeConverter.ToPrimitive(x), y);
  377. }
  378. return false;
  379. }
  380. public static bool StrictlyEqual(JsValue x, JsValue y)
  381. {
  382. var typea = x.Type;
  383. var typeb = y.Type;
  384. if (typea != typeb)
  385. {
  386. return false;
  387. }
  388. if (typea == Types.Undefined || typea == Types.Null)
  389. {
  390. return true;
  391. }
  392. if (typea == Types.Number)
  393. {
  394. var nx = ((JsNumber) x)._value;
  395. var ny = ((JsNumber) y)._value;
  396. if (double.IsNaN(nx) || double.IsNaN(ny))
  397. {
  398. return false;
  399. }
  400. if (nx.Equals(ny))
  401. {
  402. return true;
  403. }
  404. return false;
  405. }
  406. if (typea == Types.String)
  407. {
  408. return x.AsStringWithoutTypeCheck() == y.AsStringWithoutTypeCheck();
  409. }
  410. if (typea == Types.Boolean)
  411. {
  412. return ((JsBoolean) x)._value == ((JsBoolean) y)._value;
  413. }
  414. if (typea == Types.Object)
  415. {
  416. if (x.AsObject() is IObjectWrapper xw)
  417. {
  418. var yw = y.AsObject() as IObjectWrapper;
  419. return Equals(xw.Target, yw.Target);
  420. }
  421. }
  422. if (typea == Types.None)
  423. {
  424. return true;
  425. }
  426. return x == y;
  427. }
  428. public static bool SameValue(JsValue x, JsValue y)
  429. {
  430. var typea = TypeConverter.GetPrimitiveType(x);
  431. var typeb = TypeConverter.GetPrimitiveType(y);
  432. if (typea != typeb)
  433. {
  434. return false;
  435. }
  436. if (typea == Types.None)
  437. {
  438. return true;
  439. }
  440. if (typea == Types.Number)
  441. {
  442. var nx = TypeConverter.ToNumber(x);
  443. var ny = TypeConverter.ToNumber(y);
  444. if (double.IsNaN(nx) && double.IsNaN(ny))
  445. {
  446. return true;
  447. }
  448. if (nx.Equals(ny))
  449. {
  450. if (nx.Equals(0))
  451. {
  452. // +0 !== -0
  453. return NumberInstance.IsNegativeZero(nx) == NumberInstance.IsNegativeZero(ny);
  454. }
  455. return true;
  456. }
  457. return false;
  458. }
  459. if (typea == Types.String)
  460. {
  461. return TypeConverter.ToString(x) == TypeConverter.ToString(y);
  462. }
  463. if (typea == Types.Boolean)
  464. {
  465. return TypeConverter.ToBoolean(x) == TypeConverter.ToBoolean(y);
  466. }
  467. return x == y;
  468. }
  469. public static JsValue Compare(JsValue x, JsValue y, bool leftFirst = true)
  470. {
  471. JsValue px, py;
  472. if (leftFirst)
  473. {
  474. px = TypeConverter.ToPrimitive(x, Types.Number);
  475. py = TypeConverter.ToPrimitive(y, Types.Number);
  476. }
  477. else
  478. {
  479. py = TypeConverter.ToPrimitive(y, Types.Number);
  480. px = TypeConverter.ToPrimitive(x, Types.Number);
  481. }
  482. var typea = px.Type;
  483. var typeb = py.Type;
  484. if (typea != Types.String || typeb != Types.String)
  485. {
  486. var nx = TypeConverter.ToNumber(px);
  487. var ny = TypeConverter.ToNumber(py);
  488. if (double.IsNaN(nx) || double.IsNaN(ny))
  489. {
  490. return Undefined.Instance;
  491. }
  492. if (nx.Equals(ny))
  493. {
  494. return false;
  495. }
  496. if (double.IsPositiveInfinity(nx))
  497. {
  498. return false;
  499. }
  500. if (double.IsPositiveInfinity(ny))
  501. {
  502. return true;
  503. }
  504. if (double.IsNegativeInfinity(ny))
  505. {
  506. return false;
  507. }
  508. if (double.IsNegativeInfinity(nx))
  509. {
  510. return true;
  511. }
  512. return nx < ny;
  513. }
  514. else
  515. {
  516. return String.CompareOrdinal(TypeConverter.ToString(x), TypeConverter.ToString(y)) < 0;
  517. }
  518. }
  519. public Reference EvaluateIdentifier(Identifier identifier)
  520. {
  521. var env = _engine.ExecutionContext.LexicalEnvironment;
  522. var strict = StrictModeScope.IsStrictModeCode;
  523. return LexicalEnvironment.GetIdentifierReference(env, identifier.Name, strict);
  524. }
  525. public JsValue EvaluateLiteral(Literal literal)
  526. {
  527. switch (literal.TokenType)
  528. {
  529. case TokenType.BooleanLiteral:
  530. return literal.BooleanValue ? JsBoolean.True : JsBoolean.False;
  531. case TokenType.NullLiteral:
  532. return JsValue.Null;
  533. case TokenType.NumericLiteral:
  534. // implicit conversion operator goes through caching
  535. return literal.NumericValue;
  536. case TokenType.StringLiteral:
  537. // implicit conversion operator goes through caching
  538. return literal.StringValue;
  539. }
  540. if (literal.RegexValue != null) //(literal.Type == Nodes.RegularExpressionLiteral)
  541. {
  542. return _engine.RegExp.Construct(literal.RegexValue, literal.Regex.Flags);
  543. }
  544. return JsValue.FromObject(_engine, literal.Value);
  545. }
  546. public JsValue EvaluateObjectExpression(ObjectExpression objectExpression)
  547. {
  548. // http://www.ecma-international.org/ecma-262/5.1/#sec-11.1.5
  549. var obj = _engine.Object.Construct(Arguments.Empty);
  550. var propertiesCount = objectExpression.Properties.Count;
  551. for (var i = 0; i < propertiesCount; i++)
  552. {
  553. var property = objectExpression.Properties[i];
  554. var propName = property.Key.GetKey();
  555. var previous = obj.GetOwnProperty(propName);
  556. PropertyDescriptor propDesc;
  557. const PropertyFlag enumerableConfigurable = PropertyFlag.Enumerable | PropertyFlag.Configurable;
  558. switch (property.Kind)
  559. {
  560. case PropertyKind.Init:
  561. case PropertyKind.Data:
  562. var exprValue = _engine.EvaluateExpression(property.Value);
  563. var propValue = _engine.GetValue(exprValue, true);
  564. propDesc = new PropertyDescriptor(propValue, PropertyFlag.ConfigurableEnumerableWritable);
  565. break;
  566. case PropertyKind.Get:
  567. var getter = property.Value as IFunction;
  568. if (getter == null)
  569. {
  570. throw new JavaScriptException(_engine.SyntaxError);
  571. }
  572. ScriptFunctionInstance get;
  573. using (new StrictModeScope(getter.Strict))
  574. {
  575. get = new ScriptFunctionInstance(
  576. _engine,
  577. getter,
  578. _engine.ExecutionContext.LexicalEnvironment,
  579. StrictModeScope.IsStrictModeCode
  580. );
  581. }
  582. propDesc = new GetSetPropertyDescriptor(get: get, set: null, enumerableConfigurable);
  583. break;
  584. case PropertyKind.Set:
  585. var setter = property.Value as IFunction;
  586. if (setter == null)
  587. {
  588. throw new JavaScriptException(_engine.SyntaxError);
  589. }
  590. ScriptFunctionInstance set;
  591. using (new StrictModeScope(setter.Strict))
  592. {
  593. set = new ScriptFunctionInstance(
  594. _engine,
  595. setter,
  596. _engine.ExecutionContext.LexicalEnvironment,
  597. StrictModeScope.IsStrictModeCode
  598. );
  599. }
  600. propDesc = new GetSetPropertyDescriptor(get: null, set: set, enumerableConfigurable);
  601. break;
  602. default:
  603. throw new ArgumentOutOfRangeException();
  604. }
  605. if (previous != PropertyDescriptor.Undefined)
  606. {
  607. if (StrictModeScope.IsStrictModeCode && previous.IsDataDescriptor() && propDesc.IsDataDescriptor())
  608. {
  609. throw new JavaScriptException(_engine.SyntaxError);
  610. }
  611. if (previous.IsDataDescriptor() && propDesc.IsAccessorDescriptor())
  612. {
  613. throw new JavaScriptException(_engine.SyntaxError);
  614. }
  615. if (previous.IsAccessorDescriptor() && propDesc.IsDataDescriptor())
  616. {
  617. throw new JavaScriptException(_engine.SyntaxError);
  618. }
  619. if (previous.IsAccessorDescriptor() && propDesc.IsAccessorDescriptor())
  620. {
  621. if (!ReferenceEquals(propDesc.Set, null) && !ReferenceEquals(previous.Set, null))
  622. {
  623. throw new JavaScriptException(_engine.SyntaxError);
  624. }
  625. if (!ReferenceEquals(propDesc.Get, null) && !ReferenceEquals(previous.Get, null))
  626. {
  627. throw new JavaScriptException(_engine.SyntaxError);
  628. }
  629. }
  630. }
  631. obj.DefineOwnProperty(propName, propDesc, false);
  632. }
  633. return obj;
  634. }
  635. /// <summary>
  636. /// http://www.ecma-international.org/ecma-262/5.1/#sec-11.2.1
  637. /// </summary>
  638. /// <param name="memberExpression"></param>
  639. /// <returns></returns>
  640. public Reference EvaluateMemberExpression(MemberExpression memberExpression)
  641. {
  642. var baseReference = EvaluateExpression(memberExpression.Object);
  643. var baseValue = _engine.GetValue(baseReference);
  644. string propertyNameString;
  645. if (!memberExpression.Computed) // index accessor ?
  646. {
  647. // we can take fast path without querying the engine again
  648. propertyNameString = ((Identifier) memberExpression.Property).Name;
  649. }
  650. else
  651. {
  652. var propertyNameReference = EvaluateExpression(memberExpression.Property);
  653. var propertyNameValue = _engine.GetValue(propertyNameReference, true);
  654. propertyNameString = TypeConverter.ToString(propertyNameValue);
  655. }
  656. TypeConverter.CheckObjectCoercible(_engine, baseValue, memberExpression, baseReference);
  657. if (baseReference is Reference r)
  658. {
  659. _engine.ReferencePool.Return(r);
  660. }
  661. return _engine.ReferencePool.Rent(baseValue, propertyNameString, StrictModeScope.IsStrictModeCode);
  662. }
  663. public JsValue EvaluateFunctionExpression(IFunction functionExpression)
  664. {
  665. var funcEnv = LexicalEnvironment.NewDeclarativeEnvironment(_engine, _engine.ExecutionContext.LexicalEnvironment);
  666. var envRec = (DeclarativeEnvironmentRecord)funcEnv.Record;
  667. var closure = new ScriptFunctionInstance(
  668. _engine,
  669. functionExpression,
  670. funcEnv,
  671. functionExpression.Strict
  672. );
  673. if (!string.IsNullOrEmpty(functionExpression.Id?.Name))
  674. {
  675. envRec.CreateMutableBinding(functionExpression.Id.Name, closure);
  676. }
  677. return closure;
  678. }
  679. public JsValue EvaluateCallExpression(CallExpression callExpression)
  680. {
  681. var callee = EvaluateExpression(callExpression.Callee);
  682. if (_isDebugMode)
  683. {
  684. _engine.DebugHandler.AddToDebugCallStack(callExpression);
  685. }
  686. JsValue thisObject;
  687. // todo: implement as in http://www.ecma-international.org/ecma-262/5.1/#sec-11.2.4
  688. var arguments = Array.Empty<JsValue>();
  689. if (callExpression.Cached)
  690. {
  691. arguments = (JsValue[]) callExpression.CachedArguments;
  692. }
  693. else
  694. {
  695. var allLiteral = true;
  696. if (callExpression.Arguments.Count > 0)
  697. {
  698. arguments = _engine.JsValueArrayPool.RentArray(callExpression.Arguments.Count);
  699. BuildArguments(callExpression.Arguments, arguments, out allLiteral);
  700. }
  701. if (callExpression.CanBeCached)
  702. {
  703. // The arguments array can be cached if they are all literals
  704. if (allLiteral)
  705. {
  706. callExpression.CachedArguments = arguments;
  707. callExpression.Cached = true;
  708. }
  709. else
  710. {
  711. callExpression.CanBeCached = false;
  712. }
  713. }
  714. }
  715. var func = _engine.GetValue(callee);
  716. var r = callee as Reference;
  717. if (_maxRecursionDepth >= 0)
  718. {
  719. var stackItem = new CallStackElement(callExpression, func, r != null ? r.GetReferencedName() : "anonymous function");
  720. var recursionDepth = _engine.CallStack.Push(stackItem);
  721. if (recursionDepth > _maxRecursionDepth)
  722. {
  723. _engine.CallStack.Pop();
  724. throw new RecursionDepthOverflowException(_engine.CallStack, stackItem.ToString());
  725. }
  726. }
  727. if (func.IsUndefined())
  728. {
  729. throw new JavaScriptException(_engine.TypeError, r == null ? "" : string.Format("Object has no method '{0}'", r.GetReferencedName()));
  730. }
  731. if (!func.IsObject())
  732. {
  733. if (_referenceResolver == null || !_referenceResolver.TryGetCallable(_engine, callee, out func))
  734. {
  735. throw new JavaScriptException(_engine.TypeError,
  736. r == null ? "" : $"Property '{r.GetReferencedName()}' of object is not a function");
  737. }
  738. }
  739. var callable = func.TryCast<ICallable>();
  740. if (callable == null)
  741. {
  742. throw new JavaScriptException(_engine.TypeError);
  743. }
  744. if (r != null)
  745. {
  746. if (r.IsPropertyReference())
  747. {
  748. thisObject = r.GetBase();
  749. }
  750. else
  751. {
  752. var env = r.GetBase().TryCast<EnvironmentRecord>();
  753. thisObject = env.ImplicitThisValue();
  754. }
  755. }
  756. else
  757. {
  758. thisObject = Undefined.Instance;
  759. }
  760. // is it a direct call to eval ? http://www.ecma-international.org/ecma-262/5.1/#sec-15.1.2.1.1
  761. if (r != null && r.GetReferencedName() == "eval" && callable is EvalFunctionInstance)
  762. {
  763. var value = ((EvalFunctionInstance) callable).Call(thisObject, arguments, true);
  764. _engine.ReferencePool.Return(r);
  765. return value;
  766. }
  767. var result = callable.Call(thisObject, arguments);
  768. if (_isDebugMode)
  769. {
  770. _engine.DebugHandler.PopDebugCallStack();
  771. }
  772. if (_maxRecursionDepth >= 0)
  773. {
  774. _engine.CallStack.Pop();
  775. }
  776. if (!callExpression.Cached && arguments.Length > 0)
  777. {
  778. _engine.JsValueArrayPool.ReturnArray(arguments);
  779. }
  780. _engine.ReferencePool.Return(r);
  781. return result;
  782. }
  783. public JsValue EvaluateSequenceExpression(SequenceExpression sequenceExpression)
  784. {
  785. var result = Undefined.Instance;
  786. var expressionsCount = sequenceExpression.Expressions.Count;
  787. for (var i = 0; i < expressionsCount; i++)
  788. {
  789. var expression = sequenceExpression.Expressions[i];
  790. result = _engine.GetValue(_engine.EvaluateExpression(expression), true);
  791. }
  792. return result;
  793. }
  794. public JsValue EvaluateUpdateExpression(UpdateExpression updateExpression)
  795. {
  796. var value = _engine.EvaluateExpression(updateExpression.Argument);
  797. Reference r;
  798. switch (updateExpression.Operator)
  799. {
  800. case UnaryOperator.Increment:
  801. r = value as Reference;
  802. if (r != null
  803. && r.IsStrict()
  804. && r.GetBase() is EnvironmentRecord
  805. && ("eval" == r.GetReferencedName() || "arguments" == r.GetReferencedName()))
  806. {
  807. throw new JavaScriptException(_engine.SyntaxError);
  808. }
  809. var oldValue = TypeConverter.ToNumber(_engine.GetValue(value));
  810. var newValue = oldValue + 1;
  811. _engine.PutValue(r, newValue);
  812. _engine.ReferencePool.Return(r);
  813. return updateExpression.Prefix ? newValue : oldValue;
  814. case UnaryOperator.Decrement:
  815. r = value as Reference;
  816. if (r != null
  817. && r.IsStrict()
  818. && r.GetBase() is EnvironmentRecord
  819. && ("eval" == r.GetReferencedName() || "arguments" == r.GetReferencedName()))
  820. {
  821. throw new JavaScriptException(_engine.SyntaxError);
  822. }
  823. oldValue = TypeConverter.ToNumber(_engine.GetValue(value));
  824. newValue = oldValue - 1;
  825. _engine.PutValue(r, newValue);
  826. _engine.ReferencePool.Return(r);
  827. return updateExpression.Prefix ? newValue : oldValue;
  828. default:
  829. throw new ArgumentException();
  830. }
  831. }
  832. public JsValue EvaluateThisExpression(ThisExpression thisExpression)
  833. {
  834. return _engine.ExecutionContext.ThisBinding;
  835. }
  836. public JsValue EvaluateNewExpression(NewExpression newExpression)
  837. {
  838. var arguments = _engine.JsValueArrayPool.RentArray(newExpression.Arguments.Count);
  839. BuildArguments(newExpression.Arguments, arguments, out _);
  840. // todo: optimize by defining a common abstract class or interface
  841. var callee = _engine.GetValue(EvaluateExpression(newExpression.Callee), true).TryCast<IConstructor>();
  842. if (callee == null)
  843. {
  844. throw new JavaScriptException(_engine.TypeError, "The object can't be used as constructor.");
  845. }
  846. // construct the new instance using the Function's constructor method
  847. var instance = callee.Construct(arguments);
  848. _engine.JsValueArrayPool.ReturnArray(arguments);
  849. return instance;
  850. }
  851. public JsValue EvaluateArrayExpression(ArrayExpression arrayExpression)
  852. {
  853. var elements = arrayExpression.Elements;
  854. var count = elements.Count;
  855. var jsValues = _engine.JsValueArrayPool.RentArray(1);
  856. jsValues[0] = count;
  857. var a = _engine.Array.Construct(jsValues, (uint) count);
  858. for (var n = 0; n < count; n++)
  859. {
  860. var expr = elements[n];
  861. if (expr != null)
  862. {
  863. var value = _engine.GetValue(EvaluateExpression((Expression) expr), true);
  864. a.SetIndexValue((uint) n, value, updateLength: false);
  865. }
  866. }
  867. a.SetLength((uint) count);
  868. _engine.JsValueArrayPool.ReturnArray(jsValues);
  869. return a;
  870. }
  871. public JsValue EvaluateUnaryExpression(UnaryExpression unaryExpression)
  872. {
  873. var value = _engine.EvaluateExpression(unaryExpression.Argument);
  874. switch (unaryExpression.Operator)
  875. {
  876. case UnaryOperator.Plus:
  877. return TypeConverter.ToNumber(_engine.GetValue(value, true));
  878. case UnaryOperator.Minus:
  879. var n = TypeConverter.ToNumber(_engine.GetValue(value, true));
  880. return double.IsNaN(n) ? double.NaN : n*-1;
  881. case UnaryOperator.BitwiseNot:
  882. return ~TypeConverter.ToInt32(_engine.GetValue(value, true));
  883. case UnaryOperator.LogicalNot:
  884. return !TypeConverter.ToBoolean(_engine.GetValue(value, true));
  885. case UnaryOperator.Delete:
  886. var r = value as Reference;
  887. if (r == null)
  888. {
  889. return true;
  890. }
  891. if (r.IsUnresolvableReference())
  892. {
  893. if (r.IsStrict())
  894. {
  895. throw new JavaScriptException(_engine.SyntaxError);
  896. }
  897. _engine.ReferencePool.Return(r);
  898. return true;
  899. }
  900. if (r.IsPropertyReference())
  901. {
  902. var o = TypeConverter.ToObject(_engine, r.GetBase());
  903. var jsValue = o.Delete(r.GetReferencedName(), r.IsStrict());
  904. _engine.ReferencePool.Return(r);
  905. return jsValue;
  906. }
  907. if (r.IsStrict())
  908. {
  909. throw new JavaScriptException(_engine.SyntaxError);
  910. }
  911. var bindings = r.GetBase().TryCast<EnvironmentRecord>();
  912. var referencedName = r.GetReferencedName();
  913. _engine.ReferencePool.Return(r);
  914. return bindings.DeleteBinding(referencedName);
  915. case UnaryOperator.Void:
  916. _engine.GetValue(value);
  917. return Undefined.Instance;
  918. case UnaryOperator.TypeOf:
  919. r = value as Reference;
  920. if (r != null)
  921. {
  922. if (r.IsUnresolvableReference())
  923. {
  924. _engine.ReferencePool.Return(r);
  925. return "undefined";
  926. }
  927. }
  928. var v = _engine.GetValue(value, true);
  929. if (v.IsUndefined())
  930. {
  931. return "undefined";
  932. }
  933. if (v.IsNull())
  934. {
  935. return "object";
  936. }
  937. switch (v.Type)
  938. {
  939. case Types.Boolean: return "boolean";
  940. case Types.Number: return "number";
  941. case Types.String: return "string";
  942. }
  943. if (v.TryCast<ICallable>() != null)
  944. {
  945. return "function";
  946. }
  947. return "object";
  948. default:
  949. throw new ArgumentException();
  950. }
  951. }
  952. private void BuildArguments(
  953. List<ArgumentListElement> expressionArguments,
  954. JsValue[] targetArray,
  955. out bool cacheable)
  956. {
  957. cacheable = true;
  958. var count = expressionArguments.Count;
  959. for (var i = 0; i < count; i++)
  960. {
  961. var argument = (Expression) expressionArguments[i];
  962. targetArray[i] = _engine.GetValue(EvaluateExpression(argument), true);
  963. cacheable &= argument is Literal;
  964. }
  965. }
  966. }
  967. }