ExpressionIntepreter.cs 39 KB

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