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
16namespace v8 {
17namespace internal {
18
19// -----------------------------------------------------------------------------
20// ES6 section 19.1 Object Objects
21
22// ES6 section 19.1.3.4 Object.prototype.propertyIsEnumerable ( V )
23BUILTIN(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
39BUILTIN(ObjectDefineProperties) {
40  HandleScope scope(isolate);
41  DCHECK_LE(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
50BUILTIN(ObjectDefineProperty) {
51  HandleScope scope(isolate);
52  DCHECK_LE(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
60namespace {
61
62template <AccessorComponent which_accessor>
63Object 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
104Object 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): PropertyKey(..., 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  PropertyKey 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 = it.GetHolder<JSReceiver>()
161                                                     ->GetCreationContext()
162                                                     .ToHandleChecked();
163          return *AccessorPair::GetComponent(
164              isolate, native_context, Handle<AccessorPair>::cast(maybe_pair),
165              component);
166        }
167      }
168    }
169  }
170
171  return ReadOnlyRoots(isolate).undefined_value();
172}
173
174}  // namespace
175
176// ES6 B.2.2.2 a.k.a.
177// https://tc39.github.io/ecma262/#sec-object.prototype.__defineGetter__
178BUILTIN(ObjectDefineGetter) {
179  HandleScope scope(isolate);
180  Handle<Object> object = args.at(0);  // Receiver.
181  Handle<Object> name = args.at(1);
182  Handle<Object> getter = args.at(2);
183  return ObjectDefineAccessor<ACCESSOR_GETTER>(isolate, object, name, getter);
184}
185
186// ES6 B.2.2.3 a.k.a.
187// https://tc39.github.io/ecma262/#sec-object.prototype.__defineSetter__
188BUILTIN(ObjectDefineSetter) {
189  HandleScope scope(isolate);
190  Handle<Object> object = args.at(0);  // Receiver.
191  Handle<Object> name = args.at(1);
192  Handle<Object> setter = args.at(2);
193  return ObjectDefineAccessor<ACCESSOR_SETTER>(isolate, object, name, setter);
194}
195
196// ES6 B.2.2.4 a.k.a.
197// https://tc39.github.io/ecma262/#sec-object.prototype.__lookupGetter__
198BUILTIN(ObjectLookupGetter) {
199  HandleScope scope(isolate);
200  Handle<Object> object = args.at(0);
201  Handle<Object> name = args.at(1);
202  return ObjectLookupAccessor(isolate, object, name, ACCESSOR_GETTER);
203}
204
205// ES6 B.2.2.5 a.k.a.
206// https://tc39.github.io/ecma262/#sec-object.prototype.__lookupSetter__
207BUILTIN(ObjectLookupSetter) {
208  HandleScope scope(isolate);
209  Handle<Object> object = args.at(0);
210  Handle<Object> name = args.at(1);
211  return ObjectLookupAccessor(isolate, object, name, ACCESSOR_SETTER);
212}
213
214// ES6 section 19.1.2.5 Object.freeze ( O )
215BUILTIN(ObjectFreeze) {
216  HandleScope scope(isolate);
217  Handle<Object> object = args.atOrUndefined(isolate, 1);
218  if (object->IsJSReceiver()) {
219    MAYBE_RETURN(JSReceiver::SetIntegrityLevel(Handle<JSReceiver>::cast(object),
220                                               FROZEN, kThrowOnError),
221                 ReadOnlyRoots(isolate).exception());
222  }
223  return *object;
224}
225
226// ES6 section B.2.2.1.1 get Object.prototype.__proto__
227BUILTIN(ObjectPrototypeGetProto) {
228  HandleScope scope(isolate);
229  // 1. Let O be ? ToObject(this value).
230  Handle<JSReceiver> receiver;
231  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
232      isolate, receiver, Object::ToObject(isolate, args.receiver()));
233
234  // 2. Return ? O.[[GetPrototypeOf]]().
235  RETURN_RESULT_OR_FAILURE(isolate,
236                           JSReceiver::GetPrototype(isolate, receiver));
237}
238
239// ES6 section B.2.2.1.2 set Object.prototype.__proto__
240BUILTIN(ObjectPrototypeSetProto) {
241  HandleScope scope(isolate);
242  // 1. Let O be ? RequireObjectCoercible(this value).
243  Handle<Object> object = args.receiver();
244  if (object->IsNullOrUndefined(isolate)) {
245    THROW_NEW_ERROR_RETURN_FAILURE(
246        isolate, NewTypeError(MessageTemplate::kCalledOnNullOrUndefined,
247                              isolate->factory()->NewStringFromAsciiChecked(
248                                  "set Object.prototype.__proto__")));
249  }
250
251  // 2. If Type(proto) is neither Object nor Null, return undefined.
252  Handle<Object> proto = args.at(1);
253  if (!proto->IsNull(isolate) && !proto->IsJSReceiver()) {
254    return ReadOnlyRoots(isolate).undefined_value();
255  }
256
257  // 3. If Type(O) is not Object, return undefined.
258  if (!object->IsJSReceiver()) return ReadOnlyRoots(isolate).undefined_value();
259  Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(object);
260
261  // 4. Let status be ? O.[[SetPrototypeOf]](proto).
262  // 5. If status is false, throw a TypeError exception.
263  MAYBE_RETURN(
264      JSReceiver::SetPrototype(isolate, receiver, proto, true, kThrowOnError),
265      ReadOnlyRoots(isolate).exception());
266
267  // Return undefined.
268  return ReadOnlyRoots(isolate).undefined_value();
269}
270
271namespace {
272
273Object GetOwnPropertyKeys(Isolate* isolate, BuiltinArguments args,
274                          PropertyFilter filter) {
275  HandleScope scope(isolate);
276  Handle<Object> object = args.atOrUndefined(isolate, 1);
277  Handle<JSReceiver> receiver;
278  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, receiver,
279                                     Object::ToObject(isolate, object));
280  Handle<FixedArray> keys;
281  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
282      isolate, keys,
283      KeyAccumulator::GetKeys(receiver, KeyCollectionMode::kOwnOnly, filter,
284                              GetKeysConversion::kConvertToString));
285  return *isolate->factory()->NewJSArrayWithElements(keys);
286}
287
288}  // namespace
289
290// ES6 section 19.1.2.8 Object.getOwnPropertySymbols ( O )
291BUILTIN(ObjectGetOwnPropertySymbols) {
292  return GetOwnPropertyKeys(isolate, args, SKIP_STRINGS);
293}
294
295// ES6 section 19.1.2.12 Object.isFrozen ( O )
296BUILTIN(ObjectIsFrozen) {
297  HandleScope scope(isolate);
298  Handle<Object> object = args.atOrUndefined(isolate, 1);
299  Maybe<bool> result = object->IsJSReceiver()
300                           ? JSReceiver::TestIntegrityLevel(
301                                 Handle<JSReceiver>::cast(object), FROZEN)
302                           : Just(true);
303  MAYBE_RETURN(result, ReadOnlyRoots(isolate).exception());
304  return isolate->heap()->ToBoolean(result.FromJust());
305}
306
307// ES6 section 19.1.2.13 Object.isSealed ( O )
308BUILTIN(ObjectIsSealed) {
309  HandleScope scope(isolate);
310  Handle<Object> object = args.atOrUndefined(isolate, 1);
311  Maybe<bool> result = object->IsJSReceiver()
312                           ? JSReceiver::TestIntegrityLevel(
313                                 Handle<JSReceiver>::cast(object), SEALED)
314                           : Just(true);
315  MAYBE_RETURN(result, ReadOnlyRoots(isolate).exception());
316  return isolate->heap()->ToBoolean(result.FromJust());
317}
318
319BUILTIN(ObjectGetOwnPropertyDescriptors) {
320  HandleScope scope(isolate);
321  Handle<Object> object = args.atOrUndefined(isolate, 1);
322
323  Handle<JSReceiver> receiver;
324  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, receiver,
325                                     Object::ToObject(isolate, object));
326
327  Handle<FixedArray> keys;
328  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
329      isolate, keys, KeyAccumulator::GetKeys(
330                         receiver, KeyCollectionMode::kOwnOnly, ALL_PROPERTIES,
331                         GetKeysConversion::kConvertToString));
332
333  Handle<JSObject> descriptors =
334      isolate->factory()->NewJSObject(isolate->object_function());
335
336  for (int i = 0; i < keys->length(); ++i) {
337    Handle<Name> key = Handle<Name>::cast(FixedArray::get(*keys, i, isolate));
338    PropertyDescriptor descriptor;
339    Maybe<bool> did_get_descriptor = JSReceiver::GetOwnPropertyDescriptor(
340        isolate, receiver, key, &descriptor);
341    MAYBE_RETURN(did_get_descriptor, ReadOnlyRoots(isolate).exception());
342
343    if (!did_get_descriptor.FromJust()) continue;
344    Handle<Object> from_descriptor = descriptor.ToObject(isolate);
345
346    Maybe<bool> success = JSReceiver::CreateDataProperty(
347        isolate, descriptors, key, from_descriptor, Just(kDontThrow));
348    CHECK(success.FromJust());
349  }
350
351  return *descriptors;
352}
353
354// ES6 section 19.1.2.17 Object.seal ( O )
355BUILTIN(ObjectSeal) {
356  HandleScope scope(isolate);
357  Handle<Object> object = args.atOrUndefined(isolate, 1);
358  if (object->IsJSReceiver()) {
359    MAYBE_RETURN(JSReceiver::SetIntegrityLevel(Handle<JSReceiver>::cast(object),
360                                               SEALED, kThrowOnError),
361                 ReadOnlyRoots(isolate).exception());
362  }
363  return *object;
364}
365
366}  // namespace internal
367}  // namespace v8
368