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