2
0

Engine.cs 52 KB

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