FunctionInstance.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Runtime.CompilerServices;
  4. using Esprima.Ast;
  5. using Jint.Native.Object;
  6. using Jint.Native.Proxy;
  7. using Jint.Runtime;
  8. using Jint.Runtime.Descriptors;
  9. using Jint.Runtime.Environments;
  10. using Jint.Runtime.Interpreter;
  11. namespace Jint.Native.Function
  12. {
  13. public abstract class FunctionInstance : ObjectInstance, ICallable
  14. {
  15. protected PropertyDescriptor _prototypeDescriptor;
  16. protected internal PropertyDescriptor _length;
  17. internal PropertyDescriptor _nameDescriptor;
  18. protected internal EnvironmentRecord _environment;
  19. internal readonly JintFunctionDefinition _functionDefinition;
  20. internal readonly FunctionThisMode _thisMode;
  21. internal JsValue _homeObject = Undefined;
  22. internal ConstructorKind _constructorKind = ConstructorKind.Base;
  23. internal Realm _realm;
  24. private PrivateEnvironmentRecord _privateEnvironment;
  25. protected FunctionInstance(
  26. Engine engine,
  27. Realm realm,
  28. JsString name)
  29. : this(engine, realm, name, FunctionThisMode.Global, ObjectClass.Function)
  30. {
  31. }
  32. internal FunctionInstance(
  33. Engine engine,
  34. Realm realm,
  35. JintFunctionDefinition function,
  36. EnvironmentRecord scope,
  37. FunctionThisMode thisMode)
  38. : this(
  39. engine,
  40. realm,
  41. !string.IsNullOrWhiteSpace(function.Name) ? new JsString(function.Name) : null,
  42. thisMode)
  43. {
  44. _functionDefinition = function;
  45. _environment = scope;
  46. }
  47. internal FunctionInstance(
  48. Engine engine,
  49. Realm realm,
  50. JsString name,
  51. FunctionThisMode thisMode = FunctionThisMode.Global,
  52. ObjectClass objectClass = ObjectClass.Function)
  53. : base(engine, objectClass)
  54. {
  55. if (name is not null)
  56. {
  57. _nameDescriptor = new PropertyDescriptor(name, PropertyFlag.Configurable);
  58. }
  59. _realm = realm;
  60. _thisMode = thisMode;
  61. }
  62. // for example RavenDB wants to inspect this
  63. public IFunction FunctionDeclaration => _functionDefinition.Function;
  64. /// <summary>
  65. /// Executed when a function object is used as a function
  66. /// </summary>
  67. /// <param name="thisObject"></param>
  68. /// <param name="arguments"></param>
  69. /// <returns></returns>
  70. public abstract JsValue Call(JsValue thisObject, JsValue[] arguments);
  71. public bool Strict => _thisMode == FunctionThisMode.Strict;
  72. internal override bool IsConstructor => this is IConstructor;
  73. public override IEnumerable<KeyValuePair<JsValue, PropertyDescriptor>> GetOwnProperties()
  74. {
  75. if (_prototypeDescriptor != null)
  76. {
  77. yield return new KeyValuePair<JsValue, PropertyDescriptor>(CommonProperties.Prototype, _prototypeDescriptor);
  78. }
  79. if (_length != null)
  80. {
  81. yield return new KeyValuePair<JsValue, PropertyDescriptor>(CommonProperties.Length, _length);
  82. }
  83. if (_nameDescriptor != null)
  84. {
  85. yield return new KeyValuePair<JsValue, PropertyDescriptor>(CommonProperties.Name, GetOwnProperty(CommonProperties.Name));
  86. }
  87. foreach (var entry in base.GetOwnProperties())
  88. {
  89. yield return entry;
  90. }
  91. }
  92. internal override IEnumerable<JsValue> GetInitialOwnStringPropertyKeys()
  93. {
  94. if (_length != null)
  95. {
  96. yield return CommonProperties.Length;
  97. }
  98. if (_nameDescriptor != null)
  99. {
  100. yield return CommonProperties.Name;
  101. }
  102. if (_prototypeDescriptor != null)
  103. {
  104. yield return CommonProperties.Prototype;
  105. }
  106. }
  107. public override PropertyDescriptor GetOwnProperty(JsValue property)
  108. {
  109. if (property == CommonProperties.Prototype)
  110. {
  111. return _prototypeDescriptor ?? PropertyDescriptor.Undefined;
  112. }
  113. if (property == CommonProperties.Length)
  114. {
  115. return _length ?? PropertyDescriptor.Undefined;
  116. }
  117. if (property == CommonProperties.Name)
  118. {
  119. return _nameDescriptor ?? PropertyDescriptor.Undefined;
  120. }
  121. return base.GetOwnProperty(property);
  122. }
  123. protected internal override void SetOwnProperty(JsValue property, PropertyDescriptor desc)
  124. {
  125. if (property == CommonProperties.Prototype)
  126. {
  127. _prototypeDescriptor = desc;
  128. }
  129. else if (property == CommonProperties.Length)
  130. {
  131. _length = desc;
  132. }
  133. else if (property == CommonProperties.Name)
  134. {
  135. _nameDescriptor = desc;
  136. }
  137. else
  138. {
  139. base.SetOwnProperty(property, desc);
  140. }
  141. }
  142. public override bool HasOwnProperty(JsValue property)
  143. {
  144. if (property == CommonProperties.Prototype)
  145. {
  146. return _prototypeDescriptor != null;
  147. }
  148. if (property == CommonProperties.Length)
  149. {
  150. return _length != null;
  151. }
  152. if (property == CommonProperties.Name)
  153. {
  154. return _nameDescriptor != null;
  155. }
  156. return base.HasOwnProperty(property);
  157. }
  158. public override void RemoveOwnProperty(JsValue property)
  159. {
  160. if (property == CommonProperties.Prototype)
  161. {
  162. _prototypeDescriptor = null;
  163. }
  164. if (property == CommonProperties.Length)
  165. {
  166. _length = null;
  167. }
  168. if (property == CommonProperties.Name)
  169. {
  170. _nameDescriptor = null;
  171. }
  172. base.RemoveOwnProperty(property);
  173. }
  174. internal void SetFunctionName(JsValue name, string prefix = null, bool force = false)
  175. {
  176. if (!force && _nameDescriptor != null && UnwrapJsValue(_nameDescriptor) != JsString.Empty)
  177. {
  178. return;
  179. }
  180. if (name is JsSymbol symbol)
  181. {
  182. name = symbol._value.IsUndefined()
  183. ? JsString.Empty
  184. : new JsString("[" + symbol._value + "]");
  185. }
  186. if (!string.IsNullOrWhiteSpace(prefix))
  187. {
  188. name = prefix + " " + name;
  189. }
  190. _nameDescriptor = new PropertyDescriptor(name, PropertyFlag.Configurable);
  191. }
  192. /// <summary>
  193. /// https://tc39.es/ecma262/#sec-ordinarycreatefromconstructor
  194. /// </summary>
  195. /// <remarks>
  196. /// Uses separate builder to get correct type with state support to prevent allocations.
  197. /// In spec intrinsicDefaultProto is string pointing to intrinsic, but we do a selector.
  198. /// </remarks>
  199. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  200. internal T OrdinaryCreateFromConstructor<T>(
  201. JsValue constructor,
  202. Func<Intrinsics, ObjectInstance> intrinsicDefaultProto,
  203. Func<Engine, Realm, JsValue, T> objectCreator,
  204. JsValue state = null) where T : ObjectInstance
  205. {
  206. var proto = GetPrototypeFromConstructor(constructor, intrinsicDefaultProto);
  207. var obj = objectCreator(_engine, _realm, state);
  208. obj._prototype = proto;
  209. return obj;
  210. }
  211. /// <summary>
  212. /// https://tc39.es/ecma262/#sec-getprototypefromconstructor
  213. /// </summary>
  214. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  215. internal ObjectInstance GetPrototypeFromConstructor(JsValue constructor, Func<Intrinsics, ObjectInstance> intrinsicDefaultProto)
  216. {
  217. var proto = constructor.Get(CommonProperties.Prototype, constructor) as ObjectInstance;
  218. if (proto is null)
  219. {
  220. var realm = GetFunctionRealm(constructor);
  221. proto = intrinsicDefaultProto(realm.Intrinsics);
  222. }
  223. return proto;
  224. }
  225. /// <summary>
  226. /// https://tc39.es/ecma262/#sec-getfunctionrealm
  227. /// </summary>
  228. internal Realm GetFunctionRealm(JsValue obj)
  229. {
  230. if (obj is FunctionInstance functionInstance && functionInstance._realm is not null)
  231. {
  232. return functionInstance._realm;
  233. }
  234. if (obj is BindFunctionInstance bindFunctionInstance)
  235. {
  236. return GetFunctionRealm(bindFunctionInstance.BoundTargetFunction);
  237. }
  238. if (obj is ProxyInstance proxyInstance)
  239. {
  240. if (proxyInstance._handler is null)
  241. {
  242. ExceptionHelper.ThrowTypeErrorNoEngine();
  243. }
  244. return GetFunctionRealm(proxyInstance._target);
  245. }
  246. return _engine.ExecutionContext.Realm;
  247. }
  248. internal void MakeMethod(ObjectInstance homeObject)
  249. {
  250. _homeObject = homeObject;
  251. }
  252. /// <summary>
  253. /// https://tc39.es/ecma262/#sec-ordinarycallbindthis
  254. /// </summary>
  255. internal void OrdinaryCallBindThis(ExecutionContext calleeContext, JsValue thisArgument)
  256. {
  257. var thisMode = _thisMode;
  258. if (thisMode == FunctionThisMode.Lexical)
  259. {
  260. return;
  261. }
  262. var calleeRealm = _realm;
  263. var localEnv = (FunctionEnvironmentRecord) calleeContext.LexicalEnvironment;
  264. JsValue thisValue;
  265. if (_thisMode == FunctionThisMode.Strict)
  266. {
  267. thisValue = thisArgument;
  268. }
  269. else
  270. {
  271. if (thisArgument is null || thisArgument.IsNullOrUndefined())
  272. {
  273. var globalEnv = calleeRealm.GlobalEnv;
  274. thisValue = globalEnv.GlobalThisValue;
  275. }
  276. else
  277. {
  278. thisValue = TypeConverter.ToObject(calleeRealm, thisArgument);
  279. }
  280. }
  281. localEnv.BindThisValue(thisValue);
  282. }
  283. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  284. internal Completion OrdinaryCallEvaluateBody(
  285. EvaluationContext context,
  286. JsValue[] arguments,
  287. ExecutionContext calleeContext)
  288. {
  289. var argumentsInstance = _engine.FunctionDeclarationInstantiation(
  290. functionInstance: this,
  291. arguments);
  292. var result = _functionDefinition.Execute(context);
  293. argumentsInstance?.FunctionWasCalled();
  294. return result;
  295. }
  296. /// <summary>
  297. /// https://tc39.es/ecma262/#sec-prepareforordinarycall
  298. /// </summary>
  299. internal ExecutionContext PrepareForOrdinaryCall(JsValue newTarget)
  300. {
  301. var callerContext = _engine.ExecutionContext;
  302. var localEnv = JintEnvironment.NewFunctionEnvironment(_engine, this, newTarget);
  303. var calleeRealm = _realm;
  304. var calleeContext = new ExecutionContext(
  305. null,
  306. localEnv,
  307. localEnv,
  308. _privateEnvironment,
  309. calleeRealm,
  310. this);
  311. // If callerContext is not already suspended, suspend callerContext.
  312. // Push calleeContext onto the execution context stack; calleeContext is now the running execution context.
  313. // NOTE: Any exception objects produced after this point are associated with calleeRealm.
  314. // Return calleeContext.
  315. return _engine.EnterExecutionContext(calleeContext);
  316. }
  317. public override string ToString()
  318. {
  319. // TODO no way to extract SourceText from Esprima at the moment, just returning native code
  320. var nameValue = _nameDescriptor != null ? UnwrapJsValue(_nameDescriptor) : JsString.Empty;
  321. var name = "";
  322. if (!nameValue.IsUndefined())
  323. {
  324. name = TypeConverter.ToString(nameValue);
  325. }
  326. return "function " + name + "() { [native code] }";
  327. }
  328. }
  329. }