Engine.cs 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026
  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.Array;
  8. using Jint.Native.Boolean;
  9. using Jint.Native.Date;
  10. using Jint.Native.Error;
  11. using Jint.Native.Function;
  12. using Jint.Native.Global;
  13. using Jint.Native.Json;
  14. using Jint.Native.Math;
  15. using Jint.Native.Number;
  16. using Jint.Native.Object;
  17. using Jint.Native.RegExp;
  18. using Jint.Native.String;
  19. using Jint.Native.Symbol;
  20. using Jint.Pooling;
  21. using Jint.Runtime;
  22. using Jint.Runtime.CallStack;
  23. using Jint.Runtime.Debugger;
  24. using Jint.Runtime.Descriptors;
  25. using Jint.Runtime.Environments;
  26. using Jint.Runtime.Interop;
  27. using Jint.Runtime.References;
  28. namespace Jint
  29. {
  30. public class Engine
  31. {
  32. private static readonly ParserOptions DefaultParserOptions = new ParserOptions
  33. {
  34. AdaptRegexp = true,
  35. Tolerant = false,
  36. Loc = true
  37. };
  38. private readonly ExpressionInterpreter _expressions;
  39. private readonly StatementInterpreter _statements;
  40. private readonly ExecutionContextStack _executionContexts;
  41. private JsValue _completionValue = JsValue.Undefined;
  42. private int _statementsCount;
  43. private long _initialMemoryUsage;
  44. private long _timeoutTicks;
  45. private INode _lastSyntaxNode;
  46. // cached access
  47. private readonly bool _isDebugMode;
  48. private readonly bool _isStrict;
  49. private readonly int _maxStatements;
  50. private readonly IReferenceResolver _referenceResolver;
  51. public ITypeConverter ClrTypeConverter;
  52. // cache of types used when resolving CLR type names
  53. internal Dictionary<string, Type> TypeCache = new Dictionary<string, Type>();
  54. internal static Dictionary<Type, Func<Engine, object, JsValue>> TypeMappers = new Dictionary<Type, Func<Engine, object, JsValue>>
  55. {
  56. { typeof(bool), (Engine engine, object v) => (bool) v ? JsBoolean.True : JsBoolean.False },
  57. { typeof(byte), (Engine engine, object v) => JsNumber.Create((byte)v) },
  58. { typeof(char), (Engine engine, object v) => JsString.Create((char)v) },
  59. { typeof(DateTime), (Engine engine, object v) => engine.Date.Construct((DateTime)v) },
  60. { typeof(DateTimeOffset), (Engine engine, object v) => engine.Date.Construct((DateTimeOffset)v) },
  61. { typeof(decimal), (Engine engine, object v) => (JsValue) (double)(decimal)v },
  62. { typeof(double), (Engine engine, object v) => (JsValue)(double)v },
  63. { typeof(Int16), (Engine engine, object v) => JsNumber.Create((Int16)v) },
  64. { typeof(Int32), (Engine engine, object v) => JsNumber.Create((Int32)v) },
  65. { typeof(Int64), (Engine engine, object v) => (JsValue)(Int64)v },
  66. { typeof(SByte), (Engine engine, object v) => JsNumber.Create((SByte)v) },
  67. { typeof(Single), (Engine engine, object v) => (JsValue)(Single)v },
  68. { typeof(string), (Engine engine, object v) => (JsValue) (string)v },
  69. { typeof(UInt16), (Engine engine, object v) => JsNumber.Create((UInt16)v) },
  70. { typeof(UInt32), (Engine engine, object v) => JsNumber.Create((UInt32)v) },
  71. { typeof(UInt64), (Engine engine, object v) => JsNumber.Create((UInt64)v) },
  72. { typeof(System.Text.RegularExpressions.Regex), (Engine engine, object v) => engine.RegExp.Construct((System.Text.RegularExpressions.Regex)v, "") }
  73. };
  74. internal JintCallStack CallStack = new JintCallStack();
  75. static Engine()
  76. {
  77. var methodInfo = typeof(GC).GetMethod("GetAllocatedBytesForCurrentThread");
  78. if (methodInfo != null)
  79. {
  80. GetAllocatedBytesForCurrentThread = (Func<long>)Delegate.CreateDelegate(typeof(Func<long>), null, methodInfo);
  81. }
  82. }
  83. public Engine() : this(null)
  84. {
  85. }
  86. public Engine(Action<Options> options)
  87. {
  88. _executionContexts = new ExecutionContextStack();
  89. Global = GlobalObject.CreateGlobalObject(this);
  90. Object = ObjectConstructor.CreateObjectConstructor(this);
  91. Function = FunctionConstructor.CreateFunctionConstructor(this);
  92. Symbol = SymbolConstructor.CreateSymbolConstructor(this);
  93. Array = ArrayConstructor.CreateArrayConstructor(this);
  94. String = StringConstructor.CreateStringConstructor(this);
  95. RegExp = RegExpConstructor.CreateRegExpConstructor(this);
  96. Number = NumberConstructor.CreateNumberConstructor(this);
  97. Boolean = BooleanConstructor.CreateBooleanConstructor(this);
  98. Date = DateConstructor.CreateDateConstructor(this);
  99. Math = MathInstance.CreateMathObject(this);
  100. Json = JsonInstance.CreateJsonObject(this);
  101. Error = ErrorConstructor.CreateErrorConstructor(this, "Error");
  102. EvalError = ErrorConstructor.CreateErrorConstructor(this, "EvalError");
  103. RangeError = ErrorConstructor.CreateErrorConstructor(this, "RangeError");
  104. ReferenceError = ErrorConstructor.CreateErrorConstructor(this, "ReferenceError");
  105. SyntaxError = ErrorConstructor.CreateErrorConstructor(this, "SyntaxError");
  106. TypeError = ErrorConstructor.CreateErrorConstructor(this, "TypeError");
  107. UriError = ErrorConstructor.CreateErrorConstructor(this, "URIError");
  108. GlobalSymbolRegistry = new GlobalSymbolRegistry();
  109. // Because the properties might need some of the built-in object
  110. // their configuration is delayed to a later step
  111. Global.Configure();
  112. Object.Configure();
  113. Object.PrototypeObject.Configure();
  114. Symbol.Configure();
  115. Symbol.PrototypeObject.Configure();
  116. Function.Configure();
  117. Function.PrototypeObject.Configure();
  118. Array.Configure();
  119. Array.PrototypeObject.Configure();
  120. String.Configure();
  121. String.PrototypeObject.Configure();
  122. RegExp.Configure();
  123. RegExp.PrototypeObject.Configure();
  124. Number.Configure();
  125. Number.PrototypeObject.Configure();
  126. Boolean.Configure();
  127. Boolean.PrototypeObject.Configure();
  128. Date.Configure();
  129. Date.PrototypeObject.Configure();
  130. Math.Configure();
  131. Json.Configure();
  132. Error.Configure();
  133. Error.PrototypeObject.Configure();
  134. // create the global environment http://www.ecma-international.org/ecma-262/5.1/#sec-10.2.3
  135. GlobalEnvironment = LexicalEnvironment.NewObjectEnvironment(this, Global, null, false);
  136. // create the global execution context http://www.ecma-international.org/ecma-262/5.1/#sec-10.4.1.1
  137. EnterExecutionContext(GlobalEnvironment, GlobalEnvironment, Global);
  138. Options = new Options();
  139. options?.Invoke(Options);
  140. // gather some options as fields for faster checks
  141. _isDebugMode = Options.IsDebugMode;
  142. _isStrict = Options.IsStrict;
  143. _maxStatements = Options._MaxStatements;
  144. _referenceResolver = Options.ReferenceResolver;
  145. ReferencePool = new ReferencePool();
  146. ArgumentsInstancePool = new ArgumentsInstancePool(this);
  147. JsValueArrayPool = new JsValueArrayPool();
  148. Eval = new EvalFunctionInstance(this, System.Array.Empty<string>(), LexicalEnvironment.NewDeclarativeEnvironment(this, ExecutionContext.LexicalEnvironment), StrictModeScope.IsStrictModeCode);
  149. Global.FastAddProperty("eval", Eval, true, false, true);
  150. _statements = new StatementInterpreter(this);
  151. _expressions = new ExpressionInterpreter(this);
  152. if (Options._IsClrAllowed)
  153. {
  154. Global.FastAddProperty("System", new NamespaceReference(this, "System"), false, false, false);
  155. Global.FastAddProperty("importNamespace", new ClrFunctionInstance(this, (thisObj, arguments) =>
  156. {
  157. return new NamespaceReference(this, TypeConverter.ToString(arguments.At(0)));
  158. }), false, false, false);
  159. }
  160. ClrTypeConverter = new DefaultTypeConverter(this);
  161. BreakPoints = new List<BreakPoint>();
  162. DebugHandler = new DebugHandler(this);
  163. }
  164. public LexicalEnvironment GlobalEnvironment { get; }
  165. public GlobalObject Global { get; }
  166. public ObjectConstructor Object { get; }
  167. public FunctionConstructor Function { get; }
  168. public ArrayConstructor Array { get; }
  169. public StringConstructor String { get; }
  170. public RegExpConstructor RegExp { get; }
  171. public BooleanConstructor Boolean { get; }
  172. public NumberConstructor Number { get; }
  173. public DateConstructor Date { get; }
  174. public MathInstance Math { get; }
  175. public JsonInstance Json { get; }
  176. public SymbolConstructor Symbol { get; }
  177. public EvalFunctionInstance Eval { get; }
  178. public ErrorConstructor Error { get; }
  179. public ErrorConstructor EvalError { get; }
  180. public ErrorConstructor SyntaxError { get; }
  181. public ErrorConstructor TypeError { get; }
  182. public ErrorConstructor RangeError { get; }
  183. public ErrorConstructor ReferenceError { get; }
  184. public ErrorConstructor UriError { get; }
  185. public ref readonly ExecutionContext ExecutionContext
  186. {
  187. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  188. get { return ref _executionContexts.Peek(); }
  189. }
  190. public GlobalSymbolRegistry GlobalSymbolRegistry { get; }
  191. internal Options Options { [MethodImpl(MethodImplOptions.AggressiveInlining)] get; }
  192. internal ReferencePool ReferencePool
  193. {
  194. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  195. get;
  196. }
  197. internal ArgumentsInstancePool ArgumentsInstancePool { get; }
  198. internal JsValueArrayPool JsValueArrayPool { get; }
  199. #region Debugger
  200. public delegate StepMode DebugStepDelegate(object sender, DebugInformation e);
  201. public delegate StepMode BreakDelegate(object sender, DebugInformation e);
  202. public event DebugStepDelegate Step;
  203. public event BreakDelegate Break;
  204. internal DebugHandler DebugHandler { get; private set; }
  205. public List<BreakPoint> BreakPoints { get; private set; }
  206. internal StepMode? InvokeStepEvent(DebugInformation info)
  207. {
  208. return Step?.Invoke(this, info);
  209. }
  210. internal StepMode? InvokeBreakEvent(DebugInformation info)
  211. {
  212. return Break?.Invoke(this, info);
  213. }
  214. #endregion
  215. private static readonly Func<long> GetAllocatedBytesForCurrentThread;
  216. public void EnterExecutionContext(
  217. LexicalEnvironment lexicalEnvironment,
  218. LexicalEnvironment variableEnvironment,
  219. JsValue thisBinding)
  220. {
  221. var context = new ExecutionContext(
  222. lexicalEnvironment,
  223. variableEnvironment,
  224. thisBinding);
  225. _executionContexts.Push(context);
  226. }
  227. public Engine SetValue(string name, Delegate value)
  228. {
  229. Global.FastAddProperty(name, new DelegateWrapper(this, value), true, false, true);
  230. return this;
  231. }
  232. public Engine SetValue(string name, string value)
  233. {
  234. return SetValue(name, (JsValue) value);
  235. }
  236. public Engine SetValue(string name, double value)
  237. {
  238. return SetValue(name, JsNumber.Create(value));
  239. }
  240. public Engine SetValue(string name, int value)
  241. {
  242. return SetValue(name, JsNumber.Create(value));
  243. }
  244. public Engine SetValue(string name, bool value)
  245. {
  246. return SetValue(name, value ? JsBoolean.True : JsBoolean.False);
  247. }
  248. public Engine SetValue(string name, JsValue value)
  249. {
  250. Global.Put(name, value, false);
  251. return this;
  252. }
  253. public Engine SetValue(string name, object obj)
  254. {
  255. return SetValue(name, JsValue.FromObject(this, obj));
  256. }
  257. public void LeaveExecutionContext()
  258. {
  259. _executionContexts.Pop();
  260. }
  261. /// <summary>
  262. /// Initializes the statements count
  263. /// </summary>
  264. public void ResetStatementsCount()
  265. {
  266. _statementsCount = 0;
  267. }
  268. public void ResetMemoryUsage()
  269. {
  270. if (GetAllocatedBytesForCurrentThread != null)
  271. {
  272. _initialMemoryUsage = GetAllocatedBytesForCurrentThread();
  273. }
  274. }
  275. public void ResetTimeoutTicks()
  276. {
  277. var timeoutIntervalTicks = Options._TimeoutInterval.Ticks;
  278. _timeoutTicks = timeoutIntervalTicks > 0 ? DateTime.UtcNow.Ticks + timeoutIntervalTicks : 0;
  279. }
  280. /// <summary>
  281. /// Initializes list of references of called functions
  282. /// </summary>
  283. public void ResetCallStack()
  284. {
  285. CallStack.Clear();
  286. }
  287. public Engine Execute(string source)
  288. {
  289. return Execute(source, DefaultParserOptions);
  290. }
  291. public Engine Execute(string source, ParserOptions parserOptions)
  292. {
  293. var parser = new JavaScriptParser(source, parserOptions);
  294. return Execute(parser.ParseProgram());
  295. }
  296. public Engine Execute(Program program)
  297. {
  298. ResetStatementsCount();
  299. if (Options._MemoryLimit > 0)
  300. {
  301. ResetMemoryUsage();
  302. }
  303. ResetTimeoutTicks();
  304. ResetLastStatement();
  305. ResetCallStack();
  306. using (new StrictModeScope(_isStrict || program.Strict))
  307. {
  308. DeclarationBindingInstantiation(DeclarationBindingType.GlobalCode, program.HoistingScope.FunctionDeclarations, program.HoistingScope.VariableDeclarations, null, null);
  309. var result = _statements.ExecuteProgram(program);
  310. if (result.Type == CompletionType.Throw)
  311. {
  312. var ex = new JavaScriptException(result.GetValueOrDefault()).SetCallstack(this, result.Location);
  313. throw ex;
  314. }
  315. _completionValue = result.GetValueOrDefault();
  316. }
  317. return this;
  318. }
  319. private void ResetLastStatement()
  320. {
  321. _lastSyntaxNode = null;
  322. }
  323. /// <summary>
  324. /// Gets the last evaluated statement completion value
  325. /// </summary>
  326. public JsValue GetCompletionValue()
  327. {
  328. return _completionValue;
  329. }
  330. public Completion ExecuteStatement(Statement statement)
  331. {
  332. if (_maxStatements > 0 && _statementsCount++ > _maxStatements)
  333. {
  334. ThrowStatementsCountOverflowException();
  335. }
  336. if (_timeoutTicks > 0 && _timeoutTicks < DateTime.UtcNow.Ticks)
  337. {
  338. ThrowTimeoutException();
  339. }
  340. if (Options._MemoryLimit > 0)
  341. {
  342. if (GetAllocatedBytesForCurrentThread != null)
  343. {
  344. var memoryUsage = GetAllocatedBytesForCurrentThread() - _initialMemoryUsage;
  345. if (memoryUsage > Options._MemoryLimit)
  346. {
  347. throw new MemoryLimitExceededException($"Script has allocated {memoryUsage} but is limited to {Options._MemoryLimit}");
  348. }
  349. }
  350. else
  351. {
  352. throw new PlatformNotSupportedException("The current platform doesn't support MemoryLimit.");
  353. }
  354. }
  355. _lastSyntaxNode = statement;
  356. if (_isDebugMode)
  357. {
  358. DebugHandler.OnStep(statement);
  359. }
  360. switch (statement.Type)
  361. {
  362. case Nodes.BlockStatement:
  363. return _statements.ExecuteBlockStatement((BlockStatement) statement);
  364. case Nodes.ReturnStatement:
  365. return _statements.ExecuteReturnStatement((ReturnStatement) statement);
  366. case Nodes.VariableDeclaration:
  367. return _statements.ExecuteVariableDeclaration((VariableDeclaration) statement);
  368. case Nodes.BreakStatement:
  369. return _statements.ExecuteBreakStatement((BreakStatement) statement);
  370. case Nodes.ContinueStatement:
  371. return _statements.ExecuteContinueStatement((ContinueStatement) statement);
  372. case Nodes.DoWhileStatement:
  373. return _statements.ExecuteDoWhileStatement((DoWhileStatement) statement);
  374. case Nodes.EmptyStatement:
  375. return _statements.ExecuteEmptyStatement((EmptyStatement) statement);
  376. case Nodes.ExpressionStatement:
  377. return _statements.ExecuteExpressionStatement((ExpressionStatement) statement);
  378. case Nodes.ForStatement:
  379. return _statements.ExecuteForStatement((ForStatement) statement);
  380. case Nodes.ForInStatement:
  381. return _statements.ExecuteForInStatement((ForInStatement) statement);
  382. case Nodes.IfStatement:
  383. return _statements.ExecuteIfStatement((IfStatement) statement);
  384. case Nodes.LabeledStatement:
  385. return _statements.ExecuteLabeledStatement((LabeledStatement) statement);
  386. case Nodes.SwitchStatement:
  387. return _statements.ExecuteSwitchStatement((SwitchStatement) statement);
  388. case Nodes.FunctionDeclaration:
  389. return new Completion(CompletionType.Normal, null, null);
  390. case Nodes.ThrowStatement:
  391. return _statements.ExecuteThrowStatement((ThrowStatement) statement);
  392. case Nodes.TryStatement:
  393. return _statements.ExecuteTryStatement((TryStatement) statement);
  394. case Nodes.WhileStatement:
  395. return _statements.ExecuteWhileStatement((WhileStatement) statement);
  396. case Nodes.WithStatement:
  397. return _statements.ExecuteWithStatement((WithStatement) statement);
  398. case Nodes.DebuggerStatement:
  399. return _statements.ExecuteDebuggerStatement((DebuggerStatement) statement);
  400. case Nodes.Program:
  401. return _statements.ExecuteProgram((Program) statement);
  402. default:
  403. ThrowArgumentOutOfRange();
  404. return new Completion(CompletionType.Normal, null, null);
  405. }
  406. }
  407. public object EvaluateExpression(INode expression)
  408. {
  409. _lastSyntaxNode = expression;
  410. switch (expression.Type)
  411. {
  412. case Nodes.AssignmentExpression:
  413. return _expressions.EvaluateAssignmentExpression((AssignmentExpression) expression);
  414. case Nodes.ArrayExpression:
  415. return _expressions.EvaluateArrayExpression((ArrayExpression) expression);
  416. case Nodes.BinaryExpression:
  417. return _expressions.EvaluateBinaryExpression((BinaryExpression) expression);
  418. case Nodes.CallExpression:
  419. return _expressions.EvaluateCallExpression((CallExpression) expression);
  420. case Nodes.ConditionalExpression:
  421. return _expressions.EvaluateConditionalExpression((ConditionalExpression) expression);
  422. case Nodes.FunctionExpression:
  423. return _expressions.EvaluateFunctionExpression((IFunction) expression);
  424. case Nodes.Identifier:
  425. return _expressions.EvaluateIdentifier((Identifier) expression);
  426. case Nodes.Literal:
  427. return _expressions.EvaluateLiteral((Literal) expression);
  428. case Nodes.LogicalExpression:
  429. return _expressions.EvaluateLogicalExpression((BinaryExpression) expression);
  430. case Nodes.MemberExpression:
  431. return _expressions.EvaluateMemberExpression((MemberExpression) expression);
  432. case Nodes.NewExpression:
  433. return _expressions.EvaluateNewExpression((NewExpression) expression);
  434. case Nodes.ObjectExpression:
  435. return _expressions.EvaluateObjectExpression((ObjectExpression) expression);
  436. case Nodes.SequenceExpression:
  437. return _expressions.EvaluateSequenceExpression((SequenceExpression) expression);
  438. case Nodes.ThisExpression:
  439. return _expressions.EvaluateThisExpression((ThisExpression) expression);
  440. case Nodes.UpdateExpression:
  441. return _expressions.EvaluateUpdateExpression((UpdateExpression) expression);
  442. case Nodes.UnaryExpression:
  443. return _expressions.EvaluateUnaryExpression((UnaryExpression) expression);
  444. default:
  445. ThrowArgumentOutOfRange();
  446. return null;
  447. }
  448. }
  449. /// <summary>
  450. /// http://www.ecma-international.org/ecma-262/5.1/#sec-8.7.1
  451. /// </summary>
  452. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  453. public JsValue GetValue(object value)
  454. {
  455. if (value is JsValue jsValue)
  456. {
  457. return jsValue;
  458. }
  459. return GetValueFromReference(value, false);
  460. }
  461. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  462. internal JsValue GetValue(in Completion completion)
  463. {
  464. var value = completion.Value;
  465. if (value is JsValue jsValue)
  466. {
  467. return jsValue;
  468. }
  469. return GetValueFromReference(completion.Value, false);
  470. }
  471. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  472. internal JsValue GetValue(object value, bool returnReferenceToPool)
  473. {
  474. if (value is JsValue jsValue)
  475. {
  476. return jsValue;
  477. }
  478. return GetValueFromReference(value, returnReferenceToPool);
  479. }
  480. internal JsValue GetValueFromReference(object value, bool returnReferenceToPool)
  481. {
  482. var reference = value as Reference;
  483. if (reference == null)
  484. {
  485. if (value is Completion completion)
  486. {
  487. return GetValue(completion.Value, returnReferenceToPool);
  488. }
  489. }
  490. if (reference.IsUnresolvableReference())
  491. {
  492. if (_referenceResolver != null &&
  493. _referenceResolver.TryUnresolvableReference(this, reference, out JsValue val))
  494. {
  495. return val;
  496. }
  497. throw new JavaScriptException(ReferenceError, reference.GetReferencedName() + " is not defined");
  498. }
  499. var baseValue = reference.GetBase();
  500. if (reference.IsPropertyReference())
  501. {
  502. if (_referenceResolver != null &&
  503. _referenceResolver.TryPropertyReference(this, reference, ref baseValue))
  504. {
  505. return baseValue;
  506. }
  507. var referencedName = reference.GetReferencedName();
  508. if (returnReferenceToPool)
  509. {
  510. ReferencePool.Return(reference);
  511. }
  512. if (reference.HasPrimitiveBase() == false)
  513. {
  514. var o = TypeConverter.ToObject(this, baseValue);
  515. var v = o.Get(referencedName);
  516. return v;
  517. }
  518. else
  519. {
  520. var o = TypeConverter.ToObject(this, baseValue);
  521. var desc = o.GetProperty(referencedName);
  522. if (desc == PropertyDescriptor.Undefined)
  523. {
  524. return JsValue.Undefined;
  525. }
  526. if (desc.IsDataDescriptor())
  527. {
  528. return desc.Value;
  529. }
  530. var getter = desc.Get;
  531. if (getter.IsUndefined())
  532. {
  533. return Undefined.Instance;
  534. }
  535. var callable = (ICallable)getter.AsObject();
  536. return callable.Call(baseValue, Arguments.Empty);
  537. }
  538. }
  539. var record = (EnvironmentRecord) baseValue;
  540. if (ReferenceEquals(record, null))
  541. {
  542. throw new ArgumentException();
  543. }
  544. var bindingValue = record.GetBindingValue(reference.GetReferencedName(), reference.IsStrict());
  545. if (returnReferenceToPool)
  546. {
  547. ReferencePool.Return(reference);
  548. }
  549. return bindingValue;
  550. }
  551. /// <summary>
  552. /// http://www.ecma-international.org/ecma-262/5.1/#sec-8.7.2
  553. /// </summary>
  554. /// <param name="reference"></param>
  555. /// <param name="value"></param>
  556. public void PutValue(Reference reference, JsValue value)
  557. {
  558. if (reference.IsUnresolvableReference())
  559. {
  560. if (reference.IsStrict())
  561. {
  562. ThrowReferenceError();
  563. }
  564. Global.Put(reference.GetReferencedName(), value, false);
  565. }
  566. else if (reference.IsPropertyReference())
  567. {
  568. var baseValue = reference.GetBase();
  569. if (!reference.HasPrimitiveBase())
  570. {
  571. baseValue.AsObject().Put(reference.GetReferencedName(), value, reference.IsStrict());
  572. }
  573. else
  574. {
  575. PutPrimitiveBase(baseValue, reference.GetReferencedName(), value, reference.IsStrict());
  576. }
  577. }
  578. else
  579. {
  580. var baseValue = reference.GetBase();
  581. if (!(baseValue is EnvironmentRecord record))
  582. {
  583. ThrowArgumentNullException();
  584. return;
  585. }
  586. record.SetMutableBinding(reference.GetReferencedName(), value, reference.IsStrict());
  587. }
  588. }
  589. /// <summary>
  590. /// Used by PutValue when the reference has a primitive base value
  591. /// </summary>
  592. public void PutPrimitiveBase(JsValue b, string name, JsValue value, bool throwOnError)
  593. {
  594. var o = TypeConverter.ToObject(this, b);
  595. if (!o.CanPut(name))
  596. {
  597. if (throwOnError)
  598. {
  599. ThrowTypeError();
  600. }
  601. return;
  602. }
  603. var ownDesc = o.GetOwnProperty(name);
  604. if (ownDesc.IsDataDescriptor())
  605. {
  606. if (throwOnError)
  607. {
  608. ThrowTypeError();
  609. }
  610. return;
  611. }
  612. var desc = o.GetProperty(name);
  613. if (desc.IsAccessorDescriptor())
  614. {
  615. var setter = (ICallable)desc.Set.AsObject();
  616. setter.Call(b, new[] { value });
  617. }
  618. else
  619. {
  620. if (throwOnError)
  621. {
  622. ThrowTypeError();
  623. }
  624. }
  625. }
  626. /// <summary>
  627. /// Invoke the current value as function.
  628. /// </summary>
  629. /// <param name="propertyName">The name of the function to call.</param>
  630. /// <param name="arguments">The arguments of the function call.</param>
  631. /// <returns>The value returned by the function call.</returns>
  632. public JsValue Invoke(string propertyName, params object[] arguments)
  633. {
  634. return Invoke(propertyName, null, arguments);
  635. }
  636. /// <summary>
  637. /// Invoke the current value as function.
  638. /// </summary>
  639. /// <param name="propertyName">The name of the function to call.</param>
  640. /// <param name="thisObj">The this value inside the function call.</param>
  641. /// <param name="arguments">The arguments of the function call.</param>
  642. /// <returns>The value returned by the function call.</returns>
  643. public JsValue Invoke(string propertyName, object thisObj, object[] arguments)
  644. {
  645. var value = GetValue(propertyName);
  646. return Invoke(value, thisObj, arguments);
  647. }
  648. /// <summary>
  649. /// Invoke the current value as function.
  650. /// </summary>
  651. /// <param name="value">The function to call.</param>
  652. /// <param name="arguments">The arguments of the function call.</param>
  653. /// <returns>The value returned by the function call.</returns>
  654. public JsValue Invoke(JsValue value, params object[] arguments)
  655. {
  656. return Invoke(value, null, arguments);
  657. }
  658. /// <summary>
  659. /// Invoke the current value as function.
  660. /// </summary>
  661. /// <param name="value">The function to call.</param>
  662. /// <param name="thisObj">The this value inside the function call.</param>
  663. /// <param name="arguments">The arguments of the function call.</param>
  664. /// <returns>The value returned by the function call.</returns>
  665. public JsValue Invoke(JsValue value, object thisObj, object[] arguments)
  666. {
  667. var callable = value.TryCast<ICallable>();
  668. if (callable == null)
  669. {
  670. throw new ArgumentException("Can only invoke functions");
  671. }
  672. var items = JsValueArrayPool.RentArray(arguments.Length);
  673. for (int i = 0; i < arguments.Length; ++i)
  674. {
  675. items[i] = JsValue.FromObject(this, arguments[i]);
  676. }
  677. var result = callable.Call(JsValue.FromObject(this, thisObj), items);
  678. JsValueArrayPool.ReturnArray(items);
  679. return result;
  680. }
  681. /// <summary>
  682. /// Gets a named value from the Global scope.
  683. /// </summary>
  684. /// <param name="propertyName">The name of the property to return.</param>
  685. public JsValue GetValue(string propertyName)
  686. {
  687. return GetValue(Global, propertyName);
  688. }
  689. /// <summary>
  690. /// Gets the last evaluated <see cref="INode"/>.
  691. /// </summary>
  692. public INode GetLastSyntaxNode()
  693. {
  694. return _lastSyntaxNode;
  695. }
  696. /// <summary>
  697. /// Gets a named value from the specified scope.
  698. /// </summary>
  699. /// <param name="scope">The scope to get the property from.</param>
  700. /// <param name="propertyName">The name of the property to return.</param>
  701. public JsValue GetValue(JsValue scope, string propertyName)
  702. {
  703. AssertNotNullOrEmpty(nameof(propertyName), propertyName);
  704. var reference = ReferencePool.Rent(scope, propertyName, _isStrict);
  705. var jsValue = GetValue(reference);
  706. ReferencePool.Return(reference);
  707. return jsValue;
  708. }
  709. // http://www.ecma-international.org/ecma-262/5.1/#sec-10.5
  710. internal bool DeclarationBindingInstantiation(
  711. DeclarationBindingType declarationBindingType,
  712. List<FunctionDeclaration> functionDeclarations,
  713. List<VariableDeclaration> variableDeclarations,
  714. FunctionInstance functionInstance,
  715. JsValue[] arguments)
  716. {
  717. var env = ExecutionContext.VariableEnvironment.Record;
  718. bool configurableBindings = declarationBindingType == DeclarationBindingType.EvalCode;
  719. var strict = StrictModeScope.IsStrictModeCode;
  720. if (declarationBindingType == DeclarationBindingType.FunctionCode)
  721. {
  722. var parameters = functionInstance.FormalParameters;
  723. for (var i = 0; i < parameters.Length; i++)
  724. {
  725. var argName = parameters[i];
  726. var v = i + 1 > arguments.Length ? Undefined.Instance : arguments[i];
  727. var argAlreadyDeclared = env.HasBinding(argName);
  728. if (!argAlreadyDeclared)
  729. {
  730. env.CreateMutableBinding(argName, v);
  731. }
  732. env.SetMutableBinding(argName, v, strict);
  733. }
  734. }
  735. var functionDeclarationsCount = functionDeclarations.Count;
  736. for (var i = 0; i < functionDeclarationsCount; i++)
  737. {
  738. var f = functionDeclarations[i];
  739. var fn = f.Id.Name;
  740. var fo = Function.CreateFunctionObject(f);
  741. var funcAlreadyDeclared = env.HasBinding(fn);
  742. if (!funcAlreadyDeclared)
  743. {
  744. env.CreateMutableBinding(fn, configurableBindings);
  745. }
  746. else
  747. {
  748. if (ReferenceEquals(env, GlobalEnvironment.Record))
  749. {
  750. var go = Global;
  751. var existingProp = go.GetProperty(fn);
  752. if (existingProp.Configurable)
  753. {
  754. var flags = PropertyFlag.Writable | PropertyFlag.Enumerable;
  755. if (configurableBindings)
  756. {
  757. flags |= PropertyFlag.Configurable;
  758. }
  759. var descriptor = new PropertyDescriptor(Undefined.Instance, flags);
  760. go.DefineOwnProperty(fn, descriptor, true);
  761. }
  762. else
  763. {
  764. if (existingProp.IsAccessorDescriptor() || !existingProp.Enumerable)
  765. {
  766. throw new JavaScriptException(TypeError);
  767. }
  768. }
  769. }
  770. }
  771. env.SetMutableBinding(fn, fo, strict);
  772. }
  773. bool canReleaseArgumentsInstance = false;
  774. if (declarationBindingType == DeclarationBindingType.FunctionCode && !env.HasBinding("arguments"))
  775. {
  776. var argsObj = ArgumentsInstancePool.Rent(functionInstance, functionInstance.FormalParameters, arguments, env, strict);
  777. canReleaseArgumentsInstance = true;
  778. if (strict)
  779. {
  780. var declEnv = env as DeclarativeEnvironmentRecord;
  781. if (ReferenceEquals(declEnv, null))
  782. {
  783. throw new ArgumentException();
  784. }
  785. declEnv.CreateImmutableBinding("arguments", argsObj);
  786. }
  787. else
  788. {
  789. env.CreateMutableBinding("arguments", argsObj);
  790. }
  791. }
  792. // process all variable declarations in the current parser scope
  793. var variableDeclarationsCount = variableDeclarations.Count;
  794. for (var i = 0; i < variableDeclarationsCount; i++)
  795. {
  796. var variableDeclaration = variableDeclarations[i];
  797. var declarationsCount = variableDeclaration.Declarations.Count;
  798. for (var j = 0; j < declarationsCount; j++)
  799. {
  800. var d = variableDeclaration.Declarations[j];
  801. var dn = ((Identifier) d.Id).Name;
  802. var varAlreadyDeclared = env.HasBinding(dn);
  803. if (!varAlreadyDeclared)
  804. {
  805. env.CreateMutableBinding(dn, Undefined.Instance);
  806. }
  807. }
  808. }
  809. return canReleaseArgumentsInstance;
  810. }
  811. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  812. internal void UpdateLexicalEnvironment(LexicalEnvironment newEnv)
  813. {
  814. _executionContexts.ReplaceTopLexicalEnvironment(newEnv);
  815. }
  816. private static void ThrowTimeoutException()
  817. {
  818. throw new TimeoutException();
  819. }
  820. private static void ThrowStatementsCountOverflowException()
  821. {
  822. throw new StatementsCountOverflowException();
  823. }
  824. private static void ThrowArgumentOutOfRange()
  825. {
  826. throw new ArgumentOutOfRangeException();
  827. }
  828. private static void ThrowArgumentNullException()
  829. {
  830. throw new ArgumentNullException();
  831. }
  832. private void ThrowReferenceError()
  833. {
  834. throw new JavaScriptException(ReferenceError);
  835. }
  836. private static void AssertNotNullOrEmpty(string propertyname, string propertyValue)
  837. {
  838. if (string.IsNullOrEmpty(propertyValue))
  839. {
  840. throw new ArgumentException(propertyname);
  841. }
  842. }
  843. private void ThrowTypeError()
  844. {
  845. throw new JavaScriptException(TypeError);
  846. }
  847. }
  848. }