v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
property-descriptor.cc
Go to the documentation of this file.
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
6
9#include "src/heap/factory.h"
10#include "src/heap/heap-inl.h" // For ToBoolean. TODO(jkummerow): Drop.
12#include "src/objects/lookup.h"
15
16namespace v8 {
17namespace internal {
18
19namespace {
20
21// Helper function for ToPropertyDescriptor. Comments describe steps for
22// "enumerable", other properties are handled the same way.
23// Returns false if an exception was thrown.
24bool GetPropertyIfPresent(Isolate* isolate, DirectHandle<JSReceiver> receiver,
25 DirectHandle<String> name, Handle<JSAny>* value) {
26 LookupIterator it(isolate, receiver, name, receiver);
27 // 4. Let hasEnumerable be HasProperty(Obj, "enumerable").
28 Maybe<bool> has_property = JSReceiver::HasProperty(&it);
29 // 5. ReturnIfAbrupt(hasEnumerable).
30 if (has_property.IsNothing()) return false;
31 // 6. If hasEnumerable is true, then
32 if (has_property.FromJust() == true) {
33 // 6a. Let enum be ToBoolean(Get(Obj, "enumerable")).
34 // 6b. ReturnIfAbrupt(enum).
35 if (!Cast<JSAny>(Object::GetProperty(&it)).ToHandle(value)) return false;
36 }
37 return true;
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,
45 DirectHandle<JSReceiver> obj,
46 PropertyDescriptor* desc) {
47 {
49 Tagged<JSReceiver> raw_obj = *obj;
50 if (!IsJSObject(*raw_obj)) return false;
51 Tagged<Map> raw_map = raw_obj->map(isolate);
52 if (raw_map->instance_type() != JS_OBJECT_TYPE) return false;
53 if (raw_map->is_access_check_needed()) return false;
54 if (raw_map->prototype() != *isolate->initial_object_prototype())
55 return false;
56 // During bootstrapping, the object_function_prototype_map hasn't been
57 // set up yet.
58 if (isolate->bootstrapper()->IsActive()) return false;
59 if (Cast<JSObject>(raw_map->prototype())->map() !=
60 isolate->raw_native_context()->object_function_prototype_map()) {
61 return false;
62 }
63 // TODO(jkummerow): support dictionary properties?
64 if (raw_map->is_dictionary_map()) return false;
65 }
66
67 DirectHandle<Map> map(obj->map(isolate), isolate);
68
69 DirectHandle<DescriptorArray> descs(map->instance_descriptors(isolate),
70 isolate);
71 ReadOnlyRoots roots(isolate);
72 for (InternalIndex i : map->IterateOwnDescriptors()) {
73 PropertyDetails details = descs->GetDetails(i);
74 DirectHandle<Object> value;
75 if (details.location() == PropertyLocation::kField) {
76 if (details.kind() == PropertyKind::kData) {
77 value = JSObject::FastPropertyAt(isolate, Cast<JSObject>(obj),
78 details.representation(),
79 FieldIndex::ForDetails(*map, details));
80 } else {
81 DCHECK_EQ(PropertyKind::kAccessor, details.kind());
82 // Bail out to slow path.
83 return false;
84 }
85
86 } else {
87 DCHECK_EQ(PropertyLocation::kDescriptor, details.location());
88 if (details.kind() == PropertyKind::kData) {
89 value = direct_handle(descs->GetStrongValue(i), isolate);
90 } else {
91 DCHECK_EQ(PropertyKind::kAccessor, details.kind());
92 // Bail out to slow path.
93 return false;
94 }
95 }
96 Tagged<Name> key = descs->GetKey(i);
97 if (key == roots.enumerable_string()) {
98 desc->set_enumerable(Object::BooleanValue(*value, isolate));
99 } else if (key == roots.configurable_string()) {
100 desc->set_configurable(Object::BooleanValue(*value, isolate));
101 } else if (key == roots.value_string()) {
102 desc->set_value(Cast<JSAny>(value));
103 } else if (key == roots.writable_string()) {
104 desc->set_writable(Object::BooleanValue(*value, isolate));
105 } else if (key == roots.get_string()) {
106 // Bail out to slow path to throw an exception if necessary.
107 if (!IsCallable(*value)) return false;
108 desc->set_get(Cast<JSAny>(value));
109 } else if (key == roots.set_string()) {
110 // Bail out to slow path to throw an exception if necessary.
111 if (!IsCallable(*value)) return false;
112 desc->set_set(Cast<JSAny>(value));
113 }
114 }
115 if ((desc->has_get() || desc->has_set()) &&
116 (desc->has_value() || desc->has_writable())) {
117 // Bail out to slow path to throw an exception.
118 return false;
119 }
120 return true;
121}
122
123void CreateDataProperty(Isolate* isolate, DirectHandle<JSObject> object,
124 Handle<String> name, DirectHandle<Object> value) {
126 isolate, object, PropertyKey(isolate, Cast<Name>(name)), value);
127 CHECK(result.IsJust() && result.FromJust());
128}
129
130} // namespace
131
132// ES6 6.2.4.4 "FromPropertyDescriptor"
136 Factory* factory = isolate->factory();
138 // Fast case for regular accessor properties.
140 isolate->accessor_property_descriptor_map());
141 result->InObjectPropertyAtPut(JSAccessorPropertyDescriptor::kGetIndex,
142 *get());
143 result->InObjectPropertyAtPut(JSAccessorPropertyDescriptor::kSetIndex,
144 *set());
145 result->InObjectPropertyAtPut(
147 isolate->heap()->ToBoolean(enumerable()));
148 result->InObjectPropertyAtPut(
150 isolate->heap()->ToBoolean(configurable()));
151 return result;
152 }
153 if (IsRegularDataProperty()) {
154 // Fast case for regular data properties.
156 factory->NewJSObjectFromMap(isolate->data_property_descriptor_map());
157 result->InObjectPropertyAtPut(JSDataPropertyDescriptor::kValueIndex,
158 *value());
160 isolate->heap()->ToBoolean(writable()));
162 isolate->heap()->ToBoolean(enumerable()));
164 isolate->heap()->ToBoolean(configurable()));
165 return result;
166 }
168 factory->NewJSObject(isolate->object_function());
169 if (has_value()) {
170 CreateDataProperty(isolate, result, factory->value_string(), value());
171 }
172 if (has_writable()) {
173 CreateDataProperty(isolate, result, factory->writable_string(),
174 factory->ToBoolean(writable()));
175 }
176 if (has_get()) {
177 CreateDataProperty(isolate, result, factory->get_string(), get());
178 }
179 if (has_set()) {
180 CreateDataProperty(isolate, result, factory->set_string(), set());
181 }
182 if (has_enumerable()) {
183 CreateDataProperty(isolate, result, factory->enumerable_string(),
184 factory->ToBoolean(enumerable()));
185 }
186 if (has_configurable()) {
187 CreateDataProperty(isolate, result, factory->configurable_string(),
188 factory->ToBoolean(configurable()));
189 }
190 return result;
191}
192
193// ES6 6.2.4.5
194// Returns false in case of exception.
195// static
197 Handle<JSAny> obj,
198 PropertyDescriptor* desc) {
199 // 1. ReturnIfAbrupt(Obj).
200 // 2. If Type(Obj) is not Object, throw a TypeError exception.
201 if (!IsJSReceiver(*obj)) {
202 isolate->Throw(*isolate->factory()->NewTypeError(
203 MessageTemplate::kPropertyDescObject, obj));
204 return false;
205 }
206 // 3. Let desc be a new Property Descriptor that initially has no fields.
207 DCHECK(desc->is_empty());
208
210 if (ToPropertyDescriptorFastPath(isolate, receiver, desc)) {
211 return true;
212 }
213
214 // enumerable?
216 // 4 through 6b.
217 if (!GetPropertyIfPresent(isolate, receiver,
218 isolate->factory()->enumerable_string(),
219 &enumerable)) {
220 return false;
221 }
222 // 6c. Set the [[Enumerable]] field of desc to enum.
223 if (!enumerable.is_null()) {
224 desc->set_enumerable(Object::BooleanValue(*enumerable, isolate));
225 }
226
227 // configurable?
229 // 7 through 9b.
230 if (!GetPropertyIfPresent(isolate, receiver,
231 isolate->factory()->configurable_string(),
232 &configurable)) {
233 return false;
234 }
235 // 9c. Set the [[Configurable]] field of desc to conf.
236 if (!configurable.is_null()) {
237 desc->set_configurable(Object::BooleanValue(*configurable, isolate));
238 }
239
240 // value?
242 // 10 through 12b.
243 if (!GetPropertyIfPresent(isolate, receiver,
244 isolate->factory()->value_string(), &value)) {
245 return false;
246 }
247 // 12c. Set the [[Value]] field of desc to value.
248 if (!value.is_null()) desc->set_value(value);
249
250 // writable?
252 // 13 through 15b.
253 if (!GetPropertyIfPresent(isolate, receiver,
254 isolate->factory()->writable_string(), &writable)) {
255 return false;
256 }
257 // 15c. Set the [[Writable]] field of desc to writable.
258 if (!writable.is_null())
259 desc->set_writable(Object::BooleanValue(*writable, isolate));
260
261 // getter?
263 // 16 through 18b.
264 if (!GetPropertyIfPresent(isolate, receiver, isolate->factory()->get_string(),
265 &getter)) {
266 return false;
267 }
268 if (!getter.is_null()) {
269 // 18c. If IsCallable(getter) is false and getter is not undefined,
270 // throw a TypeError exception.
271 if (!IsCallable(*getter) && !IsUndefined(*getter, isolate)) {
272 isolate->Throw(*isolate->factory()->NewTypeError(
273 MessageTemplate::kObjectGetterCallable, getter));
274 return false;
275 }
276 // 18d. Set the [[Get]] field of desc to getter.
277 desc->set_get(getter);
278 }
279 // setter?
281 // 19 through 21b.
282 if (!GetPropertyIfPresent(isolate, receiver, isolate->factory()->set_string(),
283 &setter)) {
284 return false;
285 }
286 if (!setter.is_null()) {
287 // 21c. If IsCallable(setter) is false and setter is not undefined,
288 // throw a TypeError exception.
289 if (!IsCallable(*setter) && !IsUndefined(*setter, isolate)) {
290 isolate->Throw(*isolate->factory()->NewTypeError(
291 MessageTemplate::kObjectSetterCallable, setter));
292 return false;
293 }
294 // 21d. Set the [[Set]] field of desc to setter.
295 desc->set_set(setter);
296 }
297
298 // 22. If either desc.[[Get]] or desc.[[Set]] is present, then
299 // 22a. If either desc.[[Value]] or desc.[[Writable]] is present,
300 // throw a TypeError exception.
301 if ((desc->has_get() || desc->has_set()) &&
302 (desc->has_value() || desc->has_writable())) {
303 isolate->Throw(*isolate->factory()->NewTypeError(
304 MessageTemplate::kValueAndAccessor, obj));
305 return false;
306 }
307
308 // 23. Return desc.
309 return true;
310}
311
312// ES6 6.2.4.6
313// static
315 PropertyDescriptor* desc) {
316 // 1. ReturnIfAbrupt(Desc).
317 // 2. Assert: Desc is a Property Descriptor.
318 // 3. Let like be Record{
319 // [[Value]]: undefined, [[Writable]]: false,
320 // [[Get]]: undefined, [[Set]]: undefined,
321 // [[Enumerable]]: false, [[Configurable]]: false}.
322 // 4. If either IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true,
323 // then:
324 if (!IsAccessorDescriptor(desc)) {
325 // 4a. If Desc does not have a [[Value]] field, set Desc.[[Value]] to
326 // like.[[Value]].
327 if (!desc->has_value()) {
328 desc->set_value(isolate->factory()->undefined_value());
329 }
330 // 4b. If Desc does not have a [[Writable]] field, set Desc.[[Writable]]
331 // to like.[[Writable]].
332 if (!desc->has_writable()) desc->set_writable(false);
333 } else {
334 // 5. Else,
335 // 5a. If Desc does not have a [[Get]] field, set Desc.[[Get]] to
336 // like.[[Get]].
337 if (!desc->has_get()) {
338 desc->set_get(isolate->factory()->undefined_value());
339 }
340 // 5b. If Desc does not have a [[Set]] field, set Desc.[[Set]] to
341 // like.[[Set]].
342 if (!desc->has_set()) {
343 desc->set_set(isolate->factory()->undefined_value());
344 }
345 }
346 // 6. If Desc does not have an [[Enumerable]] field, set
347 // Desc.[[Enumerable]] to like.[[Enumerable]].
348 if (!desc->has_enumerable()) desc->set_enumerable(false);
349 // 7. If Desc does not have a [[Configurable]] field, set
350 // Desc.[[Configurable]] to like.[[Configurable]].
351 if (!desc->has_configurable()) desc->set_configurable(false);
352 // 8. Return Desc.
353}
354
358 isolate->factory()->NewPropertyDescriptorObject();
359
360 int flags =
361 PropertyDescriptorObject::IsEnumerableBit::encode(enumerable_) |
362 PropertyDescriptorObject::HasEnumerableBit::encode(has_enumerable_) |
363 PropertyDescriptorObject::IsConfigurableBit::encode(configurable_) |
364 PropertyDescriptorObject::HasConfigurableBit::encode(has_configurable_) |
365 PropertyDescriptorObject::IsWritableBit::encode(writable_) |
366 PropertyDescriptorObject::HasWritableBit::encode(has_writable_) |
367 PropertyDescriptorObject::HasValueBit::encode(has_value()) |
368 PropertyDescriptorObject::HasGetBit::encode(has_get()) |
369 PropertyDescriptorObject::HasSetBit::encode(has_set());
370
371 obj->set_flags(flags);
372
373 if (has_value()) obj->set_value(*value_);
374 if (has_get()) obj->set_get(*get_);
375 if (has_set()) obj->set_set(*set_);
376
377 return obj;
378}
379
380} // namespace internal
381} // namespace v8
PropertyT * getter
Handle< Boolean > ToBoolean(bool value)
Handle< JSObject > NewJSObject(DirectHandle< JSFunction > constructor, AllocationType allocation=AllocationType::kYoung, NewJSObjectType=NewJSObjectType::kNoAPIWrapper)
Definition factory.cc:2985
Handle< JSObject > NewJSObjectFromMap(DirectHandle< Map > map, AllocationType allocation=AllocationType::kYoung, DirectHandle< AllocationSite > allocation_site=DirectHandle< AllocationSite >::null(), NewJSObjectType=NewJSObjectType::kNoAPIWrapper)
Definition factory.cc:3135
static FieldIndex ForDetails(Tagged< Map > map, PropertyDetails details)
static Handle< JSAny > FastPropertyAt(Isolate *isolate, DirectHandle< JSObject > object, Representation representation, FieldIndex index)
static V8_WARN_UNUSED_RESULT Maybe< bool > CreateDataProperty(Isolate *isolate, DirectHandle< JSObject > object, PropertyKey key, DirectHandle< Object > value, Maybe< ShouldThrow > should_throw=Just(kDontThrow))
V8_EXPORT_PRIVATE static V8_WARN_UNUSED_RESULT Maybe< bool > HasProperty(LookupIterator *it)
Definition js-objects.cc:98
static V8_EXPORT_PRIVATE bool BooleanValue(Tagged< Object > obj, IsolateT *isolate)
V8_EXPORT_PRIVATE static V8_WARN_UNUSED_RESULT MaybeHandle< Object > GetProperty(LookupIterator *it, bool is_global_reference=false)
Definition objects.cc:1248
static bool IsAccessorDescriptor(PropertyDescriptor *desc)
Handle< UnionOf< JSAny, FunctionTemplateInfo > > get() const
static void CompletePropertyDescriptor(Isolate *isolate, PropertyDescriptor *desc)
static bool ToPropertyDescriptor(Isolate *isolate, Handle< JSAny > obj, PropertyDescriptor *desc)
Handle< UnionOf< JSAny, FunctionTemplateInfo > > set() const
static bool IsDataDescriptor(PropertyDescriptor *desc)
DirectHandle< JSObject > ToObject(Isolate *isolate)
DirectHandle< PropertyDescriptorObject > ToPropertyDescriptorObject(Isolate *isolate)
IndirectHandle< UnionOf< JSAny, FunctionTemplateInfo > > get_
IndirectHandle< UnionOf< JSAny, FunctionTemplateInfo > > set_
TNode< Object > receiver
std::map< const std::string, const std::string > map
ZoneVector< RpoNumber > & result
PerThreadAssertScopeDebugOnly< false, SAFEPOINTS_ASSERT, HEAP_ALLOCATION_ASSERT > DisallowGarbageCollection
Tagged(T object) -> Tagged< T >
V8_INLINE DirectHandle< T > direct_handle(Tagged< T > object, Isolate *isolate)
return value
Definition map-inl.h:893
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
Definition casting.h:150
Local< T > Handle
#define CHECK(condition)
Definition logging.h:124
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485