Engine.cs 47 KB

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