ObjectPrototype.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. #pragma warning disable CA1859 // Use concrete types when possible for improved performance -- most of prototype methods return JsValue
  2. using Jint.Collections;
  3. using Jint.Native.Array;
  4. using Jint.Native.Proxy;
  5. using Jint.Native.Symbol;
  6. using Jint.Runtime;
  7. using Jint.Runtime.Descriptors;
  8. using Jint.Runtime.Interop;
  9. namespace Jint.Native.Object
  10. {
  11. public sealed class ObjectPrototype : Prototype
  12. {
  13. private readonly ObjectConstructor _constructor;
  14. internal ObjectChangeFlags _objectChangeFlags;
  15. internal ObjectPrototype(
  16. Engine engine,
  17. Realm realm,
  18. ObjectConstructor constructor) : base(engine, realm)
  19. {
  20. _constructor = constructor;
  21. }
  22. protected override void Initialize()
  23. {
  24. const PropertyFlag propertyFlags = PropertyFlag.Configurable | PropertyFlag.Writable;
  25. const PropertyFlag lengthFlags = PropertyFlag.Configurable;
  26. var properties = new PropertyDictionary(12, checkExistingKeys: false)
  27. {
  28. ["constructor"] = new PropertyDescriptor(_constructor, propertyFlags),
  29. ["__defineGetter__"] = new PropertyDescriptor(new ClrFunction(Engine, "__defineGetter__", DefineGetter, 2, lengthFlags), propertyFlags),
  30. ["__defineSetter__"] = new PropertyDescriptor(new ClrFunction(Engine, "__defineSetter__", DefineSetter, 2, lengthFlags), propertyFlags),
  31. ["__lookupGetter__"] = new PropertyDescriptor(new ClrFunction(Engine, "__lookupGetter__", LookupGetter, 1, lengthFlags), propertyFlags),
  32. ["__lookupSetter__"] = new PropertyDescriptor(new ClrFunction(Engine, "__lookupSetter__", LookupSetter, 1, lengthFlags), propertyFlags),
  33. ["__proto__"] = new GetSetPropertyDescriptor(
  34. new ClrFunction(Engine, "get __proto__", (thisObject, _) => TypeConverter.ToObject(_realm, thisObject).GetPrototypeOf() ?? Null, 0, lengthFlags),
  35. new ClrFunction(Engine, "set __proto__", (thisObject, arguments) =>
  36. {
  37. TypeConverter.CheckObjectCoercible(_engine, thisObject);
  38. var proto = arguments.At(0);
  39. if (!proto.IsObject() && !proto.IsNull() || thisObject is not ObjectInstance objectInstance)
  40. {
  41. return Undefined;
  42. }
  43. if (!objectInstance.SetPrototypeOf(proto))
  44. {
  45. ExceptionHelper.ThrowTypeError(_realm, "Invalid prototype");
  46. }
  47. return Undefined;
  48. }, 0, lengthFlags),
  49. enumerable: false, configurable: true),
  50. ["toString"] = new PropertyDescriptor(new ClrFunction(Engine, "toString", ToObjectString, 0, lengthFlags), propertyFlags),
  51. ["toLocaleString"] = new PropertyDescriptor(new ClrFunction(Engine, "toLocaleString", ToLocaleString, 0, lengthFlags), propertyFlags),
  52. ["valueOf"] = new PropertyDescriptor(new ClrFunction(Engine, "valueOf", ValueOf, 0, lengthFlags), propertyFlags),
  53. ["hasOwnProperty"] = new PropertyDescriptor(new ClrFunction(Engine, "hasOwnProperty", HasOwnProperty, 1, lengthFlags), propertyFlags),
  54. ["isPrototypeOf"] = new PropertyDescriptor(new ClrFunction(Engine, "isPrototypeOf", IsPrototypeOf, 1, lengthFlags), propertyFlags),
  55. ["propertyIsEnumerable"] = new PropertyDescriptor(new ClrFunction(Engine, "propertyIsEnumerable", PropertyIsEnumerable, 1, lengthFlags), propertyFlags)
  56. };
  57. SetProperties(properties);
  58. }
  59. public override bool DefineOwnProperty(JsValue property, PropertyDescriptor desc)
  60. {
  61. TrackChanges(property);
  62. return base.DefineOwnProperty(property, desc);
  63. }
  64. protected internal override void SetOwnProperty(JsValue property, PropertyDescriptor desc)
  65. {
  66. TrackChanges(property);
  67. base.SetOwnProperty(property, desc);
  68. }
  69. private void TrackChanges(JsValue property)
  70. {
  71. EnsureInitialized();
  72. if (ArrayInstance.IsArrayIndex(property, out _))
  73. {
  74. _objectChangeFlags |= ObjectChangeFlags.ArrayIndex;
  75. }
  76. else
  77. {
  78. _objectChangeFlags |= property.IsSymbol() ? ObjectChangeFlags.Symbol : ObjectChangeFlags.Property;
  79. }
  80. }
  81. /// <summary>
  82. /// https://tc39.es/ecma262/#sec-object.prototype.__defineGetter__
  83. /// </summary>
  84. private JsValue DefineGetter(JsValue thisObject, JsValue[] arguments)
  85. {
  86. var o = TypeConverter.ToObject(_realm, thisObject);
  87. var p = arguments.At(0);
  88. var getter = arguments.At(1);
  89. if (!getter.IsCallable)
  90. {
  91. ExceptionHelper.ThrowTypeError(_realm, "Target is not callable");
  92. }
  93. var desc = new GetSetPropertyDescriptor(getter, null, enumerable: true, configurable: true);
  94. var key = TypeConverter.ToPropertyKey(p);
  95. o.DefinePropertyOrThrow(key, desc);
  96. return Undefined;
  97. }
  98. /// <summary>
  99. /// https://tc39.es/ecma262/#sec-object.prototype.__defineSetter__
  100. /// </summary>
  101. private JsValue DefineSetter(JsValue thisObject, JsValue[] arguments)
  102. {
  103. var o = TypeConverter.ToObject(_realm, thisObject);
  104. var p = arguments.At(0);
  105. var setter = arguments.At(1);
  106. if (!setter.IsCallable)
  107. {
  108. ExceptionHelper.ThrowTypeError(_realm, "Target is not callable");
  109. }
  110. var desc = new GetSetPropertyDescriptor(null, setter, enumerable: true, configurable: true);
  111. var key = TypeConverter.ToPropertyKey(p);
  112. o.DefinePropertyOrThrow(key, desc);
  113. return Undefined;
  114. }
  115. /// <summary>
  116. /// https://tc39.es/ecma262/#sec-object.prototype.__lookupGetter__
  117. /// </summary>
  118. private JsValue LookupGetter(JsValue thisObject, JsValue[] arguments)
  119. {
  120. var o = TypeConverter.ToObject(_realm, thisObject);
  121. var key = TypeConverter.ToPropertyKey(arguments.At(0));
  122. while (true)
  123. {
  124. var desc = o.GetOwnProperty(key);
  125. if (!ReferenceEquals(desc, PropertyDescriptor.Undefined))
  126. {
  127. if (desc.IsAccessorDescriptor())
  128. {
  129. return desc.Get ?? Undefined;
  130. }
  131. return Undefined;
  132. }
  133. o = o.GetPrototypeOf();
  134. if (o is null)
  135. {
  136. return Undefined;
  137. }
  138. }
  139. }
  140. /// <summary>
  141. /// https://tc39.es/ecma262/#sec-object.prototype.__lookupSetter__
  142. /// </summary>
  143. private JsValue LookupSetter(JsValue thisObject, JsValue[] arguments)
  144. {
  145. var o = TypeConverter.ToObject(_realm, thisObject);
  146. var key = TypeConverter.ToPropertyKey(arguments.At(0));
  147. while (true)
  148. {
  149. var desc = o.GetOwnProperty(key);
  150. if (!ReferenceEquals(desc, PropertyDescriptor.Undefined))
  151. {
  152. if (desc.IsAccessorDescriptor())
  153. {
  154. return desc.Set ?? Undefined;
  155. }
  156. return Undefined;
  157. }
  158. o = o.GetPrototypeOf();
  159. if (o is null)
  160. {
  161. return Undefined;
  162. }
  163. }
  164. }
  165. private JsValue PropertyIsEnumerable(JsValue thisObject, JsValue[] arguments)
  166. {
  167. var p = TypeConverter.ToPropertyKey(arguments[0]);
  168. var o = TypeConverter.ToObject(_realm, thisObject);
  169. var desc = o.GetOwnProperty(p);
  170. if (desc == PropertyDescriptor.Undefined)
  171. {
  172. return JsBoolean.False;
  173. }
  174. return desc.Enumerable;
  175. }
  176. private JsValue ValueOf(JsValue thisObject, JsValue[] arguments)
  177. {
  178. var o = TypeConverter.ToObject(_realm, thisObject);
  179. return o;
  180. }
  181. private JsValue IsPrototypeOf(JsValue thisObject, JsValue[] arguments)
  182. {
  183. var arg = arguments[0];
  184. if (!arg.IsObject())
  185. {
  186. return JsBoolean.False;
  187. }
  188. var v = arg.AsObject();
  189. var o = TypeConverter.ToObject(_realm, thisObject);
  190. while (true)
  191. {
  192. v = v.Prototype;
  193. if (ReferenceEquals(v, null))
  194. {
  195. return JsBoolean.False;
  196. }
  197. if (ReferenceEquals(o, v))
  198. {
  199. return JsBoolean.True;
  200. }
  201. }
  202. }
  203. /// <summary>
  204. /// https://tc39.es/ecma262/#sec-object.prototype.tolocalestring
  205. /// </summary>
  206. private JsValue ToLocaleString(JsValue thisObject, JsValue[] arguments)
  207. {
  208. return Invoke(thisObject, "toString", System.Array.Empty<JsValue>());
  209. }
  210. /// <summary>
  211. /// https://tc39.es/ecma262/#sec-object.prototype.tostring
  212. /// </summary>
  213. internal JsValue ToObjectString(JsValue thisObject, JsValue[] arguments)
  214. {
  215. if (thisObject.IsUndefined())
  216. {
  217. return "[object Undefined]";
  218. }
  219. if (thisObject.IsNull())
  220. {
  221. return "[object Null]";
  222. }
  223. var o = TypeConverter.ToObject(_realm, thisObject);
  224. var isArray = o.IsArray();
  225. var tag = o.Get(GlobalSymbolRegistry.ToStringTag);
  226. if (!tag.IsString())
  227. {
  228. if (isArray)
  229. {
  230. tag = "Array";
  231. }
  232. else if (o.IsCallable)
  233. {
  234. tag = "Function";
  235. }
  236. else
  237. {
  238. tag = (o is JsProxy ? ObjectClass.Object : o.Class).ToString();
  239. }
  240. }
  241. return "[object " + tag + "]";
  242. }
  243. /// <summary>
  244. /// http://www.ecma-international.org/ecma-262/5.1/#sec-15.2.4.5
  245. /// </summary>
  246. private JsValue HasOwnProperty(JsValue thisObject, JsValue[] arguments)
  247. {
  248. var p = TypeConverter.ToPropertyKey(arguments[0]);
  249. var o = TypeConverter.ToObject(_realm, thisObject);
  250. var desc = o.GetOwnProperty(p);
  251. return desc != PropertyDescriptor.Undefined;
  252. }
  253. }
  254. }