2
0

Engine.cs 34 KB

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