Engine.cs 47 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289
  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.Argument;
  8. using Jint.Native.Function;
  9. using Jint.Native.Object;
  10. using Jint.Native.Promise;
  11. using Jint.Native.Symbol;
  12. using Jint.Pooling;
  13. using Jint.Runtime;
  14. using Jint.Runtime.CallStack;
  15. using Jint.Runtime.Debugger;
  16. using Jint.Runtime.Descriptors;
  17. using Jint.Runtime.Environments;
  18. using Jint.Runtime.Interop;
  19. using Jint.Runtime.Interpreter;
  20. using Jint.Runtime.Interpreter.Expressions;
  21. using Jint.Runtime.References;
  22. namespace Jint
  23. {
  24. public partial class Engine
  25. {
  26. private static readonly ParserOptions DefaultParserOptions = new("<anonymous>")
  27. {
  28. AdaptRegexp = true,
  29. Tolerant = true
  30. };
  31. private readonly ExecutionContextStack _executionContexts;
  32. private JsValue _completionValue = JsValue.Undefined;
  33. internal Node _lastSyntaxNode;
  34. private readonly EventLoop _eventLoop = new();
  35. // lazy properties
  36. private DebugHandler _debugHandler;
  37. // cached access
  38. private readonly List<IConstraint> _constraints;
  39. internal readonly bool _isDebugMode;
  40. internal readonly bool _isStrict;
  41. internal readonly IReferenceResolver _referenceResolver;
  42. internal readonly ReferencePool _referencePool;
  43. internal readonly ArgumentsInstancePool _argumentsInstancePool;
  44. internal readonly JsValueArrayPool _jsValueArrayPool;
  45. public ITypeConverter ClrTypeConverter { get; internal set; }
  46. // cache of types used when resolving CLR type names
  47. internal readonly Dictionary<string, Type> TypeCache = new();
  48. internal static Dictionary<Type, Func<Engine, object, JsValue>> TypeMappers = new()
  49. {
  50. {typeof(bool), (engine, v) => (bool) v ? JsBoolean.True : JsBoolean.False},
  51. {typeof(byte), (engine, v) => JsNumber.Create((byte) v)},
  52. {typeof(char), (engine, v) => JsString.Create((char) v)},
  53. {typeof(DateTime), (engine, v) => engine.Realm.Intrinsics.Date.Construct((DateTime) v)},
  54. {typeof(DateTimeOffset), (engine, v) => engine.Realm.Intrinsics.Date.Construct((DateTimeOffset) v)},
  55. {typeof(decimal), (engine, v) => (JsValue) (double) (decimal) v},
  56. {typeof(double), (engine, v) => (JsValue) (double) v},
  57. {typeof(Int16), (engine, v) => JsNumber.Create((Int16) v)},
  58. {typeof(Int32), (engine, v) => JsNumber.Create((Int32) v)},
  59. {typeof(Int64), (engine, v) => (JsValue) (Int64) v},
  60. {typeof(SByte), (engine, v) => JsNumber.Create((SByte) v)},
  61. {typeof(Single), (engine, v) => (JsValue) (Single) v},
  62. {typeof(string), (engine, v) => JsString.Create((string) v)},
  63. {typeof(UInt16), (engine, v) => JsNumber.Create((UInt16) v)},
  64. {typeof(UInt32), (engine, v) => JsNumber.Create((UInt32) v)},
  65. {typeof(UInt64), (engine, v) => JsNumber.Create((UInt64) v)},
  66. {
  67. typeof(System.Text.RegularExpressions.Regex),
  68. (engine, v) => engine.Realm.Intrinsics.RegExp.Construct((System.Text.RegularExpressions.Regex) v, "")
  69. }
  70. };
  71. // shared frozen version
  72. internal readonly PropertyDescriptor _callerCalleeArgumentsThrowerConfigurable;
  73. internal readonly PropertyDescriptor _callerCalleeArgumentsThrowerNonConfigurable;
  74. internal readonly JintCallStack CallStack;
  75. // needed in initial engine setup, for example CLR function construction
  76. internal Intrinsics _originalIntrinsics;
  77. internal Host _host;
  78. /// <summary>
  79. /// Constructs a new engine instance.
  80. /// </summary>
  81. public Engine() : this((Action<Options>) null)
  82. {
  83. }
  84. /// <summary>
  85. /// Constructs a new engine instance and allows customizing options.
  86. /// </summary>
  87. public Engine(Action<Options> options)
  88. : this((engine, opts) => options?.Invoke(opts))
  89. {
  90. }
  91. /// <summary>
  92. /// Constructs a new engine with a custom <see cref="Options"/> instance.
  93. /// </summary>
  94. public Engine(Options options) : this((e, o) => e.Options = options)
  95. {
  96. }
  97. /// <summary>
  98. /// Constructs a new engine instance and allows customizing options.
  99. /// </summary>
  100. /// <remarks>The provided engine instance in callback is not guaranteed to be fully configured</remarks>
  101. public Engine(Action<Engine, Options> options)
  102. {
  103. _executionContexts = new ExecutionContextStack(2);
  104. _callerCalleeArgumentsThrowerConfigurable = new GetSetPropertyDescriptor.ThrowerPropertyDescriptor(this,
  105. PropertyFlag.Configurable | PropertyFlag.CustomJsValue,
  106. "'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them");
  107. _callerCalleeArgumentsThrowerNonConfigurable = new GetSetPropertyDescriptor.ThrowerPropertyDescriptor(this,
  108. PropertyFlag.CustomJsValue,
  109. "'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them");
  110. Options = new Options();
  111. options?.Invoke(this, Options);
  112. Reset();
  113. // gather some options as fields for faster checks
  114. _isDebugMode = Options.IsDebugMode;
  115. _isStrict = Options.IsStrict;
  116. _constraints = Options._Constraints;
  117. _referenceResolver = Options.ReferenceResolver;
  118. CallStack = new JintCallStack(Options.MaxRecursionDepth >= 0);
  119. _referencePool = new ReferencePool();
  120. _argumentsInstancePool = new ArgumentsInstancePool(this);
  121. _jsValueArrayPool = new JsValueArrayPool();
  122. Options.Apply(this);
  123. }
  124. private void Reset()
  125. {
  126. _host = Options._hostFactory(this);
  127. _host.Initialize(this);
  128. }
  129. internal ref readonly ExecutionContext ExecutionContext
  130. {
  131. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  132. get => ref _executionContexts.Peek();
  133. }
  134. // temporary state for realm so that we can easily pass it to functions while still not
  135. // having a proper execution context established
  136. internal Realm _realmInConstruction;
  137. public Realm Realm => _realmInConstruction ?? ExecutionContext.Realm;
  138. internal GlobalSymbolRegistry GlobalSymbolRegistry { get; } = new();
  139. internal long CurrentMemoryUsage { get; private set; }
  140. internal Options Options
  141. {
  142. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  143. get;
  144. private set;
  145. }
  146. public DebugHandler DebugHandler => _debugHandler ??= new DebugHandler(this);
  147. internal ExecutionContext EnterExecutionContext(
  148. EnvironmentRecord lexicalEnvironment,
  149. EnvironmentRecord variableEnvironment,
  150. Realm realm,
  151. PrivateEnvironmentRecord privateEnvironment)
  152. {
  153. var context = new ExecutionContext(
  154. lexicalEnvironment,
  155. variableEnvironment,
  156. privateEnvironment,
  157. realm,
  158. null);
  159. _executionContexts.Push(context);
  160. return context;
  161. }
  162. internal ExecutionContext EnterExecutionContext(ExecutionContext context)
  163. {
  164. _executionContexts.Push(context);
  165. return context;
  166. }
  167. public Engine SetValue(JsValue name, Delegate value)
  168. {
  169. Realm.GlobalObject.FastAddProperty(name, new DelegateWrapper(this, value), true, false, true);
  170. return this;
  171. }
  172. public Engine SetValue(JsValue name, string value)
  173. {
  174. return SetValue(name, new JsString(value));
  175. }
  176. public Engine SetValue(JsValue name, double value)
  177. {
  178. return SetValue(name, JsNumber.Create(value));
  179. }
  180. public Engine SetValue(JsValue name, int value)
  181. {
  182. return SetValue(name, JsNumber.Create(value));
  183. }
  184. public Engine SetValue(JsValue name, bool value)
  185. {
  186. return SetValue(name, value ? JsBoolean.True : JsBoolean.False);
  187. }
  188. public Engine SetValue(JsValue name, JsValue value)
  189. {
  190. Realm.GlobalObject.Set(name, value);
  191. return this;
  192. }
  193. public Engine SetValue(JsValue name, object obj)
  194. {
  195. var value = obj is Type t
  196. ? TypeReference.CreateTypeReference(this, t)
  197. : JsValue.FromObject(this, obj);
  198. return SetValue(name, value);
  199. }
  200. internal void LeaveExecutionContext()
  201. {
  202. _executionContexts.Pop();
  203. }
  204. /// <summary>
  205. /// Initializes the statements count
  206. /// </summary>
  207. public void ResetConstraints()
  208. {
  209. for (var i = 0; i < _constraints.Count; i++)
  210. {
  211. _constraints[i].Reset();
  212. }
  213. }
  214. /// <summary>
  215. /// Initializes list of references of called functions
  216. /// </summary>
  217. public void ResetCallStack()
  218. {
  219. CallStack.Clear();
  220. }
  221. public JsValue Evaluate(string source)
  222. => Execute(source, DefaultParserOptions)._completionValue;
  223. public JsValue Evaluate(string source, ParserOptions parserOptions)
  224. => Execute(source, parserOptions)._completionValue;
  225. public JsValue Evaluate(Script script)
  226. => Execute(script)._completionValue;
  227. public Engine Execute(string source)
  228. => Execute(source, DefaultParserOptions);
  229. public Engine Execute(string source, ParserOptions parserOptions)
  230. => Execute(new JavaScriptParser(source, parserOptions).ParseScript());
  231. public Engine Execute(Script script)
  232. {
  233. ResetConstraints();
  234. ResetLastStatement();
  235. using (new StrictModeScope(_isStrict || script.Strict))
  236. {
  237. GlobalDeclarationInstantiation(
  238. script,
  239. Realm.GlobalEnv);
  240. var list = new JintStatementList(this, null, script.Body);
  241. Completion result;
  242. try
  243. {
  244. result = list.Execute();
  245. }
  246. catch
  247. {
  248. // unhandled exception
  249. ResetCallStack();
  250. throw;
  251. }
  252. if (result.Type == CompletionType.Throw)
  253. {
  254. var ex = new JavaScriptException(result.GetValueOrDefault())
  255. .SetCallstack(this, result.Location);
  256. ResetCallStack();
  257. throw ex;
  258. }
  259. // TODO what about callstack and thrown exceptions?
  260. RunAvailableContinuations(_eventLoop);
  261. _completionValue = result.GetValueOrDefault();
  262. }
  263. return this;
  264. }
  265. /// <summary>
  266. /// EXPERIMENTAL! Subject to change.
  267. ///
  268. /// Registers a promise within the currently running EventLoop (has to be called within "ExecuteWithEventLoop" call).
  269. /// Note that ExecuteWithEventLoop will not trigger "onFinished" callback until ALL manual promises are settled.
  270. ///
  271. /// NOTE: that resolve and reject need to be called withing the same thread as "ExecuteWithEventLoop".
  272. /// The API assumes that the Engine is called from a single thread.
  273. /// </summary>
  274. /// <returns>a Promise instance and functions to either resolve or reject it</returns>
  275. public ManualPromise RegisterPromise()
  276. {
  277. var promise = new PromiseInstance(this)
  278. {
  279. _prototype = Realm.Intrinsics.Promise.PrototypeObject
  280. };
  281. var (resolve, reject) = promise.CreateResolvingFunctions();
  282. Action<JsValue> SettleWith(FunctionInstance settle) => value =>
  283. {
  284. settle.Call(JsValue.Undefined, new[] {value});
  285. RunAvailableContinuations(_eventLoop);
  286. };
  287. return new ManualPromise(promise, SettleWith(resolve), SettleWith(reject));
  288. }
  289. internal void AddToEventLoop(Action continuation)
  290. {
  291. _eventLoop.Events.Enqueue(continuation);
  292. }
  293. private static void RunAvailableContinuations(EventLoop loop)
  294. {
  295. var queue = loop.Events;
  296. while (true)
  297. {
  298. if (queue.Count == 0)
  299. {
  300. return;
  301. }
  302. var nextContinuation = queue.Dequeue();
  303. // note that continuation can enqueue new events
  304. nextContinuation();
  305. }
  306. }
  307. private void ResetLastStatement()
  308. {
  309. _lastSyntaxNode = null;
  310. }
  311. internal void RunBeforeExecuteStatementChecks(Statement statement)
  312. {
  313. // Avoid allocating the enumerator because we run this loop very often.
  314. for (var i = 0; i < _constraints.Count; i++)
  315. {
  316. _constraints[i].Check();
  317. }
  318. if (_isDebugMode)
  319. {
  320. DebugHandler.OnStep(statement);
  321. }
  322. }
  323. /// <summary>
  324. /// http://www.ecma-international.org/ecma-262/5.1/#sec-8.7.1
  325. /// </summary>
  326. public JsValue GetValue(object value)
  327. {
  328. return GetValue(value, false);
  329. }
  330. internal JsValue GetValue(object value, bool returnReferenceToPool)
  331. {
  332. if (value is JsValue jsValue)
  333. {
  334. return jsValue;
  335. }
  336. if (!(value is Reference reference))
  337. {
  338. return ((Completion) value).Value;
  339. }
  340. return GetValue(reference, returnReferenceToPool);
  341. }
  342. internal JsValue GetValue(Reference reference, bool returnReferenceToPool)
  343. {
  344. var baseValue = reference.GetBase();
  345. if (baseValue.IsUndefined())
  346. {
  347. if (_referenceResolver.TryUnresolvableReference(this, reference, out JsValue val))
  348. {
  349. return val;
  350. }
  351. ExceptionHelper.ThrowReferenceError(Realm, reference);
  352. }
  353. if ((baseValue._type & InternalTypes.ObjectEnvironmentRecord) == 0
  354. && _referenceResolver.TryPropertyReference(this, reference, ref baseValue))
  355. {
  356. return baseValue;
  357. }
  358. if (reference.IsPropertyReference())
  359. {
  360. var property = reference.GetReferencedName();
  361. if (returnReferenceToPool)
  362. {
  363. _referencePool.Return(reference);
  364. }
  365. if (baseValue.IsObject())
  366. {
  367. var o = TypeConverter.ToObject(Realm, baseValue);
  368. var v = o.Get(property, reference.GetThisValue());
  369. return v;
  370. }
  371. else
  372. {
  373. // check if we are accessing a string, boxing operation can be costly to do index access
  374. // we have good chance to have fast path with integer or string indexer
  375. ObjectInstance o = null;
  376. if ((property._type & (InternalTypes.String | InternalTypes.Integer)) != 0
  377. && baseValue is JsString s
  378. && TryHandleStringValue(property, s, ref o, out var jsValue))
  379. {
  380. return jsValue;
  381. }
  382. if (o is null)
  383. {
  384. o = TypeConverter.ToObject(Realm, baseValue);
  385. }
  386. var desc = o.GetProperty(property);
  387. if (desc == PropertyDescriptor.Undefined)
  388. {
  389. return JsValue.Undefined;
  390. }
  391. if (desc.IsDataDescriptor())
  392. {
  393. return desc.Value;
  394. }
  395. var getter = desc.Get;
  396. if (getter.IsUndefined())
  397. {
  398. return Undefined.Instance;
  399. }
  400. var callable = (ICallable) getter.AsObject();
  401. return callable.Call(baseValue, Arguments.Empty);
  402. }
  403. }
  404. var record = baseValue as EnvironmentRecord;
  405. if (record is null)
  406. {
  407. ExceptionHelper.ThrowArgumentException();
  408. }
  409. var bindingValue = record.GetBindingValue(reference.GetReferencedName().ToString(), reference.IsStrictReference());
  410. if (returnReferenceToPool)
  411. {
  412. _referencePool.Return(reference);
  413. }
  414. return bindingValue;
  415. }
  416. private bool TryHandleStringValue(JsValue property, JsString s, ref ObjectInstance o, out JsValue jsValue)
  417. {
  418. if (property == CommonProperties.Length)
  419. {
  420. jsValue = JsNumber.Create((uint) s.Length);
  421. return true;
  422. }
  423. if (property is JsNumber number && number.IsInteger())
  424. {
  425. var index = number.AsInteger();
  426. var str = s._value;
  427. if (index < 0 || index >= str.Length)
  428. {
  429. jsValue = JsValue.Undefined;
  430. return true;
  431. }
  432. jsValue = JsString.Create(str[index]);
  433. return true;
  434. }
  435. if (property is JsString propertyString
  436. && propertyString._value.Length > 0
  437. && char.IsLower(propertyString._value[0]))
  438. {
  439. // trying to find property that's always in prototype
  440. o = Realm.Intrinsics.String.PrototypeObject;
  441. }
  442. jsValue = JsValue.Undefined;
  443. return false;
  444. }
  445. /// <summary>
  446. /// https://tc39.es/ecma262/#sec-putvalue
  447. /// </summary>
  448. internal void PutValue(Reference reference, JsValue value)
  449. {
  450. var baseValue = reference.GetBase();
  451. if (reference.IsUnresolvableReference())
  452. {
  453. if (reference.IsStrictReference())
  454. {
  455. ExceptionHelper.ThrowReferenceError(Realm, reference);
  456. }
  457. Realm.GlobalObject.Set(reference.GetReferencedName(), value, throwOnError: false);
  458. }
  459. else if (reference.IsPropertyReference())
  460. {
  461. if (reference.HasPrimitiveBase())
  462. {
  463. baseValue = TypeConverter.ToObject(Realm, baseValue);
  464. }
  465. var succeeded = baseValue.Set(reference.GetReferencedName(), value, reference.GetThisValue());
  466. if (!succeeded && reference.IsStrictReference())
  467. {
  468. ExceptionHelper.ThrowTypeError(Realm);
  469. }
  470. }
  471. else
  472. {
  473. ((EnvironmentRecord) baseValue).SetMutableBinding(TypeConverter.ToString(reference.GetReferencedName()),
  474. value, reference.IsStrictReference());
  475. }
  476. }
  477. /// <summary>
  478. /// http://www.ecma-international.org/ecma-262/6.0/#sec-initializereferencedbinding
  479. /// </summary>
  480. public void InitializeReferenceBinding(Reference reference, JsValue value)
  481. {
  482. var baseValue = (EnvironmentRecord) reference.GetBase();
  483. baseValue.InitializeBinding(TypeConverter.ToString(reference.GetReferencedName()), value);
  484. }
  485. /// <summary>
  486. /// Invoke the current value as function.
  487. /// </summary>
  488. /// <param name="propertyName">The name of the function to call.</param>
  489. /// <param name="arguments">The arguments of the function call.</param>
  490. /// <returns>The value returned by the function call.</returns>
  491. public JsValue Invoke(string propertyName, params object[] arguments)
  492. {
  493. return Invoke(propertyName, null, arguments);
  494. }
  495. /// <summary>
  496. /// Invoke the current value as function.
  497. /// </summary>
  498. /// <param name="propertyName">The name of the function to call.</param>
  499. /// <param name="thisObj">The this value inside the function call.</param>
  500. /// <param name="arguments">The arguments of the function call.</param>
  501. /// <returns>The value returned by the function call.</returns>
  502. public JsValue Invoke(string propertyName, object thisObj, object[] arguments)
  503. {
  504. var value = GetValue(propertyName);
  505. return Invoke(value, thisObj, arguments);
  506. }
  507. /// <summary>
  508. /// Invoke the current value as function.
  509. /// </summary>
  510. /// <param name="value">The function to call.</param>
  511. /// <param name="arguments">The arguments of the function call.</param>
  512. /// <returns>The value returned by the function call.</returns>
  513. public JsValue Invoke(JsValue value, params object[] arguments)
  514. {
  515. return Invoke(value, null, arguments);
  516. }
  517. /// <summary>
  518. /// Invoke the current value as function.
  519. /// </summary>
  520. /// <param name="value">The function to call.</param>
  521. /// <param name="thisObj">The this value inside the function call.</param>
  522. /// <param name="arguments">The arguments of the function call.</param>
  523. /// <returns>The value returned by the function call.</returns>
  524. public JsValue Invoke(JsValue value, object thisObj, object[] arguments)
  525. {
  526. var callable = value as ICallable;
  527. if (callable is null)
  528. {
  529. ExceptionHelper.ThrowTypeError(Realm, "Can only invoke functions");
  530. }
  531. var items = _jsValueArrayPool.RentArray(arguments.Length);
  532. for (int i = 0; i < arguments.Length; ++i)
  533. {
  534. items[i] = JsValue.FromObject(this, arguments[i]);
  535. }
  536. var result = callable.Call(JsValue.FromObject(this, thisObj), items);
  537. _jsValueArrayPool.ReturnArray(items);
  538. return result;
  539. }
  540. /// <summary>
  541. /// https://tc39.es/ecma262/#sec-invoke
  542. /// </summary>
  543. internal JsValue Invoke(JsValue v, JsValue p, JsValue[] arguments)
  544. {
  545. var func = GetV(v, p);
  546. var callable = func as ICallable;
  547. if (callable is null)
  548. {
  549. ExceptionHelper.ThrowTypeErrorNoEngine("Can only invoke functions");
  550. }
  551. return callable.Call(v, arguments);
  552. }
  553. /// <summary>
  554. /// https://tc39.es/ecma262/#sec-getv
  555. /// </summary>
  556. internal JsValue GetV(JsValue v, JsValue p)
  557. {
  558. var o = TypeConverter.ToObject(Realm, v);
  559. return o.Get(p);
  560. }
  561. /// <summary>
  562. /// Gets a named value from the Global scope.
  563. /// </summary>
  564. /// <param name="propertyName">The name of the property to return.</param>
  565. public JsValue GetValue(string propertyName)
  566. {
  567. return GetValue(Realm.GlobalObject, new JsString(propertyName));
  568. }
  569. /// <summary>
  570. /// Gets the last evaluated <see cref="Node"/>.
  571. /// </summary>
  572. internal Node GetLastSyntaxNode()
  573. {
  574. return _lastSyntaxNode;
  575. }
  576. /// <summary>
  577. /// Gets a named value from the specified scope.
  578. /// </summary>
  579. /// <param name="scope">The scope to get the property from.</param>
  580. /// <param name="property">The name of the property to return.</param>
  581. public JsValue GetValue(JsValue scope, JsValue property)
  582. {
  583. var reference = _referencePool.Rent(scope, property, _isStrict, thisValue: null);
  584. var jsValue = GetValue(reference, false);
  585. _referencePool.Return(reference);
  586. return jsValue;
  587. }
  588. /// <summary>
  589. /// https://tc39.es/ecma262/#sec-resolvebinding
  590. /// </summary>
  591. internal Reference ResolveBinding(string name, EnvironmentRecord env = null)
  592. {
  593. env ??= ExecutionContext.LexicalEnvironment;
  594. return GetIdentifierReference(env, name, StrictModeScope.IsStrictModeCode);
  595. }
  596. private Reference GetIdentifierReference(EnvironmentRecord env, string name, bool strict)
  597. {
  598. if (env is null)
  599. {
  600. return new Reference(JsValue.Undefined, name, strict);
  601. }
  602. var envRec = env;
  603. if (envRec.HasBinding(name))
  604. {
  605. return new Reference(envRec, name, strict);
  606. }
  607. return GetIdentifierReference(env._outerEnv, name, strict);
  608. }
  609. /// <summary>
  610. /// https://tc39.es/ecma262/#sec-getnewtarget
  611. /// </summary>
  612. internal JsValue GetNewTarget(EnvironmentRecord thisEnvironment = null)
  613. {
  614. // we can take as argument if caller site has already determined the value, otherwise resolve
  615. thisEnvironment ??= ExecutionContext.GetThisEnvironment();
  616. return thisEnvironment.NewTarget;
  617. }
  618. /// <summary>
  619. /// https://tc39.es/ecma262/#sec-resolvethisbinding
  620. /// </summary>
  621. internal JsValue ResolveThisBinding()
  622. {
  623. var envRec = ExecutionContext.GetThisEnvironment();
  624. return envRec.GetThisBinding();
  625. }
  626. /// <summary>
  627. /// https://tc39.es/ecma262/#sec-globaldeclarationinstantiation
  628. /// </summary>
  629. private void GlobalDeclarationInstantiation(
  630. Script script,
  631. GlobalEnvironmentRecord env)
  632. {
  633. var hoistingScope = HoistingScope.GetProgramLevelDeclarations(script);
  634. var functionDeclarations = hoistingScope._functionDeclarations;
  635. var varDeclarations = hoistingScope._variablesDeclarations;
  636. var lexDeclarations = hoistingScope._lexicalDeclarations;
  637. var functionToInitialize = new LinkedList<FunctionDeclaration>();
  638. var declaredFunctionNames = new HashSet<string>();
  639. var declaredVarNames = new List<string>();
  640. var realm = Realm;
  641. if (functionDeclarations != null)
  642. {
  643. for (var i = functionDeclarations.Count - 1; i >= 0; i--)
  644. {
  645. var d = functionDeclarations[i];
  646. var fn = d.Id.Name;
  647. if (!declaredFunctionNames.Contains(fn))
  648. {
  649. var fnDefinable = env.CanDeclareGlobalFunction(fn);
  650. if (!fnDefinable)
  651. {
  652. ExceptionHelper.ThrowTypeError(realm);
  653. }
  654. declaredFunctionNames.Add(fn);
  655. functionToInitialize.AddFirst(d);
  656. }
  657. }
  658. }
  659. var boundNames = new List<string>();
  660. if (varDeclarations != null)
  661. {
  662. for (var i = 0; i < varDeclarations.Count; i++)
  663. {
  664. var d = varDeclarations[i];
  665. boundNames.Clear();
  666. d.GetBoundNames(boundNames);
  667. for (var j = 0; j < boundNames.Count; j++)
  668. {
  669. var vn = boundNames[j];
  670. if (env.HasLexicalDeclaration(vn))
  671. {
  672. ExceptionHelper.ThrowSyntaxError(realm, $"Identifier '{vn}' has already been declared");
  673. }
  674. if (!declaredFunctionNames.Contains(vn))
  675. {
  676. var vnDefinable = env.CanDeclareGlobalVar(vn);
  677. if (!vnDefinable)
  678. {
  679. ExceptionHelper.ThrowTypeError(realm);
  680. }
  681. declaredVarNames.Add(vn);
  682. }
  683. }
  684. }
  685. }
  686. if (lexDeclarations != null)
  687. {
  688. for (var i = 0; i < lexDeclarations.Count; i++)
  689. {
  690. var d = lexDeclarations[i];
  691. boundNames.Clear();
  692. d.GetBoundNames(boundNames);
  693. for (var j = 0; j < boundNames.Count; j++)
  694. {
  695. var dn = boundNames[j];
  696. if (env.HasVarDeclaration(dn)
  697. || env.HasLexicalDeclaration(dn)
  698. || env.HasRestrictedGlobalProperty(dn))
  699. {
  700. ExceptionHelper.ThrowSyntaxError(realm, $"Identifier '{dn}' has already been declared");
  701. }
  702. if (d.Kind == VariableDeclarationKind.Const)
  703. {
  704. env.CreateImmutableBinding(dn, strict: true);
  705. }
  706. else
  707. {
  708. env.CreateMutableBinding(dn, canBeDeleted: false);
  709. }
  710. }
  711. }
  712. }
  713. foreach (var f in functionToInitialize)
  714. {
  715. var fn = f.Id!.Name;
  716. if (env.HasLexicalDeclaration(fn))
  717. {
  718. ExceptionHelper.ThrowSyntaxError(realm, $"Identifier '{fn}' has already been declared");
  719. }
  720. var fo = realm.Intrinsics.Function.InstantiateFunctionObject(f, env);
  721. env.CreateGlobalFunctionBinding(fn, fo, canBeDeleted: false);
  722. }
  723. for (var i = 0; i < declaredVarNames.Count; i++)
  724. {
  725. var vn = declaredVarNames[i];
  726. env.CreateGlobalVarBinding(vn, canBeDeleted: false);
  727. }
  728. }
  729. /// <summary>
  730. /// https://tc39.es/ecma262/#sec-functiondeclarationinstantiation
  731. /// </summary>
  732. internal ArgumentsInstance FunctionDeclarationInstantiation(
  733. FunctionInstance functionInstance,
  734. JsValue[] argumentsList,
  735. EnvironmentRecord env)
  736. {
  737. var func = functionInstance._functionDefinition;
  738. var envRec = (FunctionEnvironmentRecord) env;
  739. var strict = StrictModeScope.IsStrictModeCode;
  740. var configuration = func.Initialize(functionInstance);
  741. var parameterNames = configuration.ParameterNames;
  742. var hasDuplicates = configuration.HasDuplicates;
  743. var simpleParameterList = configuration.IsSimpleParameterList;
  744. var hasParameterExpressions = configuration.HasParameterExpressions;
  745. var canInitializeParametersOnDeclaration = simpleParameterList && !configuration.HasDuplicates;
  746. envRec.InitializeParameters(parameterNames, hasDuplicates,
  747. canInitializeParametersOnDeclaration ? argumentsList : null);
  748. ArgumentsInstance ao = null;
  749. if (configuration.ArgumentsObjectNeeded)
  750. {
  751. if (strict || !simpleParameterList)
  752. {
  753. ao = CreateUnmappedArgumentsObject(argumentsList);
  754. }
  755. else
  756. {
  757. // NOTE: mapped argument object is only provided for non-strict functions that don't have a rest parameter,
  758. // any parameter default value initializers, or any destructured parameters.
  759. ao = CreateMappedArgumentsObject(functionInstance, parameterNames, argumentsList, envRec,
  760. configuration.HasRestParameter);
  761. }
  762. if (strict)
  763. {
  764. envRec.CreateImmutableBindingAndInitialize(KnownKeys.Arguments, strict: false, ao);
  765. }
  766. else
  767. {
  768. envRec.CreateMutableBindingAndInitialize(KnownKeys.Arguments, canBeDeleted: false, ao);
  769. }
  770. }
  771. if (!canInitializeParametersOnDeclaration)
  772. {
  773. // slower set
  774. envRec.AddFunctionParameters(func.Function, argumentsList);
  775. }
  776. // Let iteratorRecord be CreateListIteratorRecord(argumentsList).
  777. // If hasDuplicates is true, then
  778. // Perform ? IteratorBindingInitialization for formals with iteratorRecord and undefined as arguments.
  779. // Else,
  780. // Perform ? IteratorBindingInitialization for formals with iteratorRecord and env as arguments.
  781. EnvironmentRecord varEnv;
  782. DeclarativeEnvironmentRecord varEnvRec;
  783. if (!hasParameterExpressions)
  784. {
  785. // NOTE: Only a single lexical environment is needed for the parameters and top-level vars.
  786. for (var i = 0; i < configuration.VarsToInitialize.Count; i++)
  787. {
  788. var pair = configuration.VarsToInitialize[i];
  789. envRec.CreateMutableBindingAndInitialize(pair.Name, canBeDeleted: false, JsValue.Undefined);
  790. }
  791. varEnv = env;
  792. varEnvRec = envRec;
  793. }
  794. else
  795. {
  796. // NOTE: A separate Environment Record is needed to ensure that closures created by expressions
  797. // in the formal parameter list do not have visibility of declarations in the function body.
  798. varEnv = JintEnvironment.NewDeclarativeEnvironment(this, env);
  799. varEnvRec = (DeclarativeEnvironmentRecord) varEnv;
  800. UpdateVariableEnvironment(varEnv);
  801. for (var i = 0; i < configuration.VarsToInitialize.Count; i++)
  802. {
  803. var pair = configuration.VarsToInitialize[i];
  804. var initialValue = pair.InitialValue ?? envRec.GetBindingValue(pair.Name, strict: false);
  805. varEnvRec.CreateMutableBindingAndInitialize(pair.Name, canBeDeleted: false, initialValue);
  806. }
  807. }
  808. // NOTE: Annex B.3.3.1 adds additional steps at this point.
  809. // A https://tc39.es/ecma262/#sec-web-compat-functiondeclarationinstantiation
  810. EnvironmentRecord lexEnv;
  811. if (!strict)
  812. {
  813. lexEnv = JintEnvironment.NewDeclarativeEnvironment(this, varEnv);
  814. // NOTE: Non-strict functions use a separate lexical Environment Record for top-level lexical declarations
  815. // so that a direct eval can determine whether any var scoped declarations introduced by the eval code conflict
  816. // with pre-existing top-level lexically scoped declarations. This is not needed for strict functions
  817. // because a strict direct eval always places all declarations into a new Environment Record.
  818. }
  819. else
  820. {
  821. lexEnv = varEnv;
  822. }
  823. var lexEnvRec = lexEnv;
  824. UpdateLexicalEnvironment(lexEnv);
  825. if (configuration.LexicalDeclarations.Length > 0)
  826. {
  827. InitializeLexicalDeclarations(configuration.LexicalDeclarations, lexEnvRec);
  828. }
  829. if (configuration.FunctionsToInitialize != null)
  830. {
  831. InitializeFunctions(configuration.FunctionsToInitialize, lexEnv, varEnvRec);
  832. }
  833. return ao;
  834. }
  835. private void InitializeFunctions(
  836. LinkedList<FunctionDeclaration> functionsToInitialize,
  837. EnvironmentRecord lexEnv,
  838. DeclarativeEnvironmentRecord varEnvRec)
  839. {
  840. var realm = Realm;
  841. foreach (var f in functionsToInitialize)
  842. {
  843. var fn = f.Id.Name;
  844. var fo = realm.Intrinsics.Function.InstantiateFunctionObject(f, lexEnv);
  845. varEnvRec.SetMutableBinding(fn, fo, strict: false);
  846. }
  847. }
  848. private static void InitializeLexicalDeclarations(
  849. JintFunctionDefinition.State.LexicalVariableDeclaration[] lexicalDeclarations,
  850. EnvironmentRecord lexEnvRec)
  851. {
  852. foreach (var d in lexicalDeclarations)
  853. {
  854. for (var j = 0; j < d.BoundNames.Count; j++)
  855. {
  856. var dn = d.BoundNames[j];
  857. if (d.Kind == VariableDeclarationKind.Const)
  858. {
  859. lexEnvRec.CreateImmutableBinding(dn, strict: true);
  860. }
  861. else
  862. {
  863. lexEnvRec.CreateMutableBinding(dn, canBeDeleted: false);
  864. }
  865. }
  866. }
  867. }
  868. private ArgumentsInstance CreateMappedArgumentsObject(
  869. FunctionInstance func,
  870. Key[] formals,
  871. JsValue[] argumentsList,
  872. DeclarativeEnvironmentRecord envRec,
  873. bool hasRestParameter)
  874. {
  875. return _argumentsInstancePool.Rent(func, formals, argumentsList, envRec, hasRestParameter);
  876. }
  877. private ArgumentsInstance CreateUnmappedArgumentsObject(JsValue[] argumentsList)
  878. {
  879. return _argumentsInstancePool.Rent(argumentsList);
  880. }
  881. /// <summary>
  882. /// https://tc39.es/ecma262/#sec-evaldeclarationinstantiation
  883. /// </summary>
  884. internal void EvalDeclarationInstantiation(
  885. Script script,
  886. EnvironmentRecord varEnv,
  887. EnvironmentRecord lexEnv,
  888. PrivateEnvironmentRecord privateEnv,
  889. bool strict)
  890. {
  891. var hoistingScope = HoistingScope.GetProgramLevelDeclarations(script);
  892. var lexEnvRec = (DeclarativeEnvironmentRecord) lexEnv;
  893. var varEnvRec = varEnv;
  894. var realm = Realm;
  895. if (!strict && hoistingScope._variablesDeclarations != null)
  896. {
  897. if (varEnvRec is GlobalEnvironmentRecord globalEnvironmentRecord)
  898. {
  899. ref readonly var nodes = ref hoistingScope._variablesDeclarations;
  900. for (var i = 0; i < nodes.Count; i++)
  901. {
  902. var variablesDeclaration = nodes[i];
  903. var identifier = (Identifier) variablesDeclaration.Declarations[0].Id;
  904. if (globalEnvironmentRecord.HasLexicalDeclaration(identifier.Name))
  905. {
  906. ExceptionHelper.ThrowSyntaxError(realm, "Identifier '" + identifier.Name + "' has already been declared");
  907. }
  908. }
  909. }
  910. var thisLex = lexEnv;
  911. while (!ReferenceEquals(thisLex, varEnv))
  912. {
  913. var thisEnvRec = thisLex;
  914. if (!(thisEnvRec is ObjectEnvironmentRecord))
  915. {
  916. ref readonly var nodes = ref hoistingScope._variablesDeclarations;
  917. for (var i = 0; i < nodes.Count; i++)
  918. {
  919. var variablesDeclaration = nodes[i];
  920. var identifier = (Identifier) variablesDeclaration.Declarations[0].Id;
  921. if (thisEnvRec.HasBinding(identifier.Name))
  922. {
  923. ExceptionHelper.ThrowSyntaxError(realm);
  924. }
  925. }
  926. }
  927. thisLex = thisLex._outerEnv;
  928. }
  929. }
  930. var functionDeclarations = hoistingScope._functionDeclarations;
  931. var functionsToInitialize = new LinkedList<FunctionDeclaration>();
  932. var declaredFunctionNames = new HashSet<string>();
  933. if (functionDeclarations != null)
  934. {
  935. for (var i = functionDeclarations.Count - 1; i >= 0; i--)
  936. {
  937. var d = functionDeclarations[i];
  938. var fn = d.Id.Name;
  939. if (!declaredFunctionNames.Contains(fn))
  940. {
  941. if (varEnvRec is GlobalEnvironmentRecord ger)
  942. {
  943. var fnDefinable = ger.CanDeclareGlobalFunction(fn);
  944. if (!fnDefinable)
  945. {
  946. ExceptionHelper.ThrowTypeError(realm);
  947. }
  948. }
  949. declaredFunctionNames.Add(fn);
  950. functionsToInitialize.AddFirst(d);
  951. }
  952. }
  953. }
  954. var boundNames = new List<string>();
  955. var declaredVarNames = new List<string>();
  956. var variableDeclarations = hoistingScope._variablesDeclarations;
  957. var variableDeclarationsCount = variableDeclarations?.Count;
  958. for (var i = 0; i < variableDeclarationsCount; i++)
  959. {
  960. var variableDeclaration = variableDeclarations[i];
  961. boundNames.Clear();
  962. variableDeclaration.GetBoundNames(boundNames);
  963. for (var j = 0; j < boundNames.Count; j++)
  964. {
  965. var vn = boundNames[j];
  966. if (!declaredFunctionNames.Contains(vn))
  967. {
  968. if (varEnvRec is GlobalEnvironmentRecord ger)
  969. {
  970. var vnDefinable = ger.CanDeclareGlobalFunction(vn);
  971. if (!vnDefinable)
  972. {
  973. ExceptionHelper.ThrowTypeError(realm);
  974. }
  975. }
  976. declaredVarNames.Add(vn);
  977. }
  978. }
  979. }
  980. var lexicalDeclarations = hoistingScope._lexicalDeclarations;
  981. var lexicalDeclarationsCount = lexicalDeclarations?.Count;
  982. for (var i = 0; i < lexicalDeclarationsCount; i++)
  983. {
  984. boundNames.Clear();
  985. var d = lexicalDeclarations[i];
  986. d.GetBoundNames(boundNames);
  987. for (var j = 0; j < boundNames.Count; j++)
  988. {
  989. var dn = boundNames[j];
  990. if (d.Kind == VariableDeclarationKind.Const)
  991. {
  992. lexEnvRec.CreateImmutableBinding(dn, strict: true);
  993. }
  994. else
  995. {
  996. lexEnvRec.CreateMutableBinding(dn, canBeDeleted: false);
  997. }
  998. }
  999. }
  1000. foreach (var f in functionsToInitialize)
  1001. {
  1002. var fn = f.Id.Name;
  1003. var fo = realm.Intrinsics.Function.InstantiateFunctionObject(f, lexEnv);
  1004. if (varEnvRec is GlobalEnvironmentRecord ger)
  1005. {
  1006. ger.CreateGlobalFunctionBinding(fn, fo, canBeDeleted: true);
  1007. }
  1008. else
  1009. {
  1010. var bindingExists = varEnvRec.HasBinding(fn);
  1011. if (!bindingExists)
  1012. {
  1013. varEnvRec.CreateMutableBinding(fn, canBeDeleted: true);
  1014. varEnvRec.InitializeBinding(fn, fo);
  1015. }
  1016. else
  1017. {
  1018. varEnvRec.SetMutableBinding(fn, fo, strict: false);
  1019. }
  1020. }
  1021. }
  1022. foreach (var vn in declaredVarNames)
  1023. {
  1024. if (varEnvRec is GlobalEnvironmentRecord ger)
  1025. {
  1026. ger.CreateGlobalVarBinding(vn, true);
  1027. }
  1028. else
  1029. {
  1030. var bindingExists = varEnvRec.HasBinding(vn);
  1031. if (!bindingExists)
  1032. {
  1033. varEnvRec.CreateMutableBinding(vn, canBeDeleted: true);
  1034. varEnvRec.InitializeBinding(vn, JsValue.Undefined);
  1035. }
  1036. }
  1037. }
  1038. }
  1039. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1040. internal void UpdateLexicalEnvironment(EnvironmentRecord newEnv)
  1041. {
  1042. _executionContexts.ReplaceTopLexicalEnvironment(newEnv);
  1043. }
  1044. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1045. internal void UpdateVariableEnvironment(EnvironmentRecord newEnv)
  1046. {
  1047. _executionContexts.ReplaceTopVariableEnvironment(newEnv);
  1048. }
  1049. internal JsValue Call(ICallable callable, JsValue thisObject, JsValue[] arguments, JintExpression expression)
  1050. {
  1051. if (callable is FunctionInstance functionInstance)
  1052. {
  1053. return Call(functionInstance, thisObject, arguments, expression);
  1054. }
  1055. return callable.Call(thisObject, arguments);
  1056. }
  1057. internal JsValue Construct(IConstructor constructor, JsValue[] arguments, JsValue newTarget,
  1058. JintExpression expression)
  1059. {
  1060. if (constructor is FunctionInstance functionInstance)
  1061. {
  1062. return Construct(functionInstance, arguments, newTarget, expression);
  1063. }
  1064. return constructor.Construct(arguments, newTarget);
  1065. }
  1066. internal JsValue Call(
  1067. FunctionInstance functionInstance,
  1068. JsValue thisObject,
  1069. JsValue[] arguments,
  1070. JintExpression expression)
  1071. {
  1072. var callStackElement = new CallStackElement(functionInstance, expression, ExecutionContext);
  1073. var recursionDepth = CallStack.Push(callStackElement);
  1074. if (recursionDepth > Options.MaxRecursionDepth)
  1075. {
  1076. // pop the current element as it was never reached
  1077. CallStack.Pop();
  1078. ExceptionHelper.ThrowRecursionDepthOverflowException(CallStack, callStackElement.ToString());
  1079. }
  1080. var result = functionInstance.Call(thisObject, arguments);
  1081. CallStack.Pop();
  1082. return result;
  1083. }
  1084. internal JsValue Construct(
  1085. FunctionInstance functionInstance,
  1086. JsValue[] arguments,
  1087. JsValue newTarget,
  1088. JintExpression expression)
  1089. {
  1090. var callStackElement = new CallStackElement(functionInstance, expression, ExecutionContext);
  1091. var recursionDepth = CallStack.Push(callStackElement);
  1092. if (recursionDepth > Options.MaxRecursionDepth)
  1093. {
  1094. // pop the current element as it was never reached
  1095. CallStack.Pop();
  1096. ExceptionHelper.ThrowRecursionDepthOverflowException(CallStack, callStackElement.ToString());
  1097. }
  1098. var result = ((IConstructor) functionInstance).Construct(arguments, newTarget);
  1099. CallStack.Pop();
  1100. return result;
  1101. }
  1102. }
  1103. }