2
0

ObjectPrototype.cs 10 KB

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