Engine.cs 58 KB

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