ExpressionIntepreter.cs 40 KB

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