ExpressionIntepreter.cs 40 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174
  1. using System;
  2. using System.Collections.Generic;
  3. using Esprima;
  4. using Esprima.Ast;
  5. using Jint.Native;
  6. using Jint.Native.Function;
  7. using Jint.Native.Number;
  8. using Jint.Runtime.Descriptors;
  9. using Jint.Runtime.Descriptors.Specialized;
  10. using Jint.Runtime.Environments;
  11. using Jint.Runtime.Interop;
  12. using Jint.Runtime.References;
  13. namespace Jint.Runtime
  14. {
  15. public class ExpressionInterpreter
  16. {
  17. private readonly Engine _engine;
  18. private readonly bool _isDebugMode;
  19. private readonly int _maxRecursionDepth;
  20. private readonly IReferenceResolver _referenceResolver;
  21. public ExpressionInterpreter(Engine engine)
  22. {
  23. _engine = engine;
  24. // gather some options as fields for faster checks
  25. _isDebugMode = engine.Options.IsDebugMode;
  26. _maxRecursionDepth = engine.Options.MaxRecursionDepth;
  27. _referenceResolver = engine.Options.ReferenceResolver;
  28. }
  29. private object EvaluateExpression(Expression expression)
  30. {
  31. return _engine.EvaluateExpression(expression);
  32. }
  33. public JsValue EvaluateConditionalExpression(ConditionalExpression conditionalExpression)
  34. {
  35. var lref = _engine.EvaluateExpression(conditionalExpression.Test);
  36. if (TypeConverter.ToBoolean(_engine.GetValue(lref, true)))
  37. {
  38. var trueRef = _engine.EvaluateExpression(conditionalExpression.Consequent);
  39. return _engine.GetValue(trueRef, true);
  40. }
  41. else
  42. {
  43. var falseRef = _engine.EvaluateExpression(conditionalExpression.Alternate);
  44. return _engine.GetValue(falseRef, true);
  45. }
  46. }
  47. public JsValue EvaluateAssignmentExpression(AssignmentExpression assignmentExpression)
  48. {
  49. var lref = EvaluateExpression((Expression) assignmentExpression.Left) as Reference;
  50. JsValue rval = _engine.GetValue(EvaluateExpression(assignmentExpression.Right), true);
  51. if (lref == null)
  52. {
  53. throw new JavaScriptException(_engine.ReferenceError);
  54. }
  55. if (assignmentExpression.Operator == AssignmentOperator.Assign) // "="
  56. {
  57. if(lref.IsStrict()
  58. && lref.GetBase() is EnvironmentRecord
  59. && (lref.GetReferencedName() == "eval" || lref.GetReferencedName() == "arguments"))
  60. {
  61. throw new JavaScriptException(_engine.SyntaxError);
  62. }
  63. _engine.PutValue(lref, rval);
  64. _engine.ReferencePool.Return(lref);
  65. return rval;
  66. }
  67. JsValue lval = _engine.GetValue(lref);
  68. switch (assignmentExpression.Operator)
  69. {
  70. case AssignmentOperator.PlusAssign:
  71. var lprim = TypeConverter.ToPrimitive(lval);
  72. var rprim = TypeConverter.ToPrimitive(rval);
  73. if (lprim.IsString() || rprim.IsString())
  74. {
  75. if (!(lprim is JsString jsString))
  76. {
  77. jsString = new JsString.ConcatenatedString(TypeConverter.ToString(lprim));
  78. }
  79. lval = jsString.Append(rprim);
  80. }
  81. else
  82. {
  83. lval = TypeConverter.ToNumber(lprim) + TypeConverter.ToNumber(rprim);
  84. }
  85. break;
  86. case AssignmentOperator.MinusAssign:
  87. lval = TypeConverter.ToNumber(lval) - TypeConverter.ToNumber(rval);
  88. break;
  89. case AssignmentOperator.TimesAssign:
  90. if (lval.IsUndefined() || rval.IsUndefined())
  91. {
  92. lval = Undefined.Instance;
  93. }
  94. else
  95. {
  96. lval = TypeConverter.ToNumber(lval) * TypeConverter.ToNumber(rval);
  97. }
  98. break;
  99. case AssignmentOperator.DivideAssign:
  100. lval = Divide(lval, rval);
  101. break;
  102. case AssignmentOperator.ModuloAssign:
  103. if (lval.IsUndefined() || rval.IsUndefined())
  104. {
  105. lval = Undefined.Instance;
  106. }
  107. else
  108. {
  109. lval = TypeConverter.ToNumber(lval) % TypeConverter.ToNumber(rval);
  110. }
  111. break;
  112. case AssignmentOperator.BitwiseAndAssign:
  113. lval = TypeConverter.ToInt32(lval) & TypeConverter.ToInt32(rval);
  114. break;
  115. case AssignmentOperator.BitwiseOrAssign:
  116. lval = TypeConverter.ToInt32(lval) | TypeConverter.ToInt32(rval);
  117. break;
  118. case AssignmentOperator.BitwiseXOrAssign:
  119. lval = TypeConverter.ToInt32(lval) ^ TypeConverter.ToInt32(rval);
  120. break;
  121. case AssignmentOperator.LeftShiftAssign:
  122. lval = TypeConverter.ToInt32(lval) << (int)(TypeConverter.ToUint32(rval) & 0x1F);
  123. break;
  124. case AssignmentOperator.RightShiftAssign:
  125. lval = TypeConverter.ToInt32(lval) >> (int)(TypeConverter.ToUint32(rval) & 0x1F);
  126. break;
  127. case AssignmentOperator.UnsignedRightShiftAssign:
  128. lval = (uint)TypeConverter.ToInt32(lval) >> (int)(TypeConverter.ToUint32(rval) & 0x1F);
  129. break;
  130. default:
  131. throw new NotImplementedException();
  132. }
  133. _engine.PutValue(lref, lval);
  134. _engine.ReferencePool.Return(lref);
  135. return lval;
  136. }
  137. private JsValue Divide(JsValue lval, JsValue rval)
  138. {
  139. if (lval.IsUndefined() || rval.IsUndefined())
  140. {
  141. return Undefined.Instance;
  142. }
  143. else
  144. {
  145. var lN = TypeConverter.ToNumber(lval);
  146. var rN = TypeConverter.ToNumber(rval);
  147. if (double.IsNaN(rN) || double.IsNaN(lN))
  148. {
  149. return double.NaN;
  150. }
  151. if (double.IsInfinity(lN) && double.IsInfinity(rN))
  152. {
  153. return double.NaN;
  154. }
  155. if (double.IsInfinity(lN) && rN.Equals(0))
  156. {
  157. if (NumberInstance.IsNegativeZero(rN))
  158. {
  159. return -lN;
  160. }
  161. return lN;
  162. }
  163. if (lN.Equals(0) && rN.Equals(0))
  164. {
  165. return double.NaN;
  166. }
  167. if (rN.Equals(0))
  168. {
  169. if (NumberInstance.IsNegativeZero(rN))
  170. {
  171. return lN > 0 ? -double.PositiveInfinity : -double.NegativeInfinity;
  172. }
  173. return lN > 0 ? double.PositiveInfinity : double.NegativeInfinity;
  174. }
  175. return lN/rN;
  176. }
  177. }
  178. public JsValue EvaluateBinaryExpression(BinaryExpression expression)
  179. {
  180. JsValue left;
  181. if (expression.Left.Type == Nodes.Literal)
  182. {
  183. left = EvaluateLiteral((Literal) expression.Left);
  184. }
  185. else
  186. {
  187. left = _engine.GetValue(EvaluateExpression(expression.Left), true);
  188. }
  189. JsValue right;
  190. if (expression.Right.Type == Nodes.Literal)
  191. {
  192. right = EvaluateLiteral((Literal) expression.Right);
  193. }
  194. else
  195. {
  196. right = _engine.GetValue(EvaluateExpression(expression.Right), true);
  197. }
  198. JsValue value;
  199. switch (expression.Operator)
  200. {
  201. case BinaryOperator.Plus:
  202. var lprim = TypeConverter.ToPrimitive(left);
  203. var rprim = TypeConverter.ToPrimitive(right);
  204. if (lprim.IsString() || rprim.IsString())
  205. {
  206. value = TypeConverter.ToString(lprim) + TypeConverter.ToString(rprim);
  207. }
  208. else
  209. {
  210. value = TypeConverter.ToNumber(lprim) + TypeConverter.ToNumber(rprim);
  211. }
  212. break;
  213. case BinaryOperator.Minus:
  214. value = TypeConverter.ToNumber(left) - TypeConverter.ToNumber(right);
  215. break;
  216. case BinaryOperator.Times:
  217. if (left.IsUndefined() || right.IsUndefined())
  218. {
  219. value = Undefined.Instance;
  220. }
  221. else
  222. {
  223. value = TypeConverter.ToNumber(left) * TypeConverter.ToNumber(right);
  224. }
  225. break;
  226. case BinaryOperator.Divide:
  227. value = Divide(left, right);
  228. break;
  229. case BinaryOperator.Modulo:
  230. if (left.IsUndefined() || right.IsUndefined())
  231. {
  232. value = Undefined.Instance;
  233. }
  234. else
  235. {
  236. value = TypeConverter.ToNumber(left) % TypeConverter.ToNumber(right);
  237. }
  238. break;
  239. case BinaryOperator.Equal:
  240. value = Equal(left, right);
  241. break;
  242. case BinaryOperator.NotEqual:
  243. value = !Equal(left, right);
  244. break;
  245. case BinaryOperator.Greater:
  246. value = Compare(right, left, false);
  247. if (value.IsUndefined())
  248. {
  249. value = false;
  250. }
  251. break;
  252. case BinaryOperator.GreaterOrEqual:
  253. value = Compare(left, right);
  254. if (value.IsUndefined() || ((JsBoolean) value)._value)
  255. {
  256. value = false;
  257. }
  258. else
  259. {
  260. value = true;
  261. }
  262. break;
  263. case BinaryOperator.Less:
  264. value = Compare(left, right);
  265. if (value.IsUndefined())
  266. {
  267. value = false;
  268. }
  269. break;
  270. case BinaryOperator.LessOrEqual:
  271. value = Compare(right, left, false);
  272. if (value.IsUndefined() || ((JsBoolean) value)._value)
  273. {
  274. value = false;
  275. }
  276. else
  277. {
  278. value = true;
  279. }
  280. break;
  281. case BinaryOperator.StrictlyEqual:
  282. return StrictlyEqual(left, right);
  283. case BinaryOperator.StricltyNotEqual:
  284. return !StrictlyEqual(left, right);
  285. case BinaryOperator.BitwiseAnd:
  286. return TypeConverter.ToInt32(left) & TypeConverter.ToInt32(right);
  287. case BinaryOperator.BitwiseOr:
  288. return TypeConverter.ToInt32(left) | TypeConverter.ToInt32(right);
  289. case BinaryOperator.BitwiseXOr:
  290. return TypeConverter.ToInt32(left) ^ TypeConverter.ToInt32(right);
  291. case BinaryOperator.LeftShift:
  292. return TypeConverter.ToInt32(left) << (int)(TypeConverter.ToUint32(right) & 0x1F);
  293. case BinaryOperator.RightShift:
  294. return TypeConverter.ToInt32(left) >> (int)(TypeConverter.ToUint32(right) & 0x1F);
  295. case BinaryOperator.UnsignedRightShift:
  296. return (uint)TypeConverter.ToInt32(left) >> (int)(TypeConverter.ToUint32(right) & 0x1F);
  297. case BinaryOperator.InstanceOf:
  298. var f = right.TryCast<FunctionInstance>();
  299. if (ReferenceEquals(f, null))
  300. {
  301. throw new JavaScriptException(_engine.TypeError, "instanceof can only be used with a function object");
  302. }
  303. value = f.HasInstance(left);
  304. break;
  305. case BinaryOperator.In:
  306. if (!right.IsObject())
  307. {
  308. throw new JavaScriptException(_engine.TypeError, "in can only be used with an object");
  309. }
  310. value = right.AsObject().HasProperty(TypeConverter.ToString(left));
  311. break;
  312. default:
  313. throw new NotImplementedException();
  314. }
  315. return value;
  316. }
  317. public JsValue EvaluateLogicalExpression(BinaryExpression binaryExpression)
  318. {
  319. var left = _engine.GetValue(EvaluateExpression(binaryExpression.Left), true);
  320. switch (binaryExpression.Operator)
  321. {
  322. case BinaryOperator.LogicalAnd:
  323. if (!TypeConverter.ToBoolean(left))
  324. {
  325. return left;
  326. }
  327. return _engine.GetValue(EvaluateExpression(binaryExpression.Right), true);
  328. case BinaryOperator.LogicalOr:
  329. if (TypeConverter.ToBoolean(left))
  330. {
  331. return left;
  332. }
  333. return _engine.GetValue(EvaluateExpression(binaryExpression.Right), true);
  334. default:
  335. throw new NotImplementedException();
  336. }
  337. }
  338. private static bool Equal(JsValue x, JsValue y)
  339. {
  340. var typex = x.Type;
  341. var typey = y.Type;
  342. if (typex == typey)
  343. {
  344. return StrictlyEqual(x, y);
  345. }
  346. if (x.IsNull() && y.IsUndefined())
  347. {
  348. return true;
  349. }
  350. if (x.IsUndefined() && y.IsNull())
  351. {
  352. return true;
  353. }
  354. if (typex == Types.Number && typey == Types.String)
  355. {
  356. return Equal(x, TypeConverter.ToNumber(y));
  357. }
  358. if (typex == Types.String && typey == Types.Number)
  359. {
  360. return Equal(TypeConverter.ToNumber(x), y);
  361. }
  362. if (typex == Types.Boolean)
  363. {
  364. return Equal(TypeConverter.ToNumber(x), y);
  365. }
  366. if (typey == Types.Boolean)
  367. {
  368. return Equal(x, TypeConverter.ToNumber(y));
  369. }
  370. if (typey == Types.Object && (typex == Types.String || typex == Types.Number))
  371. {
  372. return Equal(x, TypeConverter.ToPrimitive(y));
  373. }
  374. if (typex == Types.Object && (typey == Types.String || typey == Types.Number))
  375. {
  376. return Equal(TypeConverter.ToPrimitive(x), y);
  377. }
  378. return false;
  379. }
  380. public static bool StrictlyEqual(JsValue x, JsValue y)
  381. {
  382. var typea = x.Type;
  383. var typeb = y.Type;
  384. if (typea != typeb)
  385. {
  386. return false;
  387. }
  388. if (typea == Types.Undefined || typea == Types.Null)
  389. {
  390. return true;
  391. }
  392. if (typea == Types.Number)
  393. {
  394. var nx = ((JsNumber) x)._value;
  395. var ny = ((JsNumber) y)._value;
  396. if (double.IsNaN(nx) || double.IsNaN(ny))
  397. {
  398. return false;
  399. }
  400. if (nx.Equals(ny))
  401. {
  402. return true;
  403. }
  404. return false;
  405. }
  406. if (typea == Types.String)
  407. {
  408. return x.AsStringWithoutTypeCheck() == y.AsStringWithoutTypeCheck();
  409. }
  410. if (typea == Types.Boolean)
  411. {
  412. return ((JsBoolean) x)._value == ((JsBoolean) y)._value;
  413. }
  414. if (typea == Types.Object)
  415. {
  416. if (x.AsObject() is IObjectWrapper xw)
  417. {
  418. var yw = y.AsObject() as IObjectWrapper;
  419. if (yw == null)
  420. return false;
  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. public JsValue EvaluateLiteral(Literal literal)
  528. {
  529. switch (literal.TokenType)
  530. {
  531. case TokenType.BooleanLiteral:
  532. // bool is fast enough
  533. return (JsValue) (literal.CachedValue = literal.CachedValue ?? (literal.NumericValue != 0.0 ? JsBoolean.True : JsBoolean.False));
  534. case TokenType.NullLiteral:
  535. // and so is null
  536. return JsValue.Null;
  537. case TokenType.NumericLiteral:
  538. return (JsValue) (literal.CachedValue = literal.CachedValue ?? JsNumber.Create(literal.NumericValue));
  539. case TokenType.StringLiteral:
  540. return (JsValue) (literal.CachedValue = literal.CachedValue ?? (literal.CachedValue = JsString.Create((string) literal.Value)));
  541. case TokenType.RegularExpression:
  542. // should not cache
  543. return _engine.RegExp.Construct((System.Text.RegularExpressions.Regex) literal.Value, literal.Regex.Flags);
  544. default:
  545. // a rare case, above types should cover all
  546. return JsValue.FromObject(_engine, literal.Value);
  547. }
  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 (func.IsUndefined())
  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 (v.IsUndefined())
  933. {
  934. return "undefined";
  935. }
  936. if (v.IsNull())
  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. private void BuildArguments(
  956. List<ArgumentListElement> expressionArguments,
  957. JsValue[] targetArray,
  958. out bool cacheable)
  959. {
  960. cacheable = true;
  961. var count = expressionArguments.Count;
  962. for (var i = 0; i < count; i++)
  963. {
  964. var argument = (Expression) expressionArguments[i];
  965. targetArray[i] = _engine.GetValue(EvaluateExpression(argument), true);
  966. cacheable &= argument is Literal;
  967. }
  968. }
  969. }
  970. }