ExpressionIntepreter.cs 40 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165
  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. PropertyDescriptor 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 PropertyDescriptor(propValue, PropertyFlag.ConfigurableEnumerableWritable);
  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 GetSetPropertyDescriptor(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 GetSetPropertyDescriptor(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. var options = _engine.Options;
  676. var maxRecursionDepth = options._MaxRecursionDepth;
  677. var debug = options._IsDebugMode;
  678. if (debug)
  679. {
  680. _engine.DebugHandler.AddToDebugCallStack(callExpression);
  681. }
  682. JsValue thisObject;
  683. // todo: implement as in http://www.ecma-international.org/ecma-262/5.1/#sec-11.2.4
  684. var arguments = Array.Empty<JsValue>();
  685. if (callExpression.Cached)
  686. {
  687. arguments = (JsValue[]) callExpression.CachedArguments;
  688. }
  689. else
  690. {
  691. var allLiteral = true;
  692. if (callExpression.Arguments.Count > 0)
  693. {
  694. arguments = _engine.JsValueArrayPool.RentArray(callExpression.Arguments.Count);
  695. BuildArguments(callExpression.Arguments, arguments, out allLiteral);
  696. }
  697. if (callExpression.CanBeCached)
  698. {
  699. // The arguments array can be cached if they are all literals
  700. if (allLiteral)
  701. {
  702. callExpression.CachedArguments = arguments;
  703. callExpression.Cached = true;
  704. }
  705. else
  706. {
  707. callExpression.CanBeCached = false;
  708. }
  709. }
  710. }
  711. var func = _engine.GetValue(callee);
  712. var r = callee as Reference;
  713. if (maxRecursionDepth >= 0)
  714. {
  715. var stackItem = new CallStackElement(callExpression, func, r != null ? r.GetReferencedName() : "anonymous function");
  716. var recursionDepth = _engine.CallStack.Push(stackItem);
  717. if (recursionDepth > maxRecursionDepth)
  718. {
  719. _engine.CallStack.Pop();
  720. throw new RecursionDepthOverflowException(_engine.CallStack, stackItem.ToString());
  721. }
  722. }
  723. if (ReferenceEquals(func, Undefined.Instance))
  724. {
  725. throw new JavaScriptException(_engine.TypeError, r == null ? "" : string.Format("Object has no method '{0}'", r.GetReferencedName()));
  726. }
  727. if (!func.IsObject())
  728. {
  729. if (options._ReferenceResolver == null ||
  730. !options._ReferenceResolver.TryGetCallable(_engine, callee, out func))
  731. {
  732. throw new JavaScriptException(_engine.TypeError,
  733. r == null ? "" : string.Format("Property '{0}' of object is not a function", r.GetReferencedName()));
  734. }
  735. }
  736. var callable = func.TryCast<ICallable>();
  737. if (callable == null)
  738. {
  739. throw new JavaScriptException(_engine.TypeError);
  740. }
  741. if (r != null)
  742. {
  743. if (r.IsPropertyReference())
  744. {
  745. thisObject = r.GetBase();
  746. }
  747. else
  748. {
  749. var env = r.GetBase().TryCast<EnvironmentRecord>();
  750. thisObject = env.ImplicitThisValue();
  751. }
  752. }
  753. else
  754. {
  755. thisObject = Undefined.Instance;
  756. }
  757. // is it a direct call to eval ? http://www.ecma-international.org/ecma-262/5.1/#sec-15.1.2.1.1
  758. if (r != null && r.GetReferencedName() == "eval" && callable is EvalFunctionInstance)
  759. {
  760. var value = ((EvalFunctionInstance) callable).Call(thisObject, arguments, true);
  761. _engine.ReferencePool.Return(r);
  762. return value;
  763. }
  764. var result = callable.Call(thisObject, arguments);
  765. if (debug)
  766. {
  767. _engine.DebugHandler.PopDebugCallStack();
  768. }
  769. if (maxRecursionDepth >= 0)
  770. {
  771. _engine.CallStack.Pop();
  772. }
  773. if (!callExpression.Cached && arguments.Length > 0)
  774. {
  775. _engine.JsValueArrayPool.ReturnArray(arguments);
  776. }
  777. _engine.ReferencePool.Return(r);
  778. return result;
  779. }
  780. public JsValue EvaluateSequenceExpression(SequenceExpression sequenceExpression)
  781. {
  782. var result = Undefined.Instance;
  783. var expressionsCount = sequenceExpression.Expressions.Count;
  784. for (var i = 0; i < expressionsCount; i++)
  785. {
  786. var expression = sequenceExpression.Expressions[i];
  787. result = _engine.GetValue(_engine.EvaluateExpression(expression.As<Expression>()), true);
  788. }
  789. return result;
  790. }
  791. public JsValue EvaluateUpdateExpression(UpdateExpression updateExpression)
  792. {
  793. var value = _engine.EvaluateExpression(updateExpression.Argument);
  794. Reference r;
  795. switch (updateExpression.Operator)
  796. {
  797. case UnaryOperator.Increment:
  798. r = value as Reference;
  799. if (r != null
  800. && r.IsStrict()
  801. && (r.GetBase().TryCast<EnvironmentRecord>() != null)
  802. && ("eval" == r.GetReferencedName() || "arguments" == r.GetReferencedName()))
  803. {
  804. throw new JavaScriptException(_engine.SyntaxError);
  805. }
  806. var oldValue = TypeConverter.ToNumber(_engine.GetValue(value));
  807. var newValue = oldValue + 1;
  808. _engine.PutValue(r, newValue);
  809. _engine.ReferencePool.Return(r);
  810. return updateExpression.Prefix ? newValue : oldValue;
  811. case UnaryOperator.Decrement:
  812. r = value as Reference;
  813. if (r != null
  814. && r.IsStrict()
  815. && (r.GetBase().TryCast<EnvironmentRecord>() != null)
  816. && ("eval" == r.GetReferencedName() || "arguments" == r.GetReferencedName()))
  817. {
  818. throw new JavaScriptException(_engine.SyntaxError);
  819. }
  820. oldValue = TypeConverter.ToNumber(_engine.GetValue(value));
  821. newValue = oldValue - 1;
  822. _engine.PutValue(r, newValue);
  823. _engine.ReferencePool.Return(r);
  824. return updateExpression.Prefix ? newValue : oldValue;
  825. default:
  826. throw new ArgumentException();
  827. }
  828. }
  829. public JsValue EvaluateThisExpression(ThisExpression thisExpression)
  830. {
  831. return _engine.ExecutionContext.ThisBinding;
  832. }
  833. public JsValue EvaluateNewExpression(NewExpression newExpression)
  834. {
  835. var arguments = _engine.JsValueArrayPool.RentArray(newExpression.Arguments.Count);
  836. BuildArguments(newExpression.Arguments, arguments, out _);
  837. // todo: optimize by defining a common abstract class or interface
  838. var callee = _engine.GetValue(EvaluateExpression(newExpression.Callee), true).TryCast<IConstructor>();
  839. if (callee == null)
  840. {
  841. throw new JavaScriptException(_engine.TypeError, "The object can't be used as constructor.");
  842. }
  843. // construct the new instance using the Function's constructor method
  844. var instance = callee.Construct(arguments);
  845. _engine.JsValueArrayPool.ReturnArray(arguments);
  846. return instance;
  847. }
  848. public JsValue EvaluateArrayExpression(ArrayExpression arrayExpression)
  849. {
  850. var elements = arrayExpression.Elements;
  851. var count = elements.Count;
  852. var jsValues = _engine.JsValueArrayPool.RentArray(1);
  853. jsValues[0] = count;
  854. var a = _engine.Array.Construct(jsValues, (uint) count);
  855. for (var n = 0; n < count; n++)
  856. {
  857. var expr = elements[n];
  858. if (expr != null)
  859. {
  860. var value = _engine.GetValue(EvaluateExpression(expr.As<Expression>()), true);
  861. a.SetIndexValue((uint) n, value, throwOnError: false);
  862. }
  863. }
  864. _engine.JsValueArrayPool.ReturnArray(jsValues);
  865. return a;
  866. }
  867. public JsValue EvaluateUnaryExpression(UnaryExpression unaryExpression)
  868. {
  869. var value = _engine.EvaluateExpression(unaryExpression.Argument);
  870. switch (unaryExpression.Operator)
  871. {
  872. case UnaryOperator.Plus:
  873. return TypeConverter.ToNumber(_engine.GetValue(value, true));
  874. case UnaryOperator.Minus:
  875. var n = TypeConverter.ToNumber(_engine.GetValue(value, true));
  876. return double.IsNaN(n) ? double.NaN : n*-1;
  877. case UnaryOperator.BitwiseNot:
  878. return ~TypeConverter.ToInt32(_engine.GetValue(value, true));
  879. case UnaryOperator.LogicalNot:
  880. return !TypeConverter.ToBoolean(_engine.GetValue(value, true));
  881. case UnaryOperator.Delete:
  882. var r = value as Reference;
  883. if (r == null)
  884. {
  885. return true;
  886. }
  887. if (r.IsUnresolvableReference())
  888. {
  889. if (r.IsStrict())
  890. {
  891. throw new JavaScriptException(_engine.SyntaxError);
  892. }
  893. _engine.ReferencePool.Return(r);
  894. return true;
  895. }
  896. if (r.IsPropertyReference())
  897. {
  898. var o = TypeConverter.ToObject(_engine, r.GetBase());
  899. var jsValue = o.Delete(r.GetReferencedName(), r.IsStrict());
  900. _engine.ReferencePool.Return(r);
  901. return jsValue;
  902. }
  903. if (r.IsStrict())
  904. {
  905. throw new JavaScriptException(_engine.SyntaxError);
  906. }
  907. var bindings = r.GetBase().TryCast<EnvironmentRecord>();
  908. var referencedName = r.GetReferencedName();
  909. _engine.ReferencePool.Return(r);
  910. return bindings.DeleteBinding(referencedName);
  911. case UnaryOperator.Void:
  912. _engine.GetValue(value);
  913. return Undefined.Instance;
  914. case UnaryOperator.TypeOf:
  915. r = value as Reference;
  916. if (r != null)
  917. {
  918. if (r.IsUnresolvableReference())
  919. {
  920. _engine.ReferencePool.Return(r);
  921. return "undefined";
  922. }
  923. }
  924. var v = _engine.GetValue(value, true);
  925. if (ReferenceEquals(v, Undefined.Instance))
  926. {
  927. return "undefined";
  928. }
  929. if (ReferenceEquals(v, Null.Instance))
  930. {
  931. return "object";
  932. }
  933. switch (v.Type)
  934. {
  935. case Types.Boolean: return "boolean";
  936. case Types.Number: return "number";
  937. case Types.String: return "string";
  938. }
  939. if (v.TryCast<ICallable>() != null)
  940. {
  941. return "function";
  942. }
  943. return "object";
  944. default:
  945. throw new ArgumentException();
  946. }
  947. }
  948. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  949. private void BuildArguments(
  950. List<ArgumentListElement> expressionArguments,
  951. JsValue[] targetArray,
  952. out bool cacheable)
  953. {
  954. cacheable = true;
  955. var count = expressionArguments.Count;
  956. for (var i = 0; i < count; i++)
  957. {
  958. var argument = (Expression) expressionArguments[i];
  959. targetArray[i] = _engine.GetValue(EvaluateExpression(argument), true);
  960. cacheable &= argument is Literal;
  961. }
  962. }
  963. }
  964. }