FunctionInstance.cs 8.6 KB

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