ExpressionIntepreter.cs 39 KB

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