1// Copyright 2011 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/objects/property-descriptor.h"
6
7#include "src/execution/isolate-inl.h"
8#include "src/heap/factory.h"
9#include "src/heap/heap-inl.h"  // For ToBoolean. TODO(jkummerow): Drop.
10#include "src/init/bootstrapper.h"
11#include "src/objects/lookup.h"
12#include "src/objects/objects-inl.h"
13#include "src/objects/property-descriptor-object-inl.h"
14
15namespace v8 {
16namespace internal {
17
18namespace {
19
20// Helper function for ToPropertyDescriptor. Comments describe steps for
21// "enumerable", other properties are handled the same way.
22// Returns false if an exception was thrown.
23bool GetPropertyIfPresent(Handle<JSReceiver> receiver, Handle<String> name,
24                          Handle<Object>* value) {
25  LookupIterator it(receiver->GetIsolate(), receiver, name, receiver);
26  // 4. Let hasEnumerable be HasProperty(Obj, "enumerable").
27  Maybe<bool> has_property = JSReceiver::HasProperty(&it);
28  // 5. ReturnIfAbrupt(hasEnumerable).
29  if (has_property.IsNothing()) return false;
30  // 6. If hasEnumerable is true, then
31  if (has_property.FromJust() == true) {
32    // 6a. Let enum be ToBoolean(Get(Obj, "enumerable")).
33    // 6b. ReturnIfAbrupt(enum).
34    if (!Object::GetProperty(&it).ToHandle(value)) return false;
35  }
36  return true;
37}
38
39// Helper function for ToPropertyDescriptor. Handles the case of "simple"
40// objects: nothing on the prototype chain, just own fast data properties.
41// Must not have observable side effects, because the slow path will restart
42// the entire conversion!
43bool ToPropertyDescriptorFastPath(Isolate* isolate, Handle<JSReceiver> obj,
44                                  PropertyDescriptor* desc) {
45  if (!obj->IsJSObject()) return false;
46  Handle<Map> map(Handle<JSObject>::cast(obj)->map(), isolate);
47  if (map->instance_type() != JS_OBJECT_TYPE) return false;
48  if (map->is_access_check_needed()) return false;
49  if (map->prototype() != *isolate->initial_object_prototype()) return false;
50  // During bootstrapping, the object_function_prototype_map hasn't been
51  // set up yet.
52  if (isolate->bootstrapper()->IsActive()) return false;
53  if (JSObject::cast(map->prototype()).map() !=
54      isolate->native_context()->object_function_prototype_map()) {
55    return false;
56  }
57  // TODO(jkummerow): support dictionary properties?
58  if (map->is_dictionary_map()) return false;
59  Handle<DescriptorArray> descs =
60      Handle<DescriptorArray>(map->instance_descriptors(isolate), isolate);
61  for (InternalIndex i : map->IterateOwnDescriptors()) {
62    PropertyDetails details = descs->GetDetails(i);
63    Handle<Object> value;
64    if (details.location() == PropertyLocation::kField) {
65      if (details.kind() == PropertyKind::kData) {
66        value = JSObject::FastPropertyAt(isolate, Handle<JSObject>::cast(obj),
67                                         details.representation(),
68                                         FieldIndex::ForDescriptor(*map, i));
69      } else {
70        DCHECK_EQ(PropertyKind::kAccessor, details.kind());
71        // Bail out to slow path.
72        return false;
73      }
74
75    } else {
76      DCHECK_EQ(PropertyLocation::kDescriptor, details.location());
77      if (details.kind() == PropertyKind::kData) {
78        value = handle(descs->GetStrongValue(i), isolate);
79      } else {
80        DCHECK_EQ(PropertyKind::kAccessor, details.kind());
81        // Bail out to slow path.
82        return false;
83      }
84    }
85    Name key = descs->GetKey(i);
86    ReadOnlyRoots roots(isolate);
87    if (key == roots.enumerable_string()) {
88      desc->set_enumerable(value->BooleanValue(isolate));
89    } else if (key == roots.configurable_string()) {
90      desc->set_configurable(value->BooleanValue(isolate));
91    } else if (key == roots.value_string()) {
92      desc->set_value(value);
93    } else if (key == roots.writable_string()) {
94      desc->set_writable(value->BooleanValue(isolate));
95    } else if (key == roots.get_string()) {
96      // Bail out to slow path to throw an exception if necessary.
97      if (!value->IsCallable()) return false;
98      desc->set_get(value);
99    } else if (key == roots.set_string()) {
100      // Bail out to slow path to throw an exception if necessary.
101      if (!value->IsCallable()) return false;
102      desc->set_set(value);
103    }
104  }
105  if ((desc->has_get() || desc->has_set()) &&
106      (desc->has_value() || desc->has_writable())) {
107    // Bail out to slow path to throw an exception.
108    return false;
109  }
110  return true;
111}
112
113void CreateDataProperty(Handle<JSObject> object, Handle<String> name,
114                        Handle<Object> value) {
115  LookupIterator it(object->GetIsolate(), object, name, object,
116                    LookupIterator::OWN_SKIP_INTERCEPTOR);
117  Maybe<bool> result = JSObject::CreateDataProperty(&it, value);
118  CHECK(result.IsJust() && result.FromJust());
119}
120
121}  // namespace
122
123// ES6 6.2.4.4 "FromPropertyDescriptor"
124Handle<Object> PropertyDescriptor::ToObject(Isolate* isolate) {
125  DCHECK(!(PropertyDescriptor::IsAccessorDescriptor(this) &&
126           PropertyDescriptor::IsDataDescriptor(this)));
127  Factory* factory = isolate->factory();
128  if (IsRegularAccessorProperty()) {
129    // Fast case for regular accessor properties.
130    Handle<JSObject> result = factory->NewJSObjectFromMap(
131        isolate->accessor_property_descriptor_map());
132    result->InObjectPropertyAtPut(JSAccessorPropertyDescriptor::kGetIndex,
133                                  *get());
134    result->InObjectPropertyAtPut(JSAccessorPropertyDescriptor::kSetIndex,
135                                  *set());
136    result->InObjectPropertyAtPut(
137        JSAccessorPropertyDescriptor::kEnumerableIndex,
138        isolate->heap()->ToBoolean(enumerable()));
139    result->InObjectPropertyAtPut(
140        JSAccessorPropertyDescriptor::kConfigurableIndex,
141        isolate->heap()->ToBoolean(configurable()));
142    return result;
143  }
144  if (IsRegularDataProperty()) {
145    // Fast case for regular data properties.
146    Handle<JSObject> result =
147        factory->NewJSObjectFromMap(isolate->data_property_descriptor_map());
148    result->InObjectPropertyAtPut(JSDataPropertyDescriptor::kValueIndex,
149                                  *value());
150    result->InObjectPropertyAtPut(JSDataPropertyDescriptor::kWritableIndex,
151                                  isolate->heap()->ToBoolean(writable()));
152    result->InObjectPropertyAtPut(JSDataPropertyDescriptor::kEnumerableIndex,
153                                  isolate->heap()->ToBoolean(enumerable()));
154    result->InObjectPropertyAtPut(JSDataPropertyDescriptor::kConfigurableIndex,
155                                  isolate->heap()->ToBoolean(configurable()));
156    return result;
157  }
158  Handle<JSObject> result = factory->NewJSObject(isolate->object_function());
159  if (has_value()) {
160    CreateDataProperty(result, factory->value_string(), value());
161  }
162  if (has_writable()) {
163    CreateDataProperty(result, factory->writable_string(),
164                       factory->ToBoolean(writable()));
165  }
166  if (has_get()) {
167    CreateDataProperty(result, factory->get_string(), get());
168  }
169  if (has_set()) {
170    CreateDataProperty(result, factory->set_string(), set());
171  }
172  if (has_enumerable()) {
173    CreateDataProperty(result, factory->enumerable_string(),
174                       factory->ToBoolean(enumerable()));
175  }
176  if (has_configurable()) {
177    CreateDataProperty(result, factory->configurable_string(),
178                       factory->ToBoolean(configurable()));
179  }
180  return result;
181}
182
183// ES6 6.2.4.5
184// Returns false in case of exception.
185// static
186bool PropertyDescriptor::ToPropertyDescriptor(Isolate* isolate,
187                                              Handle<Object> obj,
188                                              PropertyDescriptor* desc) {
189  // 1. ReturnIfAbrupt(Obj).
190  // 2. If Type(Obj) is not Object, throw a TypeError exception.
191  if (!obj->IsJSReceiver()) {
192    isolate->Throw(*isolate->factory()->NewTypeError(
193        MessageTemplate::kPropertyDescObject, obj));
194    return false;
195  }
196  // 3. Let desc be a new Property Descriptor that initially has no fields.
197  DCHECK(desc->is_empty());
198
199  Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(obj);
200  if (ToPropertyDescriptorFastPath(isolate, receiver, desc)) {
201    return true;
202  }
203
204  // enumerable?
205  Handle<Object> enumerable;
206  // 4 through 6b.
207  if (!GetPropertyIfPresent(receiver, isolate->factory()->enumerable_string(),
208                            &enumerable)) {
209    return false;
210  }
211  // 6c. Set the [[Enumerable]] field of desc to enum.
212  if (!enumerable.is_null()) {
213    desc->set_enumerable(enumerable->BooleanValue(isolate));
214  }
215
216  // configurable?
217  Handle<Object> configurable;
218  // 7 through 9b.
219  if (!GetPropertyIfPresent(receiver, isolate->factory()->configurable_string(),
220                            &configurable)) {
221    return false;
222  }
223  // 9c. Set the [[Configurable]] field of desc to conf.
224  if (!configurable.is_null()) {
225    desc->set_configurable(configurable->BooleanValue(isolate));
226  }
227
228  // value?
229  Handle<Object> value;
230  // 10 through 12b.
231  if (!GetPropertyIfPresent(receiver, isolate->factory()->value_string(),
232                            &value)) {
233    return false;
234  }
235  // 12c. Set the [[Value]] field of desc to value.
236  if (!value.is_null()) desc->set_value(value);
237
238  // writable?
239  Handle<Object> writable;
240  // 13 through 15b.
241  if (!GetPropertyIfPresent(receiver, isolate->factory()->writable_string(),
242                            &writable)) {
243    return false;
244  }
245  // 15c. Set the [[Writable]] field of desc to writable.
246  if (!writable.is_null()) desc->set_writable(writable->BooleanValue(isolate));
247
248  // getter?
249  Handle<Object> getter;
250  // 16 through 18b.
251  if (!GetPropertyIfPresent(receiver, isolate->factory()->get_string(),
252                            &getter)) {
253    return false;
254  }
255  if (!getter.is_null()) {
256    // 18c. If IsCallable(getter) is false and getter is not undefined,
257    // throw a TypeError exception.
258    if (!getter->IsCallable() && !getter->IsUndefined(isolate)) {
259      isolate->Throw(*isolate->factory()->NewTypeError(
260          MessageTemplate::kObjectGetterCallable, getter));
261      return false;
262    }
263    // 18d. Set the [[Get]] field of desc to getter.
264    desc->set_get(getter);
265  }
266  // setter?
267  Handle<Object> setter;
268  // 19 through 21b.
269  if (!GetPropertyIfPresent(receiver, isolate->factory()->set_string(),
270                            &setter)) {
271    return false;
272  }
273  if (!setter.is_null()) {
274    // 21c. If IsCallable(setter) is false and setter is not undefined,
275    // throw a TypeError exception.
276    if (!setter->IsCallable() && !setter->IsUndefined(isolate)) {
277      isolate->Throw(*isolate->factory()->NewTypeError(
278          MessageTemplate::kObjectSetterCallable, setter));
279      return false;
280    }
281    // 21d. Set the [[Set]] field of desc to setter.
282    desc->set_set(setter);
283  }
284
285  // 22. If either desc.[[Get]] or desc.[[Set]] is present, then
286  // 22a. If either desc.[[Value]] or desc.[[Writable]] is present,
287  // throw a TypeError exception.
288  if ((desc->has_get() || desc->has_set()) &&
289      (desc->has_value() || desc->has_writable())) {
290    isolate->Throw(*isolate->factory()->NewTypeError(
291        MessageTemplate::kValueAndAccessor, obj));
292    return false;
293  }
294
295  // 23. Return desc.
296  return true;
297}
298
299// ES6 6.2.4.6
300// static
301void PropertyDescriptor::CompletePropertyDescriptor(Isolate* isolate,
302                                                    PropertyDescriptor* desc) {
303  // 1. ReturnIfAbrupt(Desc).
304  // 2. Assert: Desc is a Property Descriptor.
305  // 3. Let like be Record{
306  //        [[Value]]: undefined, [[Writable]]: false,
307  //        [[Get]]: undefined, [[Set]]: undefined,
308  //        [[Enumerable]]: false, [[Configurable]]: false}.
309  // 4. If either IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true,
310  // then:
311  if (!IsAccessorDescriptor(desc)) {
312    // 4a. If Desc does not have a [[Value]] field, set Desc.[[Value]] to
313    //     like.[[Value]].
314    if (!desc->has_value()) {
315      desc->set_value(isolate->factory()->undefined_value());
316    }
317    // 4b. If Desc does not have a [[Writable]] field, set Desc.[[Writable]]
318    //     to like.[[Writable]].
319    if (!desc->has_writable()) desc->set_writable(false);
320  } else {
321    // 5. Else,
322    // 5a. If Desc does not have a [[Get]] field, set Desc.[[Get]] to
323    //     like.[[Get]].
324    if (!desc->has_get()) {
325      desc->set_get(isolate->factory()->undefined_value());
326    }
327    // 5b. If Desc does not have a [[Set]] field, set Desc.[[Set]] to
328    //     like.[[Set]].
329    if (!desc->has_set()) {
330      desc->set_set(isolate->factory()->undefined_value());
331    }
332  }
333  // 6. If Desc does not have an [[Enumerable]] field, set
334  //    Desc.[[Enumerable]] to like.[[Enumerable]].
335  if (!desc->has_enumerable()) desc->set_enumerable(false);
336  // 7. If Desc does not have a [[Configurable]] field, set
337  //    Desc.[[Configurable]] to like.[[Configurable]].
338  if (!desc->has_configurable()) desc->set_configurable(false);
339  // 8. Return Desc.
340}
341
342Handle<PropertyDescriptorObject> PropertyDescriptor::ToPropertyDescriptorObject(
343    Isolate* isolate) {
344  Handle<PropertyDescriptorObject> obj =
345      isolate->factory()->NewPropertyDescriptorObject();
346
347  int flags =
348      PropertyDescriptorObject::IsEnumerableBit::encode(enumerable_) |
349      PropertyDescriptorObject::HasEnumerableBit::encode(has_enumerable_) |
350      PropertyDescriptorObject::IsConfigurableBit::encode(configurable_) |
351      PropertyDescriptorObject::HasConfigurableBit::encode(has_configurable_) |
352      PropertyDescriptorObject::IsWritableBit::encode(writable_) |
353      PropertyDescriptorObject::HasWritableBit::encode(has_writable_) |
354      PropertyDescriptorObject::HasValueBit::encode(has_value()) |
355      PropertyDescriptorObject::HasGetBit::encode(has_get()) |
356      PropertyDescriptorObject::HasSetBit::encode(has_set());
357
358  obj->set_flags(flags);
359
360  if (has_value()) obj->set_value(*value_);
361  if (has_get()) obj->set_get(*get_);
362  if (has_set()) obj->set_set(*set_);
363
364  return obj;
365}
366
367}  // namespace internal
368}  // namespace v8
369