V8 API Reference, 7.2.502.16 (for Deno 0.2.4)
property-descriptor.cc
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/property-descriptor.h"
6 
7 #include "src/bootstrapper.h"
8 #include "src/heap/factory.h"
9 #include "src/isolate-inl.h"
10 #include "src/lookup.h"
11 #include "src/objects-inl.h"
12 #include "src/objects/property-descriptor-object-inl.h"
13 
14 namespace v8 {
15 namespace internal {
16 
17 namespace {
18 
19 // Helper function for ToPropertyDescriptor. Comments describe steps for
20 // "enumerable", other properties are handled the same way.
21 // Returns false if an exception was thrown.
22 bool GetPropertyIfPresent(Handle<JSReceiver> receiver, Handle<String> name,
23  Handle<Object>* value) {
24  LookupIterator it(receiver, name, receiver);
25  // 4. Let hasEnumerable be HasProperty(Obj, "enumerable").
26  Maybe<bool> has_property = JSReceiver::HasProperty(&it);
27  // 5. ReturnIfAbrupt(hasEnumerable).
28  if (has_property.IsNothing()) return false;
29  // 6. If hasEnumerable is true, then
30  if (has_property.FromJust() == true) {
31  // 6a. Let enum be ToBoolean(Get(Obj, "enumerable")).
32  // 6b. ReturnIfAbrupt(enum).
33  if (!Object::GetProperty(&it).ToHandle(value)) return false;
34  }
35  return true;
36 }
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!
43 bool ToPropertyDescriptorFastPath(Isolate* isolate, Handle<JSReceiver> obj,
44  PropertyDescriptor* desc) {
45  if (!obj->IsJSObject()) return false;
46  Map map = Handle<JSObject>::cast(obj)->map();
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);
61  for (int i = 0; i < map->NumberOfOwnDescriptors(); i++) {
62  PropertyDetails details = descs->GetDetails(i);
63  Name key = descs->GetKey(i);
64  Handle<Object> value;
65  if (details.location() == kField) {
66  if (details.kind() == kData) {
67  value = JSObject::FastPropertyAt(Handle<JSObject>::cast(obj),
68  details.representation(),
69  FieldIndex::ForDescriptor(map, i));
70  } else {
71  DCHECK_EQ(kAccessor, details.kind());
72  // Bail out to slow path.
73  return false;
74  }
75 
76  } else {
77  DCHECK_EQ(kDescriptor, details.location());
78  if (details.kind() == kData) {
79  value = handle(descs->GetStrongValue(i), isolate);
80  } else {
81  DCHECK_EQ(kAccessor, details.kind());
82  // Bail out to slow path.
83  return false;
84  }
85  }
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 
113 void CreateDataProperty(Handle<JSObject> object, Handle<String> name,
114  Handle<Object> value) {
115  LookupIterator it(object, name, object, LookupIterator::OWN_SKIP_INTERCEPTOR);
116  Maybe<bool> result = JSObject::CreateDataProperty(&it, value);
117  CHECK(result.IsJust() && result.FromJust());
118 }
119 
120 } // namespace
121 
122 
123 // ES6 6.2.4.4 "FromPropertyDescriptor"
124 Handle<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 
184 // ES6 6.2.4.5
185 // Returns false in case of exception.
186 // static
187 bool PropertyDescriptor::ToPropertyDescriptor(Isolate* isolate,
188  Handle<Object> obj,
189  PropertyDescriptor* desc) {
190  // 1. ReturnIfAbrupt(Obj).
191  // 2. If Type(Obj) is not Object, throw a TypeError exception.
192  if (!obj->IsJSReceiver()) {
193  isolate->Throw(*isolate->factory()->NewTypeError(
194  MessageTemplate::kPropertyDescObject, obj));
195  return false;
196  }
197  // 3. Let desc be a new Property Descriptor that initially has no fields.
198  DCHECK(desc->is_empty());
199 
200  Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(obj);
201  if (ToPropertyDescriptorFastPath(isolate, receiver, desc)) {
202  return true;
203  }
204 
205  // enumerable?
206  Handle<Object> enumerable;
207  // 4 through 6b.
208  if (!GetPropertyIfPresent(receiver, isolate->factory()->enumerable_string(),
209  &enumerable)) {
210  return false;
211  }
212  // 6c. Set the [[Enumerable]] field of desc to enum.
213  if (!enumerable.is_null()) {
214  desc->set_enumerable(enumerable->BooleanValue(isolate));
215  }
216 
217  // configurable?
218  Handle<Object> configurable;
219  // 7 through 9b.
220  if (!GetPropertyIfPresent(receiver, isolate->factory()->configurable_string(),
221  &configurable)) {
222  return false;
223  }
224  // 9c. Set the [[Configurable]] field of desc to conf.
225  if (!configurable.is_null()) {
226  desc->set_configurable(configurable->BooleanValue(isolate));
227  }
228 
229  // value?
230  Handle<Object> value;
231  // 10 through 12b.
232  if (!GetPropertyIfPresent(receiver, isolate->factory()->value_string(),
233  &value)) {
234  return false;
235  }
236  // 12c. Set the [[Value]] field of desc to value.
237  if (!value.is_null()) desc->set_value(value);
238 
239  // writable?
240  Handle<Object> writable;
241  // 13 through 15b.
242  if (!GetPropertyIfPresent(receiver, isolate->factory()->writable_string(),
243  &writable)) {
244  return false;
245  }
246  // 15c. Set the [[Writable]] field of desc to writable.
247  if (!writable.is_null()) desc->set_writable(writable->BooleanValue(isolate));
248 
249  // getter?
250  Handle<Object> getter;
251  // 16 through 18b.
252  if (!GetPropertyIfPresent(receiver, isolate->factory()->get_string(),
253  &getter)) {
254  return false;
255  }
256  if (!getter.is_null()) {
257  // 18c. If IsCallable(getter) is false and getter is not undefined,
258  // throw a TypeError exception.
259  if (!getter->IsCallable() && !getter->IsUndefined(isolate)) {
260  isolate->Throw(*isolate->factory()->NewTypeError(
261  MessageTemplate::kObjectGetterCallable, getter));
262  return false;
263  }
264  // 18d. Set the [[Get]] field of desc to getter.
265  desc->set_get(getter);
266  }
267  // setter?
268  Handle<Object> setter;
269  // 19 through 21b.
270  if (!GetPropertyIfPresent(receiver, isolate->factory()->set_string(),
271  &setter)) {
272  return false;
273  }
274  if (!setter.is_null()) {
275  // 21c. If IsCallable(setter) is false and setter is not undefined,
276  // throw a TypeError exception.
277  if (!setter->IsCallable() && !setter->IsUndefined(isolate)) {
278  isolate->Throw(*isolate->factory()->NewTypeError(
279  MessageTemplate::kObjectSetterCallable, setter));
280  return false;
281  }
282  // 21d. Set the [[Set]] field of desc to setter.
283  desc->set_set(setter);
284  }
285 
286  // 22. If either desc.[[Get]] or desc.[[Set]] is present, then
287  // 22a. If either desc.[[Value]] or desc.[[Writable]] is present,
288  // throw a TypeError exception.
289  if ((desc->has_get() || desc->has_set()) &&
290  (desc->has_value() || desc->has_writable())) {
291  isolate->Throw(*isolate->factory()->NewTypeError(
292  MessageTemplate::kValueAndAccessor, obj));
293  return false;
294  }
295 
296  // 23. Return desc.
297  return true;
298 }
299 
300 
301 // ES6 6.2.4.6
302 // static
303 void PropertyDescriptor::CompletePropertyDescriptor(Isolate* isolate,
304  PropertyDescriptor* desc) {
305  // 1. ReturnIfAbrupt(Desc).
306  // 2. Assert: Desc is a Property Descriptor.
307  // 3. Let like be Record{
308  // [[Value]]: undefined, [[Writable]]: false,
309  // [[Get]]: undefined, [[Set]]: undefined,
310  // [[Enumerable]]: false, [[Configurable]]: false}.
311  // 4. If either IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true,
312  // then:
313  if (!IsAccessorDescriptor(desc)) {
314  // 4a. If Desc does not have a [[Value]] field, set Desc.[[Value]] to
315  // like.[[Value]].
316  if (!desc->has_value()) {
317  desc->set_value(isolate->factory()->undefined_value());
318  }
319  // 4b. If Desc does not have a [[Writable]] field, set Desc.[[Writable]]
320  // to like.[[Writable]].
321  if (!desc->has_writable()) desc->set_writable(false);
322  } else {
323  // 5. Else,
324  // 5a. If Desc does not have a [[Get]] field, set Desc.[[Get]] to
325  // like.[[Get]].
326  if (!desc->has_get()) {
327  desc->set_get(isolate->factory()->undefined_value());
328  }
329  // 5b. If Desc does not have a [[Set]] field, set Desc.[[Set]] to
330  // like.[[Set]].
331  if (!desc->has_set()) {
332  desc->set_set(isolate->factory()->undefined_value());
333  }
334  }
335  // 6. If Desc does not have an [[Enumerable]] field, set
336  // Desc.[[Enumerable]] to like.[[Enumerable]].
337  if (!desc->has_enumerable()) desc->set_enumerable(false);
338  // 7. If Desc does not have a [[Configurable]] field, set
339  // Desc.[[Configurable]] to like.[[Configurable]].
340  if (!desc->has_configurable()) desc->set_configurable(false);
341  // 8. Return Desc.
342 }
343 
344 Handle<PropertyDescriptorObject> PropertyDescriptor::ToPropertyDescriptorObject(
345  Isolate* isolate) {
346  Handle<PropertyDescriptorObject> obj = Handle<PropertyDescriptorObject>::cast(
347  isolate->factory()->NewFixedArray(PropertyDescriptorObject::kLength));
348 
349  int flags =
350  PropertyDescriptorObject::IsEnumerableBit::encode(enumerable_) |
351  PropertyDescriptorObject::HasEnumerableBit::encode(has_enumerable_) |
352  PropertyDescriptorObject::IsConfigurableBit::encode(configurable_) |
353  PropertyDescriptorObject::HasConfigurableBit::encode(has_configurable_) |
354  PropertyDescriptorObject::IsWritableBit::encode(writable_) |
355  PropertyDescriptorObject::HasWritableBit::encode(has_writable_) |
356  PropertyDescriptorObject::HasValueBit::encode(has_value()) |
357  PropertyDescriptorObject::HasGetBit::encode(has_get()) |
358  PropertyDescriptorObject::HasSetBit::encode(has_set());
359 
360  obj->set(PropertyDescriptorObject::kFlagsIndex, Smi::FromInt(flags));
361 
362  obj->set(PropertyDescriptorObject::kValueIndex,
363  has_value() ? *value_ : ReadOnlyRoots(isolate).the_hole_value());
364  obj->set(PropertyDescriptorObject::kGetIndex,
365  has_get() ? *get_ : ReadOnlyRoots(isolate).the_hole_value());
366  obj->set(PropertyDescriptorObject::kSetIndex,
367  has_set() ? *set_ : ReadOnlyRoots(isolate).the_hole_value());
368 
369  return obj;
370 }
371 
372 } // namespace internal
373 } // namespace v8
Definition: libplatform.h:13