ExpressionIntepreter.cs 38 KB

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