Engine.cs 57 KB

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