2
0

Engine.cs 47 KB

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