Engine.cs 47 KB

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