1 // Copyright 2016 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/builtins/builtins-utils-inl.h"
6 #include "src/builtins/builtins.h"
7 #include "src/codegen/code-factory.h"
8 #include "src/common/message-template.h"
9 #include "src/heap/heap-inl.h"  // For ToBoolean. TODO(jkummerow): Drop.
10 #include "src/logging/counters.h"
11 #include "src/objects/keys.h"
12 #include "src/objects/lookup.h"
13 #include "src/objects/objects-inl.h"
14 #include "src/objects/property-descriptor.h"
15 
16 namespace v8 {
17 namespace internal {
18 
19 // -----------------------------------------------------------------------------
20 // ES6 section 19.1 Object Objects
21 
22 // ES6 section 19.1.3.4 Object.prototype.propertyIsEnumerable ( V )
BUILTIN(ObjectPrototypePropertyIsEnumerable)23 BUILTIN(ObjectPrototypePropertyIsEnumerable) {
24   HandleScope scope(isolate);
25   Handle<JSReceiver> object;
26   Handle<Name> name;
27   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
28       isolate, name, Object::ToName(isolate, args.atOrUndefined(isolate, 1)));
29   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
30       isolate, object, Object::ToObject(isolate, args.receiver()));
31   Maybe<PropertyAttributes> maybe =
32       JSReceiver::GetOwnPropertyAttributes(object, name);
33   if (maybe.IsNothing()) return ReadOnlyRoots(isolate).exception();
34   if (maybe.FromJust() == ABSENT) return ReadOnlyRoots(isolate).false_value();
35   return isolate->heap()->ToBoolean((maybe.FromJust() & DONT_ENUM) == 0);
36 }
37 
38 // ES6 section 19.1.2.3 Object.defineProperties
BUILTIN(ObjectDefineProperties)39 BUILTIN(ObjectDefineProperties) {
40   HandleScope scope(isolate);
41   DCHECK_EQ(3, args.length());
42   Handle<Object> target = args.at(1);
43   Handle<Object> properties = args.at(2);
44 
45   RETURN_RESULT_OR_FAILURE(
46       isolate, JSReceiver::DefineProperties(isolate, target, properties));
47 }
48 
49 // ES6 section 19.1.2.4 Object.defineProperty
BUILTIN(ObjectDefineProperty)50 BUILTIN(ObjectDefineProperty) {
51   HandleScope scope(isolate);
52   DCHECK_EQ(4, args.length());
53   Handle<Object> target = args.at(1);
54   Handle<Object> key = args.at(2);
55   Handle<Object> attributes = args.at(3);
56 
57   return JSReceiver::DefineProperty(isolate, target, key, attributes);
58 }
59 
60 namespace {
61 
62 template <AccessorComponent which_accessor>
ObjectDefineAccessor(Isolate * isolate,Handle<Object> object,Handle<Object> name,Handle<Object> accessor)63 Object ObjectDefineAccessor(Isolate* isolate, Handle<Object> object,
64                             Handle<Object> name, Handle<Object> accessor) {
65   // 1. Let O be ? ToObject(this value).
66   Handle<JSReceiver> receiver;
67   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, receiver,
68                                      Object::ToObject(isolate, object));
69   // 2. If IsCallable(getter) is false, throw a TypeError exception.
70   if (!accessor->IsCallable()) {
71     MessageTemplate message =
72         which_accessor == ACCESSOR_GETTER
73             ? MessageTemplate::kObjectGetterExpectingFunction
74             : MessageTemplate::kObjectSetterExpectingFunction;
75     THROW_NEW_ERROR_RETURN_FAILURE(isolate, NewTypeError(message));
76   }
77   // 3. Let desc be PropertyDescriptor{[[Get]]: getter, [[Enumerable]]: true,
78   //                                   [[Configurable]]: true}.
79   PropertyDescriptor desc;
80   if (which_accessor == ACCESSOR_GETTER) {
81     desc.set_get(accessor);
82   } else {
83     DCHECK(which_accessor == ACCESSOR_SETTER);
84     desc.set_set(accessor);
85   }
86   desc.set_enumerable(true);
87   desc.set_configurable(true);
88   // 4. Let key be ? ToPropertyKey(P).
89   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, name,
90                                      Object::ToPropertyKey(isolate, name));
91   // 5. Perform ? DefinePropertyOrThrow(O, key, desc).
92   // To preserve legacy behavior, we ignore errors silently rather than
93   // throwing an exception.
94   Maybe<bool> success = JSReceiver::DefineOwnProperty(
95       isolate, receiver, name, &desc, Just(kThrowOnError));
96   MAYBE_RETURN(success, ReadOnlyRoots(isolate).exception());
97   if (!success.FromJust()) {
98     isolate->CountUsage(v8::Isolate::kDefineGetterOrSetterWouldThrow);
99   }
100   // 6. Return undefined.
101   return ReadOnlyRoots(isolate).undefined_value();
102 }
103 
ObjectLookupAccessor(Isolate * isolate,Handle<Object> object,Handle<Object> key,AccessorComponent component)104 Object ObjectLookupAccessor(Isolate* isolate, Handle<Object> object,
105                             Handle<Object> key, AccessorComponent component) {
106   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, object,
107                                      Object::ToObject(isolate, object));
108   // TODO(jkummerow/verwaest): LookupIterator::Key(..., bool*) performs a
109   // functionally equivalent conversion, but handles element indices slightly
110   // differently. Does one of the approaches have a performance advantage?
111   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, key,
112                                      Object::ToPropertyKey(isolate, key));
113   LookupIterator::Key lookup_key(isolate, key);
114   LookupIterator it(isolate, object, lookup_key,
115                     LookupIterator::PROTOTYPE_CHAIN_SKIP_INTERCEPTOR);
116 
117   for (; it.IsFound(); it.Next()) {
118     switch (it.state()) {
119       case LookupIterator::INTERCEPTOR:
120       case LookupIterator::NOT_FOUND:
121       case LookupIterator::TRANSITION:
122         UNREACHABLE();
123 
124       case LookupIterator::ACCESS_CHECK:
125         if (it.HasAccess()) continue;
126         isolate->ReportFailedAccessCheck(it.GetHolder<JSObject>());
127         RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate);
128         return ReadOnlyRoots(isolate).undefined_value();
129 
130       case LookupIterator::JSPROXY: {
131         PropertyDescriptor desc;
132         Maybe<bool> found = JSProxy::GetOwnPropertyDescriptor(
133             isolate, it.GetHolder<JSProxy>(), it.GetName(), &desc);
134         MAYBE_RETURN(found, ReadOnlyRoots(isolate).exception());
135         if (found.FromJust()) {
136           if (component == ACCESSOR_GETTER && desc.has_get()) {
137             return *desc.get();
138           }
139           if (component == ACCESSOR_SETTER && desc.has_set()) {
140             return *desc.set();
141           }
142           return ReadOnlyRoots(isolate).undefined_value();
143         }
144         Handle<Object> prototype;
145         ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
146             isolate, prototype, JSProxy::GetPrototype(it.GetHolder<JSProxy>()));
147         if (prototype->IsNull(isolate)) {
148           return ReadOnlyRoots(isolate).undefined_value();
149         }
150         return ObjectLookupAccessor(isolate, prototype, key, component);
151       }
152 
153       case LookupIterator::INTEGER_INDEXED_EXOTIC:
154       case LookupIterator::DATA:
155         return ReadOnlyRoots(isolate).undefined_value();
156 
157       case LookupIterator::ACCESSOR: {
158         Handle<Object> maybe_pair = it.GetAccessors();
159         if (maybe_pair->IsAccessorPair()) {
160           Handle<NativeContext> native_context =
161               it.GetHolder<JSReceiver>()->GetCreationContext();
162           return *AccessorPair::GetComponent(
163               isolate, native_context, Handle<AccessorPair>::cast(maybe_pair),
164               component);
165         }
166       }
167     }
168   }
169 
170   return ReadOnlyRoots(isolate).undefined_value();
171 }
172 
173 }  // namespace
174 
175 // ES6 B.2.2.2 a.k.a.
176 // https://tc39.github.io/ecma262/#sec-object.prototype.__defineGetter__
BUILTIN(ObjectDefineGetter)177 BUILTIN(ObjectDefineGetter) {
178   HandleScope scope(isolate);
179   Handle<Object> object = args.at(0);  // Receiver.
180   Handle<Object> name = args.at(1);
181   Handle<Object> getter = args.at(2);
182   return ObjectDefineAccessor<ACCESSOR_GETTER>(isolate, object, name, getter);
183 }
184 
185 // ES6 B.2.2.3 a.k.a.
186 // https://tc39.github.io/ecma262/#sec-object.prototype.__defineSetter__
BUILTIN(ObjectDefineSetter)187 BUILTIN(ObjectDefineSetter) {
188   HandleScope scope(isolate);
189   Handle<Object> object = args.at(0);  // Receiver.
190   Handle<Object> name = args.at(1);
191   Handle<Object> setter = args.at(2);
192   return ObjectDefineAccessor<ACCESSOR_SETTER>(isolate, object, name, setter);
193 }
194 
195 // ES6 B.2.2.4 a.k.a.
196 // https://tc39.github.io/ecma262/#sec-object.prototype.__lookupGetter__
BUILTIN(ObjectLookupGetter)197 BUILTIN(ObjectLookupGetter) {
198   HandleScope scope(isolate);
199   Handle<Object> object = args.at(0);
200   Handle<Object> name = args.at(1);
201   return ObjectLookupAccessor(isolate, object, name, ACCESSOR_GETTER);
202 }
203 
204 // ES6 B.2.2.5 a.k.a.
205 // https://tc39.github.io/ecma262/#sec-object.prototype.__lookupSetter__
BUILTIN(ObjectLookupSetter)206 BUILTIN(ObjectLookupSetter) {
207   HandleScope scope(isolate);
208   Handle<Object> object = args.at(0);
209   Handle<Object> name = args.at(1);
210   return ObjectLookupAccessor(isolate, object, name, ACCESSOR_SETTER);
211 }
212 
213 // ES6 section 19.1.2.5 Object.freeze ( O )
BUILTIN(ObjectFreeze)214 BUILTIN(ObjectFreeze) {
215   HandleScope scope(isolate);
216   Handle<Object> object = args.atOrUndefined(isolate, 1);
217   if (object->IsJSReceiver()) {
218     MAYBE_RETURN(JSReceiver::SetIntegrityLevel(Handle<JSReceiver>::cast(object),
219                                                FROZEN, kThrowOnError),
220                  ReadOnlyRoots(isolate).exception());
221   }
222   return *object;
223 }
224 
225 // ES6 section B.2.2.1.1 get Object.prototype.__proto__
BUILTIN(ObjectPrototypeGetProto)226 BUILTIN(ObjectPrototypeGetProto) {
227   HandleScope scope(isolate);
228   // 1. Let O be ? ToObject(this value).
229   Handle<JSReceiver> receiver;
230   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
231       isolate, receiver, Object::ToObject(isolate, args.receiver()));
232 
233   // 2. Return ? O.[[GetPrototypeOf]]().
234   RETURN_RESULT_OR_FAILURE(isolate,
235                            JSReceiver::GetPrototype(isolate, receiver));
236 }
237 
238 // ES6 section B.2.2.1.2 set Object.prototype.__proto__
BUILTIN(ObjectPrototypeSetProto)239 BUILTIN(ObjectPrototypeSetProto) {
240   HandleScope scope(isolate);
241   // 1. Let O be ? RequireObjectCoercible(this value).
242   Handle<Object> object = args.receiver();
243   if (object->IsNullOrUndefined(isolate)) {
244     THROW_NEW_ERROR_RETURN_FAILURE(
245         isolate, NewTypeError(MessageTemplate::kCalledOnNullOrUndefined,
246                               isolate->factory()->NewStringFromAsciiChecked(
247                                   "set Object.prototype.__proto__")));
248   }
249 
250   // 2. If Type(proto) is neither Object nor Null, return undefined.
251   Handle<Object> proto = args.at(1);
252   if (!proto->IsNull(isolate) && !proto->IsJSReceiver()) {
253     return ReadOnlyRoots(isolate).undefined_value();
254   }
255 
256   // 3. If Type(O) is not Object, return undefined.
257   if (!object->IsJSReceiver()) return ReadOnlyRoots(isolate).undefined_value();
258   Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(object);
259 
260   // 4. Let status be ? O.[[SetPrototypeOf]](proto).
261   // 5. If status is false, throw a TypeError exception.
262   MAYBE_RETURN(JSReceiver::SetPrototype(receiver, proto, true, kThrowOnError),
263                ReadOnlyRoots(isolate).exception());
264 
265   // Return undefined.
266   return ReadOnlyRoots(isolate).undefined_value();
267 }
268 
269 namespace {
270 
GetOwnPropertyKeys(Isolate * isolate,BuiltinArguments args,PropertyFilter filter)271 Object GetOwnPropertyKeys(Isolate* isolate, BuiltinArguments args,
272                           PropertyFilter filter) {
273   HandleScope scope(isolate);
274   Handle<Object> object = args.atOrUndefined(isolate, 1);
275   Handle<JSReceiver> receiver;
276   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, receiver,
277                                      Object::ToObject(isolate, object));
278   Handle<FixedArray> keys;
279   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
280       isolate, keys,
281       KeyAccumulator::GetKeys(receiver, KeyCollectionMode::kOwnOnly, filter,
282                               GetKeysConversion::kConvertToString));
283   return *isolate->factory()->NewJSArrayWithElements(keys);
284 }
285 
286 }  // namespace
287 
288 // ES6 section 19.1.2.8 Object.getOwnPropertySymbols ( O )
BUILTIN(ObjectGetOwnPropertySymbols)289 BUILTIN(ObjectGetOwnPropertySymbols) {
290   return GetOwnPropertyKeys(isolate, args, SKIP_STRINGS);
291 }
292 
293 // ES6 section 19.1.2.12 Object.isFrozen ( O )
BUILTIN(ObjectIsFrozen)294 BUILTIN(ObjectIsFrozen) {
295   HandleScope scope(isolate);
296   Handle<Object> object = args.atOrUndefined(isolate, 1);
297   Maybe<bool> result = object->IsJSReceiver()
298                            ? JSReceiver::TestIntegrityLevel(
299                                  Handle<JSReceiver>::cast(object), FROZEN)
300                            : Just(true);
301   MAYBE_RETURN(result, ReadOnlyRoots(isolate).exception());
302   return isolate->heap()->ToBoolean(result.FromJust());
303 }
304 
305 // ES6 section 19.1.2.13 Object.isSealed ( O )
BUILTIN(ObjectIsSealed)306 BUILTIN(ObjectIsSealed) {
307   HandleScope scope(isolate);
308   Handle<Object> object = args.atOrUndefined(isolate, 1);
309   Maybe<bool> result = object->IsJSReceiver()
310                            ? JSReceiver::TestIntegrityLevel(
311                                  Handle<JSReceiver>::cast(object), SEALED)
312                            : Just(true);
313   MAYBE_RETURN(result, ReadOnlyRoots(isolate).exception());
314   return isolate->heap()->ToBoolean(result.FromJust());
315 }
316 
BUILTIN(ObjectGetOwnPropertyDescriptors)317 BUILTIN(ObjectGetOwnPropertyDescriptors) {
318   HandleScope scope(isolate);
319   Handle<Object> object = args.atOrUndefined(isolate, 1);
320 
321   Handle<JSReceiver> receiver;
322   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, receiver,
323                                      Object::ToObject(isolate, object));
324 
325   Handle<FixedArray> keys;
326   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
327       isolate, keys, KeyAccumulator::GetKeys(
328                          receiver, KeyCollectionMode::kOwnOnly, ALL_PROPERTIES,
329                          GetKeysConversion::kConvertToString));
330 
331   Handle<JSObject> descriptors =
332       isolate->factory()->NewJSObject(isolate->object_function());
333 
334   for (int i = 0; i < keys->length(); ++i) {
335     Handle<Name> key = Handle<Name>::cast(FixedArray::get(*keys, i, isolate));
336     PropertyDescriptor descriptor;
337     Maybe<bool> did_get_descriptor = JSReceiver::GetOwnPropertyDescriptor(
338         isolate, receiver, key, &descriptor);
339     MAYBE_RETURN(did_get_descriptor, ReadOnlyRoots(isolate).exception());
340 
341     if (!did_get_descriptor.FromJust()) continue;
342     Handle<Object> from_descriptor = descriptor.ToObject(isolate);
343 
344     Maybe<bool> success = JSReceiver::CreateDataProperty(
345         isolate, descriptors, key, from_descriptor, Just(kDontThrow));
346     CHECK(success.FromJust());
347   }
348 
349   return *descriptors;
350 }
351 
352 // ES6 section 19.1.2.17 Object.seal ( O )
BUILTIN(ObjectSeal)353 BUILTIN(ObjectSeal) {
354   HandleScope scope(isolate);
355   Handle<Object> object = args.atOrUndefined(isolate, 1);
356   if (object->IsJSReceiver()) {
357     MAYBE_RETURN(JSReceiver::SetIntegrityLevel(Handle<JSReceiver>::cast(object),
358                                                SEALED, kThrowOnError),
359                  ReadOnlyRoots(isolate).exception());
360   }
361   return *object;
362 }
363 
364 }  // namespace internal
365 }  // namespace v8
366