• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
15 namespace v8 {
16 namespace internal {
17 
18 namespace {
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.
GetPropertyIfPresent(Handle<JSReceiver> receiver,Handle<String> name,Handle<Object> * value)23 bool 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!
ToPropertyDescriptorFastPath(Isolate * isolate,Handle<JSReceiver> obj,PropertyDescriptor * desc)43 bool 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(kRelaxedLoad), isolate);
61   for (InternalIndex i : map->IterateOwnDescriptors()) {
62     PropertyDetails details = descs->GetDetails(i);
63     Handle<Object> value;
64     if (details.location() == kField) {
65       if (details.kind() == kData) {
66         value = JSObject::FastPropertyAt(Handle<JSObject>::cast(obj),
67                                          details.representation(),
68                                          FieldIndex::ForDescriptor(*map, i));
69       } else {
70         DCHECK_EQ(kAccessor, details.kind());
71         // Bail out to slow path.
72         return false;
73       }
74 
75     } else {
76       DCHECK_EQ(kDescriptor, details.location());
77       if (details.kind() == kData) {
78         value = handle(descs->GetStrongValue(i), isolate);
79       } else {
80         DCHECK_EQ(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 
CreateDataProperty(Handle<JSObject> object,Handle<String> name,Handle<Object> value)113 void 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"
ToObject(Isolate * isolate)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 // ES6 6.2.4.5
184 // Returns false in case of exception.
185 // static
ToPropertyDescriptor(Isolate * isolate,Handle<Object> obj,PropertyDescriptor * desc)186 bool 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
CompletePropertyDescriptor(Isolate * isolate,PropertyDescriptor * desc)301 void 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 
ToPropertyDescriptorObject(Isolate * isolate)342 Handle<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