Engine.cs 47 KB

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