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
15namespace v8 {
16namespace internal {
17
18namespace {
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.
23bool 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!
44bool 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
114void 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"
125Handle<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
188bool 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
304void 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
345Handle<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