Engine.cs 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875
  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. using ExecutionContext = Jint.Runtime.Environments.ExecutionContext;
  37. namespace Jint
  38. {
  39. public class Engine
  40. {
  41. private static readonly ParserOptions DefaultParserOptions = new ParserOptions
  42. {
  43. AdaptRegexp = true,
  44. Tolerant = true,
  45. Loc = true
  46. };
  47. private static readonly JsString _errorFunctionName = new JsString("Error");
  48. private static readonly JsString _evalErrorFunctionName = new JsString("EvalError");
  49. private static readonly JsString _rangeErrorFunctionName = new JsString("RangeError");
  50. private static readonly JsString _referenceErrorFunctionName = new JsString("ReferenceError");
  51. private static readonly JsString _syntaxErrorFunctionName = new JsString("SyntaxError");
  52. private static readonly JsString _typeErrorFunctionName = new JsString("TypeError");
  53. private static readonly JsString _uriErrorFunctionName = new JsString("URIError");
  54. private readonly ExecutionContextStack _executionContexts;
  55. private JsValue _completionValue = JsValue.Undefined;
  56. private int _statementsCount;
  57. private long _initialMemoryUsage;
  58. private long _timeoutTicks;
  59. internal INode _lastSyntaxNode;
  60. // lazy properties
  61. private ErrorConstructor _error;
  62. private ErrorConstructor _evalError;
  63. private ErrorConstructor _rangeError;
  64. private ErrorConstructor _referenceError;
  65. private ErrorConstructor _syntaxError;
  66. private ErrorConstructor _typeError;
  67. private ErrorConstructor _uriError;
  68. private DebugHandler _debugHandler;
  69. private List<BreakPoint> _breakPoints;
  70. // cached access
  71. private readonly bool _isDebugMode;
  72. internal readonly bool _isStrict;
  73. private readonly int _maxStatements;
  74. private readonly long _memoryLimit;
  75. internal readonly bool _runBeforeStatementChecks;
  76. internal readonly IReferenceResolver _referenceResolver;
  77. internal readonly ReferencePool _referencePool;
  78. internal readonly ArgumentsInstancePool _argumentsInstancePool;
  79. internal readonly JsValueArrayPool _jsValueArrayPool;
  80. public ITypeConverter ClrTypeConverter { get; set; }
  81. // cache of types used when resolving CLR type names
  82. internal readonly Dictionary<string, Type> TypeCache = new Dictionary<string, Type>();
  83. internal static Dictionary<Type, Func<Engine, object, JsValue>> TypeMappers = new Dictionary<Type, Func<Engine, object, JsValue>>
  84. {
  85. { typeof(bool), (engine, v) => (bool) v ? JsBoolean.True : JsBoolean.False },
  86. { typeof(byte), (engine, v) => JsNumber.Create((byte)v) },
  87. { typeof(char), (engine, v) => JsString.Create((char)v) },
  88. { typeof(DateTime), (engine, v) => engine.Date.Construct((DateTime)v) },
  89. { typeof(DateTimeOffset), (engine, v) => engine.Date.Construct((DateTimeOffset)v) },
  90. { typeof(decimal), (engine, v) => (JsValue) (double)(decimal)v },
  91. { typeof(double), (engine, v) => (JsValue)(double)v },
  92. { typeof(Int16), (engine, v) => JsNumber.Create((Int16)v) },
  93. { typeof(Int32), (engine, v) => JsNumber.Create((Int32)v) },
  94. { typeof(Int64), (engine, v) => (JsValue)(Int64)v },
  95. { typeof(SByte), (engine, v) => JsNumber.Create((SByte)v) },
  96. { typeof(Single), (engine, v) => (JsValue)(Single)v },
  97. { typeof(string), (engine, v) => (JsValue) (string)v },
  98. { typeof(UInt16), (engine, v) => JsNumber.Create((UInt16)v) },
  99. { typeof(UInt32), (engine, v) => JsNumber.Create((UInt32)v) },
  100. { typeof(UInt64), (engine, v) => JsNumber.Create((UInt64)v) },
  101. { typeof(System.Text.RegularExpressions.Regex), (engine, v) => engine.RegExp.Construct((System.Text.RegularExpressions.Regex)v, "", engine) }
  102. };
  103. // shared frozen version
  104. internal readonly PropertyDescriptor _getSetThrower;
  105. internal readonly struct ClrPropertyDescriptorFactoriesKey : IEquatable<ClrPropertyDescriptorFactoriesKey>
  106. {
  107. public ClrPropertyDescriptorFactoriesKey(Type type, in Key propertyName)
  108. {
  109. Type = type;
  110. PropertyName = propertyName;
  111. }
  112. private readonly Type Type;
  113. private readonly Key PropertyName;
  114. public bool Equals(ClrPropertyDescriptorFactoriesKey other)
  115. {
  116. return Type == other.Type && PropertyName == other.PropertyName;
  117. }
  118. public override bool Equals(object obj)
  119. {
  120. if (ReferenceEquals(null, obj))
  121. {
  122. return false;
  123. }
  124. return obj is ClrPropertyDescriptorFactoriesKey other && Equals(other);
  125. }
  126. public override int GetHashCode()
  127. {
  128. unchecked
  129. {
  130. return (Type.GetHashCode() * 397) ^ PropertyName.GetHashCode();
  131. }
  132. }
  133. }
  134. internal readonly Dictionary<ClrPropertyDescriptorFactoriesKey, Func<Engine, object, PropertyDescriptor>> ClrPropertyDescriptorFactories =
  135. new Dictionary<ClrPropertyDescriptorFactoriesKey, Func<Engine, object, PropertyDescriptor>>();
  136. internal readonly JintCallStack CallStack = new JintCallStack();
  137. static Engine()
  138. {
  139. var methodInfo = typeof(GC).GetMethod("GetAllocatedBytesForCurrentThread");
  140. if (methodInfo != null)
  141. {
  142. GetAllocatedBytesForCurrentThread = (Func<long>)Delegate.CreateDelegate(typeof(Func<long>), null, methodInfo);
  143. }
  144. }
  145. public Engine() : this(null)
  146. {
  147. }
  148. public Engine(Action<Options> options)
  149. {
  150. _executionContexts = new ExecutionContextStack(2);
  151. Global = GlobalObject.CreateGlobalObject(this);
  152. Object = ObjectConstructor.CreateObjectConstructor(this);
  153. Function = FunctionConstructor.CreateFunctionConstructor(this);
  154. _getSetThrower = new GetSetPropertyDescriptor.ThrowerPropertyDescriptor(Function.ThrowTypeError);
  155. Symbol = SymbolConstructor.CreateSymbolConstructor(this);
  156. Array = ArrayConstructor.CreateArrayConstructor(this);
  157. Map = MapConstructor.CreateMapConstructor(this);
  158. Set = SetConstructor.CreateSetConstructor(this);
  159. Iterator = IteratorConstructor.CreateIteratorConstructor(this);
  160. String = StringConstructor.CreateStringConstructor(this);
  161. RegExp = RegExpConstructor.CreateRegExpConstructor(this);
  162. Number = NumberConstructor.CreateNumberConstructor(this);
  163. Boolean = BooleanConstructor.CreateBooleanConstructor(this);
  164. Date = DateConstructor.CreateDateConstructor(this);
  165. Math = MathInstance.CreateMathObject(this);
  166. Json = JsonInstance.CreateJsonObject(this);
  167. Proxy = ProxyConstructor.CreateProxyConstructor(this);
  168. Reflect = ReflectInstance.CreateReflectObject(this);
  169. GlobalSymbolRegistry = new GlobalSymbolRegistry();
  170. // Because the properties might need some of the built-in object
  171. // their configuration is delayed to a later step
  172. // this is implementation dependent, and only to pass some unit tests
  173. Global._prototype = Object.PrototypeObject;
  174. Object._prototype = Function.PrototypeObject;
  175. // create the global environment http://www.ecma-international.org/ecma-262/5.1/#sec-10.2.3
  176. GlobalEnvironment = LexicalEnvironment.NewObjectEnvironment(this, Global, null, false);
  177. // create the global execution context http://www.ecma-international.org/ecma-262/5.1/#sec-10.4.1.1
  178. EnterExecutionContext(GlobalEnvironment, GlobalEnvironment, Global);
  179. Options = new Options();
  180. options?.Invoke(Options);
  181. // gather some options as fields for faster checks
  182. _isDebugMode = Options.IsDebugMode;
  183. _isStrict = Options.IsStrict;
  184. _maxStatements = Options._MaxStatements;
  185. _referenceResolver = Options.ReferenceResolver;
  186. _memoryLimit = Options._MemoryLimit;
  187. _runBeforeStatementChecks = (_maxStatements > 0 &&_maxStatements < int.MaxValue)
  188. || Options._TimeoutInterval.Ticks > 0
  189. || _memoryLimit > 0
  190. || _isDebugMode;
  191. _referencePool = new ReferencePool();
  192. _argumentsInstancePool = new ArgumentsInstancePool(this);
  193. _jsValueArrayPool = new JsValueArrayPool();
  194. Eval = new EvalFunctionInstance(this, ArrayExt.Empty<string>(), LexicalEnvironment.NewDeclarativeEnvironment(this, ExecutionContext.LexicalEnvironment), StrictModeScope.IsStrictModeCode);
  195. Global.SetProperty(KnownKeys.Eval, new PropertyDescriptor(Eval, PropertyFlag.Configurable | PropertyFlag.Writable));
  196. if (Options._IsClrAllowed)
  197. {
  198. Global.SetProperty("System", new PropertyDescriptor(new NamespaceReference(this, "System"), PropertyFlag.AllForbidden));
  199. Global.SetProperty("importNamespace", new PropertyDescriptor(new ClrFunctionInstance(
  200. this,
  201. "importNamespace",
  202. (thisObj, arguments) => new NamespaceReference(this, TypeConverter.ToString(arguments.At(0)))), PropertyFlag.AllForbidden));
  203. }
  204. ClrTypeConverter = new DefaultTypeConverter(this);
  205. }
  206. public LexicalEnvironment GlobalEnvironment { get; }
  207. public GlobalObject Global { get; }
  208. public ObjectConstructor Object { get; }
  209. public FunctionConstructor Function { get; }
  210. public ArrayConstructor Array { get; }
  211. public MapConstructor Map { get; }
  212. public SetConstructor Set { get; }
  213. public IteratorConstructor Iterator { get; }
  214. public StringConstructor String { get; }
  215. public RegExpConstructor RegExp { get; }
  216. public BooleanConstructor Boolean { get; }
  217. public NumberConstructor Number { get; }
  218. public DateConstructor Date { get; }
  219. public MathInstance Math { get; }
  220. public JsonInstance Json { get; }
  221. public ProxyConstructor Proxy { get; }
  222. public ReflectInstance Reflect { get; }
  223. public SymbolConstructor Symbol { get; }
  224. public EvalFunctionInstance Eval { get; }
  225. public ErrorConstructor Error => _error ?? (_error = ErrorConstructor.CreateErrorConstructor(this, _errorFunctionName));
  226. public ErrorConstructor EvalError => _evalError ?? (_evalError = ErrorConstructor.CreateErrorConstructor(this, _evalErrorFunctionName));
  227. public ErrorConstructor SyntaxError => _syntaxError ?? (_syntaxError = ErrorConstructor.CreateErrorConstructor(this, _syntaxErrorFunctionName));
  228. public ErrorConstructor TypeError => _typeError ?? (_typeError = ErrorConstructor.CreateErrorConstructor(this, _typeErrorFunctionName));
  229. public ErrorConstructor RangeError => _rangeError ?? (_rangeError = ErrorConstructor.CreateErrorConstructor(this, _rangeErrorFunctionName));
  230. public ErrorConstructor ReferenceError => _referenceError ?? (_referenceError = ErrorConstructor.CreateErrorConstructor(this, _referenceErrorFunctionName));
  231. public ErrorConstructor UriError => _uriError ?? (_uriError = ErrorConstructor.CreateErrorConstructor(this, _uriErrorFunctionName));
  232. public ref readonly ExecutionContext ExecutionContext
  233. {
  234. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  235. get => ref _executionContexts.Peek();
  236. }
  237. public GlobalSymbolRegistry GlobalSymbolRegistry { get; }
  238. internal Options Options { [MethodImpl(MethodImplOptions.AggressiveInlining)] get; }
  239. #region Debugger
  240. public delegate StepMode DebugStepDelegate(object sender, DebugInformation e);
  241. public delegate StepMode BreakDelegate(object sender, DebugInformation e);
  242. public event DebugStepDelegate Step;
  243. public event BreakDelegate Break;
  244. internal DebugHandler DebugHandler => _debugHandler ?? (_debugHandler = new DebugHandler(this));
  245. public List<BreakPoint> BreakPoints => _breakPoints ?? (_breakPoints = new List<BreakPoint>());
  246. internal StepMode? InvokeStepEvent(DebugInformation info)
  247. {
  248. return Step?.Invoke(this, info);
  249. }
  250. internal StepMode? InvokeBreakEvent(DebugInformation info)
  251. {
  252. return Break?.Invoke(this, info);
  253. }
  254. #endregion
  255. private static readonly Func<long> GetAllocatedBytesForCurrentThread;
  256. public void EnterExecutionContext(
  257. LexicalEnvironment lexicalEnvironment,
  258. LexicalEnvironment variableEnvironment,
  259. JsValue thisBinding)
  260. {
  261. var context = new ExecutionContext(
  262. lexicalEnvironment,
  263. variableEnvironment,
  264. thisBinding);
  265. _executionContexts.Push(context);
  266. }
  267. public Engine SetValue(in Key name, Delegate value)
  268. {
  269. Global.FastAddProperty(name, new DelegateWrapper(this, value), true, false, true);
  270. return this;
  271. }
  272. public Engine SetValue(in Key name, string value)
  273. {
  274. return SetValue(name, (JsValue) value);
  275. }
  276. public Engine SetValue(in Key name, double value)
  277. {
  278. return SetValue(name, JsNumber.Create(value));
  279. }
  280. public Engine SetValue(in Key name, int value)
  281. {
  282. return SetValue(name, JsNumber.Create(value));
  283. }
  284. public Engine SetValue(in Key name, bool value)
  285. {
  286. return SetValue(name, value ? JsBoolean.True : JsBoolean.False);
  287. }
  288. public Engine SetValue(in Key name, JsValue value)
  289. {
  290. Global.Set(name, value, Global);
  291. return this;
  292. }
  293. public Engine SetValue(in Key name, object obj)
  294. {
  295. return SetValue(name, JsValue.FromObject(this, obj));
  296. }
  297. public void LeaveExecutionContext()
  298. {
  299. _executionContexts.Pop();
  300. }
  301. /// <summary>
  302. /// Initializes the statements count
  303. /// </summary>
  304. public void ResetStatementsCount()
  305. {
  306. _statementsCount = 0;
  307. }
  308. public void ResetMemoryUsage()
  309. {
  310. if (GetAllocatedBytesForCurrentThread != null)
  311. {
  312. _initialMemoryUsage = GetAllocatedBytesForCurrentThread();
  313. }
  314. }
  315. public void ResetTimeoutTicks()
  316. {
  317. var timeoutIntervalTicks = Options._TimeoutInterval.Ticks;
  318. _timeoutTicks = timeoutIntervalTicks > 0 ? DateTime.UtcNow.Ticks + timeoutIntervalTicks : 0;
  319. }
  320. /// <summary>
  321. /// Initializes list of references of called functions
  322. /// </summary>
  323. public void ResetCallStack()
  324. {
  325. CallStack.Clear();
  326. }
  327. public Engine Execute(string source)
  328. {
  329. return Execute(source, DefaultParserOptions);
  330. }
  331. public Engine Execute(string source, ParserOptions parserOptions)
  332. {
  333. var parser = new JavaScriptParser(source, parserOptions);
  334. return Execute(parser.ParseProgram());
  335. }
  336. public Engine Execute(Program program)
  337. {
  338. ResetStatementsCount();
  339. if (_memoryLimit > 0)
  340. {
  341. ResetMemoryUsage();
  342. }
  343. ResetTimeoutTicks();
  344. ResetLastStatement();
  345. ResetCallStack();
  346. using (new StrictModeScope(_isStrict || program.Strict))
  347. {
  348. DeclarationBindingInstantiation(
  349. DeclarationBindingType.GlobalCode,
  350. program.HoistingScope,
  351. functionInstance: null,
  352. arguments: null);
  353. var list = new JintStatementList(this, null, program.Body);
  354. var result = list.Execute();
  355. if (result.Type == CompletionType.Throw)
  356. {
  357. var ex = new JavaScriptException(result.GetValueOrDefault()).SetCallstack(this, result.Location);
  358. throw ex;
  359. }
  360. _completionValue = result.GetValueOrDefault();
  361. }
  362. return this;
  363. }
  364. private void ResetLastStatement()
  365. {
  366. _lastSyntaxNode = null;
  367. }
  368. /// <summary>
  369. /// Gets the last evaluated statement completion value
  370. /// </summary>
  371. public JsValue GetCompletionValue()
  372. {
  373. return _completionValue;
  374. }
  375. internal void RunBeforeExecuteStatementChecks(Statement statement)
  376. {
  377. if (_maxStatements > 0 && _statementsCount++ > _maxStatements)
  378. {
  379. ExceptionHelper.ThrowStatementsCountOverflowException();
  380. }
  381. if (_timeoutTicks > 0 && _timeoutTicks < DateTime.UtcNow.Ticks)
  382. {
  383. ExceptionHelper.ThrowTimeoutException();
  384. }
  385. if (_memoryLimit > 0)
  386. {
  387. if (GetAllocatedBytesForCurrentThread != null)
  388. {
  389. var memoryUsage = GetAllocatedBytesForCurrentThread() - _initialMemoryUsage;
  390. if (memoryUsage > _memoryLimit)
  391. {
  392. ExceptionHelper.ThrowMemoryLimitExceededException($"Script has allocated {memoryUsage} but is limited to {_memoryLimit}");
  393. }
  394. }
  395. else
  396. {
  397. ExceptionHelper.ThrowPlatformNotSupportedException("The current platform doesn't support MemoryLimit.");
  398. }
  399. }
  400. if (_isDebugMode)
  401. {
  402. DebugHandler.OnStep(statement);
  403. }
  404. }
  405. /// <summary>
  406. /// http://www.ecma-international.org/ecma-262/5.1/#sec-8.7.1
  407. /// </summary>
  408. public JsValue GetValue(object value)
  409. {
  410. return GetValue(value, false);
  411. }
  412. internal JsValue GetValue(object value, bool returnReferenceToPool)
  413. {
  414. if (value is JsValue jsValue)
  415. {
  416. return jsValue;
  417. }
  418. if (!(value is Reference reference))
  419. {
  420. return ((Completion) value).Value;
  421. }
  422. return GetValue(reference, returnReferenceToPool);
  423. }
  424. internal JsValue GetValue(Reference reference, bool returnReferenceToPool)
  425. {
  426. if (reference._baseValue._type == InternalTypes.Undefined)
  427. {
  428. if (_referenceResolver != null &&
  429. _referenceResolver.TryUnresolvableReference(this, reference, out JsValue val))
  430. {
  431. return val;
  432. }
  433. ExceptionHelper.ThrowReferenceError(this, reference);
  434. }
  435. var baseValue = reference._baseValue;
  436. if (_referenceResolver != null
  437. && reference._baseValue._type < InternalTypes.ObjectEnvironmentRecord
  438. && _referenceResolver.TryPropertyReference(this, reference, ref baseValue))
  439. {
  440. return baseValue;
  441. }
  442. if (reference.IsPropertyReference())
  443. {
  444. ref readonly var referencedName = ref reference.GetReferencedName();
  445. if (returnReferenceToPool)
  446. {
  447. _referencePool.Return(reference);
  448. }
  449. if (reference._baseValue._type == InternalTypes.Object)
  450. {
  451. var o = TypeConverter.ToObject(this, baseValue);
  452. var v = o.Get(referencedName);
  453. return v;
  454. }
  455. else
  456. {
  457. var o = TypeConverter.ToObject(this, baseValue);
  458. var desc = o.GetProperty(referencedName);
  459. if (desc == PropertyDescriptor.Undefined)
  460. {
  461. return JsValue.Undefined;
  462. }
  463. if (desc.IsDataDescriptor())
  464. {
  465. return desc.Value;
  466. }
  467. var getter = desc.Get;
  468. if (getter.IsUndefined())
  469. {
  470. return Undefined.Instance;
  471. }
  472. var callable = (ICallable) getter.AsObject();
  473. return callable.Call(baseValue, Arguments.Empty);
  474. }
  475. }
  476. if (!(baseValue is EnvironmentRecord record))
  477. {
  478. return ExceptionHelper.ThrowArgumentException<JsValue>();
  479. }
  480. var bindingValue = record.GetBindingValue(reference.GetReferencedName(), reference._strict);
  481. if (returnReferenceToPool)
  482. {
  483. _referencePool.Return(reference);
  484. }
  485. return bindingValue;
  486. }
  487. /// <summary>
  488. /// http://www.ecma-international.org/ecma-262/#sec-putvalue
  489. /// </summary>
  490. public void PutValue(Reference reference, JsValue value)
  491. {
  492. ref readonly var referencedName = ref reference.GetReferencedName();
  493. var baseValue = reference._baseValue;
  494. if (reference.IsUnresolvableReference())
  495. {
  496. if (reference._strict)
  497. {
  498. ExceptionHelper.ThrowReferenceError(this, reference);
  499. }
  500. Global.Set(referencedName, value);
  501. }
  502. else if (reference.IsPropertyReference())
  503. {
  504. if (reference.HasPrimitiveBase())
  505. {
  506. baseValue = TypeConverter.ToObject(this, reference._baseValue);
  507. }
  508. var thisValue = GetThisValue(reference);
  509. var succeeded = baseValue.Set(referencedName, value, thisValue);
  510. if (!succeeded && reference._strict)
  511. {
  512. ExceptionHelper.ThrowTypeError(this);
  513. }
  514. }
  515. else
  516. {
  517. ((EnvironmentRecord) baseValue).SetMutableBinding(referencedName, value, reference._strict);
  518. }
  519. }
  520. private static JsValue GetThisValue(Reference reference)
  521. {
  522. if (reference.IsSuperReference())
  523. {
  524. return ExceptionHelper.ThrowNotImplementedException<JsValue>();
  525. }
  526. return reference._baseValue;
  527. }
  528. /// <summary>
  529. /// Invoke the current value as function.
  530. /// </summary>
  531. /// <param name="propertyName">The name of 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(string propertyName, params object[] arguments)
  535. {
  536. return Invoke(propertyName, null, arguments);
  537. }
  538. /// <summary>
  539. /// Invoke the current value as function.
  540. /// </summary>
  541. /// <param name="propertyName">The name of 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(string propertyName, object thisObj, object[] arguments)
  546. {
  547. var value = GetValue(propertyName);
  548. return Invoke(value, thisObj, arguments);
  549. }
  550. /// <summary>
  551. /// Invoke the current value as function.
  552. /// </summary>
  553. /// <param name="value">The function to call.</param>
  554. /// <param name="arguments">The arguments of the function call.</param>
  555. /// <returns>The value returned by the function call.</returns>
  556. public JsValue Invoke(JsValue value, params object[] arguments)
  557. {
  558. return Invoke(value, null, arguments);
  559. }
  560. /// <summary>
  561. /// Invoke the current value as function.
  562. /// </summary>
  563. /// <param name="value">The function to call.</param>
  564. /// <param name="thisObj">The this value inside the function call.</param>
  565. /// <param name="arguments">The arguments of the function call.</param>
  566. /// <returns>The value returned by the function call.</returns>
  567. public JsValue Invoke(JsValue value, object thisObj, object[] arguments)
  568. {
  569. var callable = value as ICallable ?? ExceptionHelper.ThrowArgumentException<ICallable>("Can only invoke functions");
  570. var items = _jsValueArrayPool.RentArray(arguments.Length);
  571. for (int i = 0; i < arguments.Length; ++i)
  572. {
  573. items[i] = JsValue.FromObject(this, arguments[i]);
  574. }
  575. var result = callable.Call(JsValue.FromObject(this, thisObj), items);
  576. _jsValueArrayPool.ReturnArray(items);
  577. return result;
  578. }
  579. /// <summary>
  580. /// Gets a named value from the Global scope.
  581. /// </summary>
  582. /// <param name="propertyName">The name of the property to return.</param>
  583. public JsValue GetValue(string propertyName)
  584. {
  585. return GetValue(Global, propertyName);
  586. }
  587. /// <summary>
  588. /// Gets the last evaluated <see cref="INode"/>.
  589. /// </summary>
  590. public INode GetLastSyntaxNode()
  591. {
  592. return _lastSyntaxNode;
  593. }
  594. /// <summary>
  595. /// Gets a named value from the specified scope.
  596. /// </summary>
  597. /// <param name="scope">The scope to get the property from.</param>
  598. /// <param name="propertyName">The name of the property to return.</param>
  599. public JsValue GetValue(JsValue scope, in Key propertyName)
  600. {
  601. AssertNotNullOrEmpty(nameof(propertyName), propertyName);
  602. var reference = _referencePool.Rent(scope, propertyName, _isStrict);
  603. var jsValue = GetValue(reference, false);
  604. _referencePool.Return(reference);
  605. return jsValue;
  606. }
  607. // http://www.ecma-international.org/ecma-262/5.1/#sec-10.5
  608. internal ArgumentsInstance DeclarationBindingInstantiation(
  609. DeclarationBindingType declarationBindingType,
  610. HoistingScope hoistingScope,
  611. FunctionInstance functionInstance,
  612. JsValue[] arguments)
  613. {
  614. var env = ExecutionContext.VariableEnvironment._record;
  615. bool configurableBindings = declarationBindingType == DeclarationBindingType.EvalCode;
  616. var strict = StrictModeScope.IsStrictModeCode;
  617. ArgumentsInstance argsObj = null;
  618. var der = env as DeclarativeEnvironmentRecord;
  619. if (declarationBindingType == DeclarationBindingType.FunctionCode)
  620. {
  621. // arrow functions don't needs arguments
  622. var arrowFunctionInstance = functionInstance as ArrowFunctionInstance;
  623. argsObj = arrowFunctionInstance is null
  624. ? _argumentsInstancePool.Rent(functionInstance, functionInstance._formalParameters, arguments, env, strict)
  625. : null;
  626. var functionDeclaration = (functionInstance as ScriptFunctionInstance)?.FunctionDeclaration ??
  627. arrowFunctionInstance?.FunctionDeclaration;
  628. if (!ReferenceEquals(der, null))
  629. {
  630. der.AddFunctionParameters(arguments, argsObj, functionDeclaration);
  631. }
  632. else
  633. {
  634. // TODO: match functionality with DeclarationEnvironmentRecord.AddFunctionParameters here
  635. // slow path
  636. var parameters = functionInstance._formalParameters;
  637. for (uint i = 0; i < (uint) parameters.Length; i++)
  638. {
  639. Key argName = parameters[i];
  640. var v = i + 1 > arguments.Length ? Undefined.Instance : arguments[i];
  641. v = DeclarativeEnvironmentRecord.HandleAssignmentPatternIfNeeded(functionDeclaration, v, i);
  642. var argAlreadyDeclared = env.HasBinding(argName);
  643. if (!argAlreadyDeclared)
  644. {
  645. env.CreateMutableBinding(argName, v);
  646. }
  647. env.SetMutableBinding(argName, v, strict);
  648. }
  649. env.CreateMutableBinding(KnownKeys.Arguments, argsObj);
  650. }
  651. }
  652. var functionDeclarations = hoistingScope.FunctionDeclarations;
  653. if (functionDeclarations.Count > 0)
  654. {
  655. AddFunctionDeclarations(ref functionDeclarations, env, configurableBindings, strict);
  656. }
  657. var variableDeclarations = hoistingScope.VariableDeclarations;
  658. if (variableDeclarations.Count == 0)
  659. {
  660. return argsObj;
  661. }
  662. // process all variable declarations in the current parser scope
  663. if (!ReferenceEquals(der, null))
  664. {
  665. der.AddVariableDeclarations(ref variableDeclarations);
  666. }
  667. else
  668. {
  669. // slow path
  670. var variableDeclarationsCount = variableDeclarations.Count;
  671. for (var i = 0; i < variableDeclarationsCount; i++)
  672. {
  673. var variableDeclaration = variableDeclarations[i];
  674. var declarations = variableDeclaration.Declarations;
  675. var declarationsCount = declarations.Count;
  676. for (var j = 0; j < declarationsCount; j++)
  677. {
  678. var d = declarations[j];
  679. if (d.Id is Identifier id1)
  680. {
  681. Key name = id1.Name;
  682. var varAlreadyDeclared = env.HasBinding(name);
  683. if (!varAlreadyDeclared)
  684. {
  685. env.CreateMutableBinding(name, Undefined.Instance);
  686. }
  687. }
  688. }
  689. }
  690. }
  691. return argsObj;
  692. }
  693. private void AddFunctionDeclarations(
  694. ref NodeList<FunctionDeclaration> functionDeclarations,
  695. EnvironmentRecord env,
  696. bool configurableBindings,
  697. bool strict)
  698. {
  699. var functionDeclarationsCount = functionDeclarations.Count;
  700. for (var i = 0; i < functionDeclarationsCount; i++)
  701. {
  702. var f = functionDeclarations[i];
  703. Key fn = f.Id.Name;
  704. var fo = Function.CreateFunctionObject(f);
  705. var funcAlreadyDeclared = env.HasBinding(fn);
  706. if (!funcAlreadyDeclared)
  707. {
  708. env.CreateMutableBinding(fn, configurableBindings);
  709. }
  710. else
  711. {
  712. if (ReferenceEquals(env, GlobalEnvironment._record))
  713. {
  714. var go = Global;
  715. var existingProp = go.GetProperty(fn);
  716. if (existingProp.Configurable)
  717. {
  718. var flags = PropertyFlag.Writable | PropertyFlag.Enumerable;
  719. if (configurableBindings)
  720. {
  721. flags |= PropertyFlag.Configurable;
  722. }
  723. var descriptor = new PropertyDescriptor(Undefined.Instance, flags);
  724. go.DefinePropertyOrThrow(fn, descriptor);
  725. }
  726. else
  727. {
  728. if (existingProp.IsAccessorDescriptor() || !existingProp.Enumerable)
  729. {
  730. ExceptionHelper.ThrowTypeError(this);
  731. }
  732. }
  733. }
  734. }
  735. env.SetMutableBinding(fn, fo, strict);
  736. }
  737. }
  738. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  739. internal void UpdateLexicalEnvironment(LexicalEnvironment newEnv)
  740. {
  741. _executionContexts.ReplaceTopLexicalEnvironment(newEnv);
  742. }
  743. private static void AssertNotNullOrEmpty(string propertyName, string propertyValue)
  744. {
  745. if (string.IsNullOrEmpty(propertyValue))
  746. {
  747. ExceptionHelper.ThrowArgumentException(propertyName);
  748. }
  749. }
  750. }
  751. }