StatementInterpreter.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Runtime.CompilerServices;
  4. using Esprima.Ast;
  5. using Jint.Native;
  6. using Jint.Runtime.Descriptors;
  7. using Jint.Runtime.Environments;
  8. using Jint.Runtime.References;
  9. namespace Jint.Runtime
  10. {
  11. public class StatementInterpreter
  12. {
  13. private readonly Engine _engine;
  14. public StatementInterpreter(Engine engine)
  15. {
  16. _engine = engine;
  17. }
  18. private Completion ExecuteStatement(Statement statement)
  19. {
  20. return _engine.ExecuteStatement(statement);
  21. }
  22. public Completion ExecuteEmptyStatement(EmptyStatement emptyStatement)
  23. {
  24. return new Completion(CompletionType.Normal, null, null);
  25. }
  26. public Completion ExecuteExpressionStatement(ExpressionStatement expressionStatement)
  27. {
  28. var exprRef = _engine.EvaluateExpression(expressionStatement.Expression);
  29. return new Completion(CompletionType.Normal, _engine.GetValue(exprRef, true), null);
  30. }
  31. public Completion ExecuteIfStatement(IfStatement ifStatement)
  32. {
  33. Completion result;
  34. if (TypeConverter.ToBoolean(_engine.GetValue(_engine.EvaluateExpression(ifStatement.Test), true)))
  35. {
  36. result = ExecuteStatement(ifStatement.Consequent);
  37. }
  38. else if (ifStatement.Alternate != null)
  39. {
  40. result = ExecuteStatement(ifStatement.Alternate);
  41. }
  42. else
  43. {
  44. return new Completion(CompletionType.Normal, null, null);
  45. }
  46. return result;
  47. }
  48. public Completion ExecuteLabeledStatement(LabeledStatement labeledStatement)
  49. {
  50. // TODO: Esprima added Statement.Label, maybe not necessary as this line is finding the
  51. // containing label and could keep a table per program with all the labels
  52. // labeledStatement.Body.LabelSet = labeledStatement.Label;
  53. var result = ExecuteStatement(labeledStatement.Body);
  54. if (result.Type == CompletionType.Break && result.Identifier == labeledStatement.Label.Name)
  55. {
  56. var value = result.Value;
  57. return new Completion(CompletionType.Normal, value, null);
  58. }
  59. return result;
  60. }
  61. /// <summary>
  62. /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.6.1
  63. /// </summary>
  64. /// <param name="doWhileStatement"></param>
  65. /// <returns></returns>
  66. public Completion ExecuteDoWhileStatement(DoWhileStatement doWhileStatement)
  67. {
  68. JsValue v = Undefined.Instance;
  69. bool iterating;
  70. do
  71. {
  72. var stmt = ExecuteStatement(doWhileStatement.Body);
  73. if (!ReferenceEquals(stmt.Value, null))
  74. {
  75. v = stmt.Value;
  76. }
  77. if (stmt.Type != CompletionType.Continue || stmt.Identifier != doWhileStatement?.LabelSet?.Name)
  78. {
  79. if (stmt.Type == CompletionType.Break && (stmt.Identifier == null || stmt.Identifier == doWhileStatement?.LabelSet?.Name))
  80. {
  81. return new Completion(CompletionType.Normal, v, null);
  82. }
  83. if (stmt.Type != CompletionType.Normal)
  84. {
  85. return stmt;
  86. }
  87. }
  88. var exprRef = _engine.EvaluateExpression(doWhileStatement.Test);
  89. iterating = TypeConverter.ToBoolean(_engine.GetValue(exprRef, true));
  90. } while (iterating);
  91. return new Completion(CompletionType.Normal, v, null);
  92. }
  93. /// <summary>
  94. /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.6.2
  95. /// </summary>
  96. /// <param name="whileStatement"></param>
  97. /// <returns></returns>
  98. public Completion ExecuteWhileStatement(WhileStatement whileStatement)
  99. {
  100. JsValue v = Undefined.Instance;
  101. while (true)
  102. {
  103. var jsValue = _engine.GetValue(_engine.EvaluateExpression(whileStatement.Test), true);
  104. if (!TypeConverter.ToBoolean(jsValue))
  105. {
  106. return new Completion(CompletionType.Normal, v, null);
  107. }
  108. var stmt = ExecuteStatement(whileStatement.Body);
  109. if (!ReferenceEquals(stmt.Value, null))
  110. {
  111. v = stmt.Value;
  112. }
  113. if (stmt.Type != CompletionType.Continue || stmt.Identifier != whileStatement?.LabelSet?.Name)
  114. {
  115. if (stmt.Type == CompletionType.Break && (stmt.Identifier == null || stmt.Identifier == whileStatement?.LabelSet?.Name))
  116. {
  117. return new Completion(CompletionType.Normal, v, null);
  118. }
  119. if (stmt.Type != CompletionType.Normal)
  120. {
  121. return stmt;
  122. }
  123. }
  124. }
  125. }
  126. /// <summary>
  127. /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.6.3
  128. /// </summary>
  129. /// <param name="forStatement"></param>
  130. /// <returns></returns>
  131. public Completion ExecuteForStatement(ForStatement forStatement)
  132. {
  133. var init = forStatement.Init;
  134. if (init != null)
  135. {
  136. if (init.Type == Nodes.VariableDeclaration)
  137. {
  138. var c = ExecuteStatement((Statement) init);
  139. }
  140. else
  141. {
  142. _engine.GetValue(_engine.EvaluateExpression(init), true);
  143. }
  144. }
  145. JsValue v = Undefined.Instance;
  146. while (true)
  147. {
  148. if (forStatement.Test != null)
  149. {
  150. var testExprRef = _engine.EvaluateExpression(forStatement.Test);
  151. if (!TypeConverter.ToBoolean(_engine.GetValue(testExprRef, true)))
  152. {
  153. return new Completion(CompletionType.Normal, v, null);
  154. }
  155. }
  156. var stmt = ExecuteStatement(forStatement.Body);
  157. if (!ReferenceEquals(stmt.Value, null))
  158. {
  159. v = stmt.Value;
  160. }
  161. var stmtType = stmt.Type;
  162. if (stmtType == CompletionType.Break && (stmt.Identifier == null || stmt.Identifier == forStatement?.LabelSet?.Name))
  163. {
  164. return new Completion(CompletionType.Normal, v, null);
  165. }
  166. if (stmtType != CompletionType.Continue || ((stmt.Identifier != null) && stmt.Identifier != forStatement?.LabelSet?.Name))
  167. {
  168. if (stmtType != CompletionType.Normal)
  169. {
  170. return stmt;
  171. }
  172. }
  173. if (forStatement.Update != null)
  174. {
  175. _engine.GetValue(_engine.EvaluateExpression(forStatement.Update), true);
  176. }
  177. }
  178. }
  179. /// <summary>
  180. /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.6.4
  181. /// </summary>
  182. /// <param name="forInStatement"></param>
  183. /// <returns></returns>
  184. public Completion ExecuteForInStatement(ForInStatement forInStatement)
  185. {
  186. var identifier = forInStatement.Left.Type == Nodes.VariableDeclaration
  187. ? (Identifier) ((VariableDeclaration) forInStatement.Left).Declarations[0].Id
  188. : (Identifier) forInStatement.Left;
  189. var varRef = _engine.EvaluateExpression(identifier) as Reference;
  190. var experValue = _engine.GetValue(_engine.EvaluateExpression(forInStatement.Right), true);
  191. if (experValue.IsUndefined() || experValue.IsNull())
  192. {
  193. return new Completion(CompletionType.Normal, null, null);
  194. }
  195. var obj = TypeConverter.ToObject(_engine, experValue);
  196. JsValue v = Null.Instance;
  197. // keys are constructed using the prototype chain
  198. var cursor = obj;
  199. var processedKeys = new HashSet<string>();
  200. while (!ReferenceEquals(cursor, null))
  201. {
  202. var keys = _engine.Object.GetOwnPropertyNames(Undefined.Instance, Arguments.From(cursor)).AsArray();
  203. for (var i = 0; i < keys.GetLength(); i++)
  204. {
  205. var p = keys.GetOwnProperty(TypeConverter.ToString(i)).Value.AsStringWithoutTypeCheck();
  206. if (processedKeys.Contains(p))
  207. {
  208. continue;
  209. }
  210. processedKeys.Add(p);
  211. // collection might be modified by inner statement
  212. if (cursor.GetOwnProperty(p) == PropertyDescriptor.Undefined)
  213. {
  214. continue;
  215. }
  216. var value = cursor.GetOwnProperty(p);
  217. if (!value.Enumerable)
  218. {
  219. continue;
  220. }
  221. _engine.PutValue(varRef, p);
  222. var stmt = ExecuteStatement(forInStatement.Body);
  223. if (!ReferenceEquals(stmt.Value, null))
  224. {
  225. v = stmt.Value;
  226. }
  227. if (stmt.Type == CompletionType.Break)
  228. {
  229. return new Completion(CompletionType.Normal, v, null);
  230. }
  231. if (stmt.Type != CompletionType.Continue)
  232. {
  233. if (stmt.Type != CompletionType.Normal)
  234. {
  235. return stmt;
  236. }
  237. }
  238. }
  239. cursor = cursor.Prototype;
  240. }
  241. return new Completion(CompletionType.Normal, v, null);
  242. }
  243. /// <summary>
  244. /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.7
  245. /// </summary>
  246. /// <param name="continueStatement"></param>
  247. /// <returns></returns>
  248. public Completion ExecuteContinueStatement(ContinueStatement continueStatement)
  249. {
  250. return new Completion(
  251. CompletionType.Continue,
  252. null,
  253. continueStatement.Label?.Name);
  254. }
  255. /// <summary>
  256. /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.8
  257. /// </summary>
  258. /// <param name="breakStatement"></param>
  259. /// <returns></returns>
  260. public Completion ExecuteBreakStatement(BreakStatement breakStatement)
  261. {
  262. return new Completion(
  263. CompletionType.Break,
  264. null,
  265. breakStatement.Label?.Name);
  266. }
  267. /// <summary>
  268. /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.9
  269. /// </summary>
  270. /// <param name="statement"></param>
  271. /// <returns></returns>
  272. public Completion ExecuteReturnStatement(ReturnStatement statement)
  273. {
  274. if (statement.Argument == null)
  275. {
  276. return new Completion(CompletionType.Return, Undefined.Instance, null);
  277. }
  278. var jsValue = _engine.GetValue(_engine.EvaluateExpression(statement.Argument), true);
  279. return new Completion(CompletionType.Return, jsValue, null);
  280. }
  281. /// <summary>
  282. /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.10
  283. /// </summary>
  284. /// <param name="withStatement"></param>
  285. /// <returns></returns>
  286. public Completion ExecuteWithStatement(WithStatement withStatement)
  287. {
  288. var jsValue = _engine.GetValue(_engine.EvaluateExpression(withStatement.Object), true);
  289. var obj = TypeConverter.ToObject(_engine, jsValue);
  290. var oldEnv = _engine.ExecutionContext.LexicalEnvironment;
  291. var newEnv = LexicalEnvironment.NewObjectEnvironment(_engine, obj, oldEnv, true);
  292. _engine.UpdateLexicalEnvironment(newEnv);
  293. Completion c;
  294. try
  295. {
  296. c = ExecuteStatement(withStatement.Body);
  297. }
  298. catch (JavaScriptException e)
  299. {
  300. c = new Completion(CompletionType.Throw, e.Error, null, withStatement.Location);
  301. }
  302. finally
  303. {
  304. _engine.UpdateLexicalEnvironment(oldEnv);
  305. }
  306. return c;
  307. }
  308. /// <summary>
  309. /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.11
  310. /// </summary>
  311. /// <param name="switchStatement"></param>
  312. /// <returns></returns>
  313. public Completion ExecuteSwitchStatement(SwitchStatement switchStatement)
  314. {
  315. var jsValue = _engine.GetValue(_engine.EvaluateExpression(switchStatement.Discriminant), true);
  316. var r = ExecuteSwitchBlock(switchStatement.Cases, jsValue);
  317. if (r.Type == CompletionType.Break && r.Identifier == switchStatement.LabelSet?.Name)
  318. {
  319. return new Completion(CompletionType.Normal, r.Value, null);
  320. }
  321. return r;
  322. }
  323. public Completion ExecuteSwitchBlock(List<SwitchCase> switchBlock, JsValue input)
  324. {
  325. JsValue v = Undefined.Instance;
  326. SwitchCase defaultCase = null;
  327. bool hit = false;
  328. var switchBlockCount = switchBlock.Count;
  329. for (var i = 0; i < switchBlockCount; i++)
  330. {
  331. var clause = switchBlock[i];
  332. if (clause.Test == null)
  333. {
  334. defaultCase = clause;
  335. }
  336. else
  337. {
  338. var clauseSelector = _engine.GetValue(_engine.EvaluateExpression(clause.Test), true);
  339. if (ExpressionInterpreter.StrictlyEqual(clauseSelector, input))
  340. {
  341. hit = true;
  342. }
  343. }
  344. if (hit && clause.Consequent != null)
  345. {
  346. var r = ExecuteStatementList(clause.Consequent);
  347. if (r.Type != CompletionType.Normal)
  348. {
  349. return r;
  350. }
  351. v = r.Value ?? Undefined.Instance;
  352. }
  353. }
  354. // do we need to execute the default case ?
  355. if (hit == false && defaultCase != null)
  356. {
  357. var r = ExecuteStatementList(defaultCase.Consequent);
  358. if (r.Type != CompletionType.Normal)
  359. {
  360. return r;
  361. }
  362. v = r.Value ?? Undefined.Instance;
  363. }
  364. return new Completion(CompletionType.Normal, v, null);
  365. }
  366. public Completion ExecuteStatementList(List<StatementListItem> statementList)
  367. {
  368. var c = new Completion(CompletionType.Normal, null, null);
  369. Completion sl = c;
  370. Statement s = null;
  371. try
  372. {
  373. var statementListCount = statementList.Count;
  374. for (var i = 0; i < statementListCount; i++)
  375. {
  376. s = (Statement) statementList[i];
  377. c = ExecuteStatement(s);
  378. if (c.Type != CompletionType.Normal)
  379. {
  380. var executeStatementList = new Completion(
  381. c.Type,
  382. c.Value ?? sl.Value,
  383. c.Identifier,
  384. c.Location);
  385. return executeStatementList;
  386. }
  387. sl = c;
  388. }
  389. }
  390. catch (JavaScriptException v)
  391. {
  392. var completion = new Completion(CompletionType.Throw, v.Error, null, v.Location ?? s.Location);
  393. return completion;
  394. }
  395. return new Completion(c.Type, c.GetValueOrDefault(), c.Identifier);
  396. }
  397. /// <summary>
  398. /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.13
  399. /// </summary>
  400. /// <param name="throwStatement"></param>
  401. /// <returns></returns>
  402. public Completion ExecuteThrowStatement(ThrowStatement throwStatement)
  403. {
  404. var jsValue = _engine.GetValue(_engine.EvaluateExpression(throwStatement.Argument), true);
  405. return new Completion(CompletionType.Throw, jsValue, null, throwStatement.Location);
  406. }
  407. /// <summary>
  408. /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.14
  409. /// </summary>
  410. /// <param name="tryStatement"></param>
  411. /// <returns></returns>
  412. public Completion ExecuteTryStatement(TryStatement tryStatement)
  413. {
  414. var b = ExecuteStatement(tryStatement.Block);
  415. if (b.Type == CompletionType.Throw)
  416. {
  417. // execute catch
  418. var catchClause = tryStatement.Handler;
  419. if (catchClause != null)
  420. {
  421. var c = _engine.GetValue(b);
  422. var oldEnv = _engine.ExecutionContext.LexicalEnvironment;
  423. var catchEnv = LexicalEnvironment.NewDeclarativeEnvironment(_engine, oldEnv);
  424. catchEnv.Record.CreateMutableBinding(((Identifier) catchClause.Param).Name, c);
  425. _engine.UpdateLexicalEnvironment(catchEnv);
  426. b = ExecuteStatement(catchClause.Body);
  427. _engine.UpdateLexicalEnvironment(oldEnv);
  428. }
  429. }
  430. if (tryStatement.Finalizer != null)
  431. {
  432. var f = ExecuteStatement(tryStatement.Finalizer);
  433. if (f.Type == CompletionType.Normal)
  434. {
  435. return b;
  436. }
  437. return f;
  438. }
  439. return b;
  440. }
  441. public Completion ExecuteProgram(Program program)
  442. {
  443. return ExecuteStatementList(program.Body);
  444. }
  445. public Completion ExecuteVariableDeclaration(VariableDeclaration statement)
  446. {
  447. var declarationsCount = statement.Declarations.Count;
  448. for (var i = 0; i < declarationsCount; i++)
  449. {
  450. var declaration = statement.Declarations[i];
  451. if (declaration.Init != null)
  452. {
  453. if (!(_engine.EvaluateExpression(declaration.Id) is Reference lhs))
  454. {
  455. throw new ArgumentException();
  456. }
  457. if (lhs.IsStrict()
  458. && lhs.GetBase() is EnvironmentRecord
  459. && (lhs.GetReferencedName() == "eval" || lhs.GetReferencedName() == "arguments"))
  460. {
  461. throw new JavaScriptException(_engine.SyntaxError);
  462. }
  463. var value = _engine.GetValue(_engine.EvaluateExpression(declaration.Init), true);
  464. _engine.PutValue(lhs, value);
  465. _engine.ReferencePool.Return(lhs);
  466. }
  467. }
  468. return new Completion(CompletionType.Normal, Undefined.Instance, null);
  469. }
  470. public Completion ExecuteBlockStatement(BlockStatement blockStatement)
  471. {
  472. return ExecuteStatementList(blockStatement.Body);
  473. }
  474. public Completion ExecuteDebuggerStatement(DebuggerStatement debuggerStatement)
  475. {
  476. if (_engine.Options._IsDebuggerStatementAllowed)
  477. {
  478. if (!System.Diagnostics.Debugger.IsAttached)
  479. {
  480. System.Diagnostics.Debugger.Launch();
  481. }
  482. System.Diagnostics.Debugger.Break();
  483. }
  484. return new Completion(CompletionType.Normal, null, null);
  485. }
  486. }
  487. }