ObjectPrototype.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. /*
  2. * Copyright (c) 2020, Andreas Kling <andreas@ladybird.org>
  3. * Copyright (c) 2020-2023, Linus Groh <linusg@serenityos.org>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include <AK/ByteString.h>
  8. #include <AK/Function.h>
  9. #include <LibJS/Runtime/AbstractOperations.h>
  10. #include <LibJS/Runtime/Accessor.h>
  11. #include <LibJS/Runtime/BooleanObject.h>
  12. #include <LibJS/Runtime/Completion.h>
  13. #include <LibJS/Runtime/Date.h>
  14. #include <LibJS/Runtime/GlobalObject.h>
  15. #include <LibJS/Runtime/NumberObject.h>
  16. #include <LibJS/Runtime/ObjectPrototype.h>
  17. #include <LibJS/Runtime/RegExpObject.h>
  18. #include <LibJS/Runtime/StringObject.h>
  19. #include <LibJS/Runtime/Value.h>
  20. namespace JS {
  21. GC_DEFINE_ALLOCATOR(ObjectPrototype);
  22. ObjectPrototype::ObjectPrototype(Realm& realm)
  23. : Object(Object::ConstructWithoutPrototypeTag::Tag, realm)
  24. {
  25. }
  26. void ObjectPrototype::initialize(Realm& realm)
  27. {
  28. auto& vm = this->vm();
  29. Base::initialize(realm);
  30. // This must be called after the constructor has returned, so that the below code
  31. // can find the ObjectPrototype through normal paths.
  32. u8 attr = Attribute::Writable | Attribute::Configurable;
  33. define_native_function(realm, vm.names.hasOwnProperty, has_own_property, 1, attr);
  34. define_native_function(realm, vm.names.toString, to_string, 0, attr);
  35. define_native_function(realm, vm.names.toLocaleString, to_locale_string, 0, attr);
  36. define_native_function(realm, vm.names.valueOf, value_of, 0, attr);
  37. define_native_function(realm, vm.names.propertyIsEnumerable, property_is_enumerable, 1, attr);
  38. define_native_function(realm, vm.names.isPrototypeOf, is_prototype_of, 1, attr);
  39. // Annex B
  40. define_native_function(realm, vm.names.__defineGetter__, define_getter, 2, attr);
  41. define_native_function(realm, vm.names.__defineSetter__, define_setter, 2, attr);
  42. define_native_function(realm, vm.names.__lookupGetter__, lookup_getter, 1, attr);
  43. define_native_function(realm, vm.names.__lookupSetter__, lookup_setter, 1, attr);
  44. define_native_accessor(realm, vm.names.__proto__, proto_getter, proto_setter, Attribute::Configurable);
  45. }
  46. // 10.4.7.1 [[SetPrototypeOf]] ( V ), https://tc39.es/ecma262/#sec-immutable-prototype-exotic-objects-setprototypeof-v
  47. ThrowCompletionOr<bool> ObjectPrototype::internal_set_prototype_of(Object* prototype)
  48. {
  49. // 1. Return ? SetImmutablePrototype(O, V).
  50. return set_immutable_prototype(prototype);
  51. }
  52. // 20.1.3.2 Object.prototype.hasOwnProperty ( V ), https://tc39.es/ecma262/#sec-object.prototype.hasownproperty
  53. JS_DEFINE_NATIVE_FUNCTION(ObjectPrototype::has_own_property)
  54. {
  55. // 1. Let P be ? ToPropertyKey(V).
  56. auto property_key = TRY(vm.argument(0).to_property_key(vm));
  57. // 2. Let O be ? ToObject(this value).
  58. auto this_object = TRY(vm.this_value().to_object(vm));
  59. // 3. Return ? HasOwnProperty(O, P).
  60. return Value(TRY(this_object->has_own_property(property_key)));
  61. }
  62. // 20.1.3.3 Object.prototype.isPrototypeOf ( V ), https://tc39.es/ecma262/#sec-object.prototype.isprototypeof
  63. JS_DEFINE_NATIVE_FUNCTION(ObjectPrototype::is_prototype_of)
  64. {
  65. auto object_argument = vm.argument(0);
  66. // 1. If V is not an Object, return false.
  67. if (!object_argument.is_object())
  68. return Value(false);
  69. auto* object = &object_argument.as_object();
  70. // 2. Let O be ? ToObject(this value).
  71. auto this_object = TRY(vm.this_value().to_object(vm));
  72. // 3. Repeat,
  73. for (;;) {
  74. // a. Set V to ? V.[[GetPrototypeOf]]().
  75. object = TRY(object->internal_get_prototype_of());
  76. // b. If V is null, return false.
  77. if (!object)
  78. return Value(false);
  79. // c. If SameValue(O, V) is true, return true.
  80. if (same_value(this_object, object))
  81. return Value(true);
  82. }
  83. }
  84. // 20.1.3.4 Object.prototype.propertyIsEnumerable ( V ), https://tc39.es/ecma262/#sec-object.prototype.propertyisenumerable
  85. JS_DEFINE_NATIVE_FUNCTION(ObjectPrototype::property_is_enumerable)
  86. {
  87. // 1. Let P be ? ToPropertyKey(V).
  88. auto property_key = TRY(vm.argument(0).to_property_key(vm));
  89. // 2. Let O be ? ToObject(this value).
  90. auto this_object = TRY(vm.this_value().to_object(vm));
  91. // 3. Let desc be ? O.[[GetOwnProperty]](P).
  92. auto property_descriptor = TRY(this_object->internal_get_own_property(property_key));
  93. // 4. If desc is undefined, return false.
  94. if (!property_descriptor.has_value())
  95. return Value(false);
  96. // 5. Return desc.[[Enumerable]].
  97. return Value(*property_descriptor->enumerable);
  98. }
  99. // 20.1.3.5 Object.prototype.toLocaleString ( [ reserved1 [ , reserved2 ] ] ), https://tc39.es/ecma262/#sec-object.prototype.tolocalestring
  100. JS_DEFINE_NATIVE_FUNCTION(ObjectPrototype::to_locale_string)
  101. {
  102. // 1. Let O be the this value.
  103. auto this_value = vm.this_value();
  104. // 2. Return ? Invoke(O, "toString").
  105. return this_value.invoke(vm, vm.names.toString);
  106. }
  107. // 20.1.3.6 Object.prototype.toString ( ), https://tc39.es/ecma262/#sec-object.prototype.tostring
  108. JS_DEFINE_NATIVE_FUNCTION(ObjectPrototype::to_string)
  109. {
  110. auto this_value = vm.this_value();
  111. // 1. If the this value is undefined, return "[object Undefined]".
  112. if (this_value.is_undefined())
  113. return PrimitiveString::create(vm, "[object Undefined]"_string);
  114. // 2. If the this value is null, return "[object Null]".
  115. if (this_value.is_null())
  116. return PrimitiveString::create(vm, "[object Null]"_string);
  117. // 3. Let O be ! ToObject(this value).
  118. auto object = MUST(this_value.to_object(vm));
  119. // 4. Let isArray be ? IsArray(O).
  120. auto is_array = TRY(Value(object).is_array(vm));
  121. ByteString builtin_tag;
  122. // 5. If isArray is true, let builtinTag be "Array".
  123. if (is_array)
  124. builtin_tag = "Array";
  125. // 6. Else if O has a [[ParameterMap]] internal slot, let builtinTag be "Arguments".
  126. else if (object->has_parameter_map())
  127. builtin_tag = "Arguments";
  128. // 7. Else if O has a [[Call]] internal method, let builtinTag be "Function".
  129. else if (object->is_function())
  130. builtin_tag = "Function";
  131. // 8. Else if O has an [[ErrorData]] internal slot, let builtinTag be "Error".
  132. else if (is<Error>(*object))
  133. builtin_tag = "Error";
  134. // 9. Else if O has a [[BooleanData]] internal slot, let builtinTag be "Boolean".
  135. else if (is<BooleanObject>(*object))
  136. builtin_tag = "Boolean";
  137. // 10. Else if O has a [[NumberData]] internal slot, let builtinTag be "Number".
  138. else if (is<NumberObject>(*object))
  139. builtin_tag = "Number";
  140. // 11. Else if O has a [[StringData]] internal slot, let builtinTag be "String".
  141. else if (is<StringObject>(*object))
  142. builtin_tag = "String";
  143. // 12. Else if O has a [[DateValue]] internal slot, let builtinTag be "Date".
  144. else if (is<Date>(*object))
  145. builtin_tag = "Date";
  146. // 13. Else if O has a [[RegExpMatcher]] internal slot, let builtinTag be "RegExp".
  147. else if (is<RegExpObject>(*object))
  148. builtin_tag = "RegExp";
  149. // 14. Else, let builtinTag be "Object".
  150. else
  151. builtin_tag = "Object";
  152. // 15. Let tag be ? Get(O, @@toStringTag).
  153. auto to_string_tag = TRY(object->get(vm.well_known_symbol_to_string_tag()));
  154. // Optimization: Instead of creating another PrimitiveString from builtin_tag, we separate tag and to_string_tag and add an additional branch to step 16.
  155. ByteString tag;
  156. // 16. If Type(tag) is not String, set tag to builtinTag.
  157. if (!to_string_tag.is_string())
  158. tag = move(builtin_tag);
  159. else
  160. tag = to_string_tag.as_string().byte_string();
  161. // 17. Return the string-concatenation of "[object ", tag, and "]".
  162. return PrimitiveString::create(vm, ByteString::formatted("[object {}]", tag));
  163. }
  164. // 20.1.3.7 Object.prototype.valueOf ( ), https://tc39.es/ecma262/#sec-object.prototype.valueof
  165. JS_DEFINE_NATIVE_FUNCTION(ObjectPrototype::value_of)
  166. {
  167. // 1. Return ? ToObject(this value).
  168. return TRY(vm.this_value().to_object(vm));
  169. }
  170. // 20.1.3.8.1 get Object.prototype.__proto__, https://tc39.es/ecma262/#sec-get-object.prototype.__proto__
  171. JS_DEFINE_NATIVE_FUNCTION(ObjectPrototype::proto_getter)
  172. {
  173. // 1. Let O be ? ToObject(this value).
  174. auto object = TRY(vm.this_value().to_object(vm));
  175. // 2. Return ? O.[[GetPrototypeOf]]().
  176. return TRY(object->internal_get_prototype_of());
  177. }
  178. // 20.1.3.8.2 set Object.prototype.__proto__, https://tc39.es/ecma262/#sec-set-object.prototype.__proto__
  179. JS_DEFINE_NATIVE_FUNCTION(ObjectPrototype::proto_setter)
  180. {
  181. auto proto = vm.argument(0);
  182. // 1. Let O be ? RequireObjectCoercible(this value).
  183. auto object = TRY(require_object_coercible(vm, vm.this_value()));
  184. // 2. If proto is not an Object and proto is not null, return undefined.
  185. if (!proto.is_object() && !proto.is_null())
  186. return js_undefined();
  187. // 3. If O is not an Object, return undefined.
  188. if (!object.is_object())
  189. return js_undefined();
  190. // 4. Let status be ? O.[[SetPrototypeOf]](proto).
  191. auto status = TRY(object.as_object().internal_set_prototype_of(proto.is_object() ? &proto.as_object() : nullptr));
  192. // 5. If status is false, throw a TypeError exception.
  193. if (!status) {
  194. // FIXME: Improve/contextualize error message
  195. return vm.throw_completion<TypeError>(ErrorType::ObjectSetPrototypeOfReturnedFalse);
  196. }
  197. // 6. Return undefined.
  198. return js_undefined();
  199. }
  200. // 20.1.3.9.1 Object.prototype.__defineGetter__ ( P, getter ), https://tc39.es/ecma262/#sec-object.prototype.__defineGetter__
  201. JS_DEFINE_NATIVE_FUNCTION(ObjectPrototype::define_getter)
  202. {
  203. auto property = vm.argument(0);
  204. auto getter = vm.argument(1);
  205. // 1. Let O be ? ToObject(this value).
  206. auto object = TRY(vm.this_value().to_object(vm));
  207. // 2. If IsCallable(getter) is false, throw a TypeError exception.
  208. if (!getter.is_function())
  209. return vm.throw_completion<TypeError>(ErrorType::NotAFunction, getter.to_string_without_side_effects());
  210. // 3. Let desc be PropertyDescriptor { [[Get]]: getter, [[Enumerable]]: true, [[Configurable]]: true }.
  211. auto descriptor = PropertyDescriptor { .get = &getter.as_function(), .enumerable = true, .configurable = true };
  212. // 4. Let key be ? ToPropertyKey(P).
  213. auto key = TRY(property.to_property_key(vm));
  214. // 5. Perform ? DefinePropertyOrThrow(O, key, desc).
  215. TRY(object->define_property_or_throw(key, descriptor));
  216. // 6. Return undefined.
  217. return js_undefined();
  218. }
  219. // 20.1.3.9.2 Object.prototype.__defineSetter__ ( P, setter ), https://tc39.es/ecma262/#sec-object.prototype.__defineSetter__
  220. JS_DEFINE_NATIVE_FUNCTION(ObjectPrototype::define_setter)
  221. {
  222. auto property = vm.argument(0);
  223. auto setter = vm.argument(1);
  224. // 1. Let O be ? ToObject(this value).
  225. auto object = TRY(vm.this_value().to_object(vm));
  226. // 2. If IsCallable(setter) is false, throw a TypeError exception.
  227. if (!setter.is_function())
  228. return vm.throw_completion<TypeError>(ErrorType::NotAFunction, setter.to_string_without_side_effects());
  229. // 3. Let desc be PropertyDescriptor { [[Set]]: setter, [[Enumerable]]: true, [[Configurable]]: true }.
  230. auto descriptor = PropertyDescriptor { .set = &setter.as_function(), .enumerable = true, .configurable = true };
  231. // 4. Let key be ? ToPropertyKey(P).
  232. auto key = TRY(property.to_property_key(vm));
  233. // 5. Perform ? DefinePropertyOrThrow(O, key, desc).
  234. TRY(object->define_property_or_throw(key, descriptor));
  235. // 6. Return undefined.
  236. return js_undefined();
  237. }
  238. // 20.1.3.9.3 Object.prototype.__lookupGetter__ ( P ), https://tc39.es/ecma262/#sec-object.prototype.__lookupGetter__
  239. JS_DEFINE_NATIVE_FUNCTION(ObjectPrototype::lookup_getter)
  240. {
  241. auto property = vm.argument(0);
  242. // 1. Let O be ? ToObject(this value).
  243. auto object = GC::Ptr { TRY(vm.this_value().to_object(vm)) };
  244. // 2. Let key be ? ToPropertyKey(P).
  245. auto key = TRY(property.to_property_key(vm));
  246. // 3. Repeat,
  247. while (object) {
  248. // a. Let desc be ? O.[[GetOwnProperty]](key).
  249. auto desc = TRY(object->internal_get_own_property(key));
  250. // b. If desc is not undefined, then
  251. if (desc.has_value()) {
  252. // i. If IsAccessorDescriptor(desc) is true, return desc.[[Get]].
  253. if (desc->is_accessor_descriptor())
  254. return *desc->get ?: js_undefined();
  255. // ii. Return undefined.
  256. return js_undefined();
  257. }
  258. // c. Set O to ? O.[[GetPrototypeOf]]().
  259. object = TRY(object->internal_get_prototype_of());
  260. }
  261. // d. If O is null, return undefined.
  262. return js_undefined();
  263. }
  264. // 20.1.3.9.4 Object.prototype.__lookupSetter__ ( P ), https://tc39.es/ecma262/#sec-object.prototype.__lookupSetter__
  265. JS_DEFINE_NATIVE_FUNCTION(ObjectPrototype::lookup_setter)
  266. {
  267. auto property = vm.argument(0);
  268. // 1. Let O be ? ToObject(this value).
  269. auto object = GC::Ptr { TRY(vm.this_value().to_object(vm)) };
  270. // 2. Let key be ? ToPropertyKey(P).
  271. auto key = TRY(property.to_property_key(vm));
  272. // 3. Repeat,
  273. while (object) {
  274. // a. Let desc be ? O.[[GetOwnProperty]](key).
  275. auto desc = TRY(object->internal_get_own_property(key));
  276. // b. If desc is not undefined, then
  277. if (desc.has_value()) {
  278. // i. If IsAccessorDescriptor(desc) is true, return desc.[[Set]].
  279. if (desc->is_accessor_descriptor())
  280. return *desc->set ?: js_undefined();
  281. // ii. Return undefined.
  282. return js_undefined();
  283. }
  284. // c. Set O to ? O.[[GetPrototypeOf]]().
  285. object = TRY(object->internal_get_prototype_of());
  286. }
  287. // d. If O is null, return undefined.
  288. return js_undefined();
  289. }
  290. }