ObjectPrototype.cs 10 KB

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