Function.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. using System.Diagnostics;
  2. using System.Runtime.CompilerServices;
  3. using Jint.Native.Object;
  4. using Jint.Runtime;
  5. using Jint.Runtime.Descriptors;
  6. using Jint.Runtime.Environments;
  7. using Jint.Runtime.Interpreter;
  8. using Environment = Jint.Runtime.Environments.Environment;
  9. namespace Jint.Native.Function;
  10. [DebuggerDisplay("{ToString(),nq}")]
  11. #pragma warning disable MA0049
  12. public abstract partial class Function : ObjectInstance, ICallable
  13. #pragma warning restore MA0049
  14. {
  15. protected PropertyDescriptor? _prototypeDescriptor;
  16. protected internal PropertyDescriptor? _length;
  17. internal PropertyDescriptor? _nameDescriptor;
  18. internal Environment? _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. internal PrivateEnvironment? _privateEnvironment;
  25. private readonly IScriptOrModule? _scriptOrModule;
  26. protected Function(
  27. Engine engine,
  28. Realm realm,
  29. JsString? name)
  30. : this(engine, realm, name, FunctionThisMode.Global)
  31. {
  32. }
  33. internal Function(
  34. Engine engine,
  35. Realm realm,
  36. JintFunctionDefinition function,
  37. Environment env,
  38. FunctionThisMode thisMode)
  39. : this(
  40. engine,
  41. realm,
  42. !string.IsNullOrWhiteSpace(function.Name) ? new JsString(function.Name!) : null,
  43. thisMode)
  44. {
  45. _functionDefinition = function;
  46. _environment = env;
  47. }
  48. internal Function(
  49. Engine engine,
  50. Realm realm,
  51. JsString? name,
  52. FunctionThisMode thisMode = FunctionThisMode.Global)
  53. : base(engine, ObjectClass.Function)
  54. {
  55. if (name is not null)
  56. {
  57. _nameDescriptor = new PropertyDescriptor(name, PropertyFlag.Configurable);
  58. }
  59. _realm = realm;
  60. _thisMode = thisMode;
  61. _scriptOrModule = _engine.GetActiveScriptOrModule();
  62. }
  63. // for example RavenDB wants to inspect this
  64. public IFunction? FunctionDeclaration => _functionDefinition?.Function;
  65. internal override bool IsCallable => true;
  66. JsValue ICallable.Call(JsValue thisObject, params JsCallArguments arguments) => Call(thisObject, arguments);
  67. /// <summary>
  68. /// Executed when a function object is used as a function
  69. /// </summary>
  70. protected internal abstract JsValue Call(JsValue thisObject, JsCallArguments 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 sealed 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 (CommonProperties.Prototype.Equals(property))
  110. {
  111. return _prototypeDescriptor ?? PropertyDescriptor.Undefined;
  112. }
  113. if (CommonProperties.Length.Equals(property))
  114. {
  115. return _length ?? PropertyDescriptor.Undefined;
  116. }
  117. if (CommonProperties.Name.Equals(property))
  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 (CommonProperties.Prototype.Equals(property))
  126. {
  127. _prototypeDescriptor = desc;
  128. }
  129. else if (CommonProperties.Length.Equals(property))
  130. {
  131. _length = desc;
  132. }
  133. else if (CommonProperties.Name.Equals(property))
  134. {
  135. _nameDescriptor = desc;
  136. }
  137. else
  138. {
  139. base.SetOwnProperty(property, desc);
  140. }
  141. }
  142. public override void RemoveOwnProperty(JsValue property)
  143. {
  144. if (CommonProperties.Prototype.Equals(property))
  145. {
  146. _prototypeDescriptor = null;
  147. }
  148. if (CommonProperties.Length.Equals(property))
  149. {
  150. _length = null;
  151. }
  152. if (CommonProperties.Name.Equals(property))
  153. {
  154. _nameDescriptor = null;
  155. }
  156. base.RemoveOwnProperty(property);
  157. }
  158. /// <summary>
  159. /// https://tc39.es/ecma262/#sec-setfunctionname
  160. /// </summary>
  161. internal void SetFunctionName(JsValue name, string? prefix = null, bool force = false)
  162. {
  163. if (!force && _nameDescriptor != null && UnwrapJsValue(_nameDescriptor) != JsString.Empty)
  164. {
  165. return;
  166. }
  167. if (name is JsSymbol symbol)
  168. {
  169. name = symbol._value.IsUndefined()
  170. ? JsString.Empty
  171. : new JsString("[" + symbol._value + "]");
  172. }
  173. else if (name is PrivateName privateName)
  174. {
  175. name = "#" + privateName.Description;
  176. }
  177. if (!string.IsNullOrWhiteSpace(prefix))
  178. {
  179. name = prefix + " " + name;
  180. }
  181. _nameDescriptor = new PropertyDescriptor(name, PropertyFlag.Configurable);
  182. }
  183. /// <summary>
  184. /// https://tc39.es/ecma262/#sec-ordinarycreatefromconstructor
  185. /// </summary>
  186. /// <remarks>
  187. /// Uses separate builder to get correct type with state support to prevent allocations.
  188. /// In spec intrinsicDefaultProto is string pointing to intrinsic, but we do a selector.
  189. /// </remarks>
  190. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  191. internal T OrdinaryCreateFromConstructor<T, TState>(
  192. JsValue constructor,
  193. Func<Intrinsics, ObjectInstance> intrinsicDefaultProto,
  194. Func<Engine, Realm, TState?, T> objectCreator,
  195. TState? state = default) where T : ObjectInstance
  196. {
  197. var proto = GetPrototypeFromConstructor(constructor, intrinsicDefaultProto);
  198. var obj = objectCreator(_engine, _realm, state);
  199. obj._prototype = proto;
  200. return obj;
  201. }
  202. /// <summary>
  203. /// https://tc39.es/ecma262/#sec-getprototypefromconstructor
  204. /// </summary>
  205. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  206. internal ObjectInstance GetPrototypeFromConstructor(JsValue constructor, Func<Intrinsics, ObjectInstance> intrinsicDefaultProto)
  207. {
  208. if (constructor.Get(CommonProperties.Prototype) is not ObjectInstance proto)
  209. {
  210. var realm = GetFunctionRealm(constructor);
  211. proto = intrinsicDefaultProto(realm.Intrinsics);
  212. }
  213. return proto;
  214. }
  215. /// <summary>
  216. /// https://tc39.es/ecma262/#sec-getfunctionrealm
  217. /// </summary>
  218. internal Realm GetFunctionRealm(JsValue obj)
  219. {
  220. if (obj is Function functionInstance && functionInstance._realm is not null)
  221. {
  222. return functionInstance._realm;
  223. }
  224. if (obj is BindFunction bindFunctionInstance)
  225. {
  226. return GetFunctionRealm(bindFunctionInstance.BoundTargetFunction);
  227. }
  228. if (obj is JsProxy proxyInstance)
  229. {
  230. if (proxyInstance._handler is null)
  231. {
  232. Throw.TypeErrorNoEngine();
  233. }
  234. return GetFunctionRealm(proxyInstance._target);
  235. }
  236. return _engine.ExecutionContext.Realm;
  237. }
  238. /// <summary>
  239. /// https://tc39.es/ecma262/#sec-makemethod
  240. /// </summary>
  241. internal void MakeMethod(ObjectInstance homeObject)
  242. {
  243. _homeObject = homeObject;
  244. }
  245. /// <summary>
  246. /// https://tc39.es/ecma262/#sec-ordinarycallbindthis
  247. /// </summary>
  248. internal void OrdinaryCallBindThis(in ExecutionContext calleeContext, JsValue thisArgument)
  249. {
  250. if (_thisMode == FunctionThisMode.Lexical)
  251. {
  252. return;
  253. }
  254. var calleeRealm = _realm;
  255. var localEnv = (FunctionEnvironment) calleeContext.LexicalEnvironment;
  256. JsValue thisValue;
  257. if (_thisMode == FunctionThisMode.Strict)
  258. {
  259. thisValue = thisArgument;
  260. }
  261. else
  262. {
  263. if (thisArgument is null || thisArgument.IsNullOrUndefined())
  264. {
  265. var globalEnv = calleeRealm.GlobalEnv;
  266. thisValue = globalEnv.GlobalThisValue;
  267. }
  268. else
  269. {
  270. thisValue = TypeConverter.ToObject(calleeRealm, thisArgument);
  271. }
  272. }
  273. localEnv.BindThisValue(thisValue);
  274. }
  275. /// <summary>
  276. /// https://tc39.es/ecma262/#sec-prepareforordinarycall
  277. /// </summary>
  278. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  279. internal ref readonly ExecutionContext PrepareForOrdinaryCall(JsValue newTarget)
  280. {
  281. var callerContext = _engine.ExecutionContext;
  282. var localEnv = JintEnvironment.NewFunctionEnvironment(_engine, this, newTarget);
  283. var calleeRealm = _realm;
  284. var calleeContext = new ExecutionContext(
  285. _scriptOrModule,
  286. lexicalEnvironment: localEnv,
  287. variableEnvironment: localEnv,
  288. _privateEnvironment,
  289. calleeRealm,
  290. generator: null,
  291. function: this);
  292. // If callerContext is not already suspended, suspend callerContext.
  293. // Push calleeContext onto the execution context stack; calleeContext is now the running execution context.
  294. // NOTE: Any exception objects produced after this point are associated with calleeRealm.
  295. // Return calleeContext.
  296. _engine.EnterExecutionContext(calleeContext);
  297. return ref _engine.ExecutionContext;
  298. }
  299. internal void MakeConstructor(bool writableProperty = true, ObjectInstance? prototype = null)
  300. {
  301. _constructorKind = ConstructorKind.Base;
  302. if (prototype is null)
  303. {
  304. prototype = new ObjectInstanceWithConstructor(_engine, this)
  305. {
  306. _prototype = _realm.Intrinsics.Object.PrototypeObject
  307. };
  308. }
  309. _prototypeDescriptor = new PropertyDescriptor(prototype, writableProperty, enumerable: false, configurable: false);
  310. }
  311. internal void SetFunctionLength(JsNumber length)
  312. {
  313. DefinePropertyOrThrow(CommonProperties.Length, new PropertyDescriptor(length, writable: false, enumerable: false, configurable: true));
  314. }
  315. // native syntax doesn't expect to have private identifier indicator
  316. private static readonly char[] _functionNameTrimStartChars = ['#'];
  317. public sealed override object ToObject()
  318. {
  319. return (JsCallDelegate) Call;
  320. }
  321. public override string ToString()
  322. {
  323. if (_functionDefinition?.Function is Node node && _engine.Options.Host.FunctionToStringHandler(this, node) is { } s)
  324. {
  325. return s;
  326. }
  327. var nameValue = _nameDescriptor != null ? UnwrapJsValue(_nameDescriptor) : JsString.Empty;
  328. var name = "";
  329. if (!nameValue.IsUndefined())
  330. {
  331. name = TypeConverter.ToString(nameValue);
  332. }
  333. name = name.TrimStart(_functionNameTrimStartChars);
  334. return $"function {name}() {{ [native code] }}";
  335. }
  336. private sealed class ObjectInstanceWithConstructor : ObjectInstance
  337. {
  338. private PropertyDescriptor? _constructor;
  339. public ObjectInstanceWithConstructor(Engine engine, ObjectInstance thisObj) : base(engine)
  340. {
  341. _constructor = new PropertyDescriptor(thisObj, PropertyFlag.NonEnumerable);
  342. }
  343. public override IEnumerable<KeyValuePair<JsValue, PropertyDescriptor>> GetOwnProperties()
  344. {
  345. if (_constructor != null)
  346. {
  347. yield return new KeyValuePair<JsValue, PropertyDescriptor>(CommonProperties.Constructor, _constructor);
  348. }
  349. foreach (var entry in base.GetOwnProperties())
  350. {
  351. yield return entry;
  352. }
  353. }
  354. public override PropertyDescriptor GetOwnProperty(JsValue property)
  355. {
  356. if (CommonProperties.Constructor.Equals(property))
  357. {
  358. return _constructor ?? PropertyDescriptor.Undefined;
  359. }
  360. return base.GetOwnProperty(property);
  361. }
  362. protected internal override void SetOwnProperty(JsValue property, PropertyDescriptor desc)
  363. {
  364. if (CommonProperties.Constructor.Equals(property))
  365. {
  366. _constructor = desc;
  367. }
  368. else
  369. {
  370. base.SetOwnProperty(property, desc);
  371. }
  372. }
  373. public override void RemoveOwnProperty(JsValue property)
  374. {
  375. if (CommonProperties.Constructor.Equals(property))
  376. {
  377. _constructor = null;
  378. }
  379. else
  380. {
  381. base.RemoveOwnProperty(property);
  382. }
  383. }
  384. }
  385. }