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