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