ExpressionIntepreter.cs 40 KB

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