FunctionInstance.cs 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. using System.Collections.Generic;
  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. namespace Jint.Native.Function
  9. {
  10. public abstract class FunctionInstance : ObjectInstance, ICallable
  11. {
  12. internal enum FunctionThisMode
  13. {
  14. Lexical,
  15. Strict,
  16. Global
  17. }
  18. protected internal PropertyDescriptor _prototypeDescriptor;
  19. protected internal PropertyDescriptor _length;
  20. internal PropertyDescriptor _nameDescriptor;
  21. protected internal LexicalEnvironment _environment;
  22. internal readonly JintFunctionDefinition _functionDefinition;
  23. internal readonly FunctionThisMode _thisMode;
  24. internal FunctionInstance(
  25. Engine engine,
  26. JintFunctionDefinition function,
  27. LexicalEnvironment scope,
  28. FunctionThisMode thisMode)
  29. : this(engine, !string.IsNullOrWhiteSpace(function.Name) ? new JsString(function.Name) : null, thisMode)
  30. {
  31. _functionDefinition = function;
  32. _environment = scope;
  33. }
  34. internal FunctionInstance(
  35. Engine engine,
  36. JsString name,
  37. FunctionThisMode thisMode = FunctionThisMode.Global,
  38. ObjectClass objectClass = ObjectClass.Function)
  39. : base(engine, objectClass)
  40. {
  41. if (!(name is null))
  42. {
  43. _nameDescriptor = new PropertyDescriptor(name, PropertyFlag.Configurable);
  44. }
  45. _thisMode = thisMode;
  46. }
  47. protected FunctionInstance(
  48. Engine engine,
  49. JsString name)
  50. : this(engine, name, FunctionThisMode.Global, ObjectClass.Function)
  51. {
  52. }
  53. /// <summary>
  54. /// Executed when a function object is used as a function
  55. /// </summary>
  56. /// <param name="thisObject"></param>
  57. /// <param name="arguments"></param>
  58. /// <returns></returns>
  59. public abstract JsValue Call(JsValue thisObject, JsValue[] arguments);
  60. public bool Strict => _thisMode == FunctionThisMode.Strict;
  61. public virtual bool HasInstance(JsValue v)
  62. {
  63. if (!(v is ObjectInstance o))
  64. {
  65. return false;
  66. }
  67. var p = Get(CommonProperties.Prototype);
  68. if (!(p is ObjectInstance prototype))
  69. {
  70. ExceptionHelper.ThrowTypeError(_engine, $"Function has non-object prototype '{TypeConverter.ToString(p)}' in instanceof check");
  71. }
  72. while (true)
  73. {
  74. o = o.Prototype;
  75. if (o is null)
  76. {
  77. return false;
  78. }
  79. if (SameValue(p, o))
  80. {
  81. return true;
  82. }
  83. }
  84. }
  85. public override IEnumerable<KeyValuePair<JsValue, PropertyDescriptor>> GetOwnProperties()
  86. {
  87. if (_prototypeDescriptor != null)
  88. {
  89. yield return new KeyValuePair<JsValue, PropertyDescriptor>(CommonProperties.Prototype, _prototypeDescriptor);
  90. }
  91. if (_length != null)
  92. {
  93. yield return new KeyValuePair<JsValue, PropertyDescriptor>(CommonProperties.Length, _length);
  94. }
  95. if (_nameDescriptor != null)
  96. {
  97. yield return new KeyValuePair<JsValue, PropertyDescriptor>(CommonProperties.Name, GetOwnProperty(CommonProperties.Name));
  98. }
  99. foreach (var entry in base.GetOwnProperties())
  100. {
  101. yield return entry;
  102. }
  103. }
  104. public override List<JsValue> GetOwnPropertyKeys(Types types)
  105. {
  106. var keys = new List<JsValue>();
  107. if (_prototypeDescriptor != null)
  108. {
  109. keys.Add(CommonProperties.Prototype);
  110. }
  111. if (_length != null)
  112. {
  113. keys.Add(CommonProperties.Length);
  114. }
  115. if (_nameDescriptor != null)
  116. {
  117. keys.Add(CommonProperties.Name);
  118. }
  119. keys.AddRange(base.GetOwnPropertyKeys(types));
  120. return keys;
  121. }
  122. public override PropertyDescriptor GetOwnProperty(JsValue property)
  123. {
  124. if (property == CommonProperties.Prototype)
  125. {
  126. return _prototypeDescriptor ?? PropertyDescriptor.Undefined;
  127. }
  128. if (property == CommonProperties.Length)
  129. {
  130. return _length ?? PropertyDescriptor.Undefined;
  131. }
  132. if (property == CommonProperties.Name)
  133. {
  134. return _nameDescriptor ?? PropertyDescriptor.Undefined;
  135. }
  136. return base.GetOwnProperty(property);
  137. }
  138. protected internal override void SetOwnProperty(JsValue property, PropertyDescriptor desc)
  139. {
  140. if (property == CommonProperties.Prototype)
  141. {
  142. _prototypeDescriptor = desc;
  143. }
  144. else if (property == CommonProperties.Length)
  145. {
  146. _length = desc;
  147. }
  148. else if (property == CommonProperties.Name)
  149. {
  150. _nameDescriptor = desc;
  151. }
  152. else
  153. {
  154. base.SetOwnProperty(property, desc);
  155. }
  156. }
  157. public override bool HasOwnProperty(JsValue property)
  158. {
  159. if (property == CommonProperties.Prototype)
  160. {
  161. return _prototypeDescriptor != null;
  162. }
  163. if (property == CommonProperties.Length)
  164. {
  165. return _length != null;
  166. }
  167. if (property == CommonProperties.Name)
  168. {
  169. return _nameDescriptor != null;
  170. }
  171. return base.HasOwnProperty(property);
  172. }
  173. public override void RemoveOwnProperty(JsValue property)
  174. {
  175. if (property == CommonProperties.Prototype)
  176. {
  177. _prototypeDescriptor = null;
  178. }
  179. if (property == CommonProperties.Length)
  180. {
  181. _length = null;
  182. }
  183. if (property == CommonProperties.Name)
  184. {
  185. _nameDescriptor = null;
  186. }
  187. base.RemoveOwnProperty(property);
  188. }
  189. internal void SetFunctionName(JsValue name, string prefix = null, bool force = false)
  190. {
  191. if (!force && _nameDescriptor != null && !UnwrapJsValue(_nameDescriptor).IsUndefined())
  192. {
  193. return;
  194. }
  195. if (name is JsSymbol symbol)
  196. {
  197. name = symbol._value.IsUndefined()
  198. ? JsString.Empty
  199. : new JsString("[" + symbol._value + "]");
  200. }
  201. if (!string.IsNullOrWhiteSpace(prefix))
  202. {
  203. name = prefix + " " + name;
  204. }
  205. _nameDescriptor = new PropertyDescriptor(name, PropertyFlag.Configurable);
  206. }
  207. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  208. internal ObjectInstance OrdinaryCreateFromConstructor(JsValue constructor, ObjectInstance intrinsicDefaultProto)
  209. {
  210. var proto = GetPrototypeFromConstructor(constructor, intrinsicDefaultProto);
  211. var obj = new ObjectInstance(_engine)
  212. {
  213. _prototype = proto
  214. };
  215. return obj;
  216. }
  217. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  218. private static ObjectInstance GetPrototypeFromConstructor(JsValue constructor, ObjectInstance intrinsicDefaultProto)
  219. {
  220. var proto = constructor.Get(CommonProperties.Prototype, constructor) as ObjectInstance;
  221. // If Type(proto) is not Object, then
  222. // Let realm be ? GetFunctionRealm(constructor).
  223. // Set proto to realm's intrinsic object named intrinsicDefaultProto.
  224. return proto ?? intrinsicDefaultProto;
  225. }
  226. public override string ToString()
  227. {
  228. // TODO no way to extract SourceText from Esprima at the moment, just returning native code
  229. var nameValue = _nameDescriptor != null ? UnwrapJsValue(_nameDescriptor) : JsString.Empty;
  230. var name = "";
  231. if (!nameValue.IsUndefined())
  232. {
  233. name = TypeConverter.ToString(nameValue);
  234. }
  235. return "function " + name + "() {{[native code]}}";
  236. }
  237. }
  238. }