property-descriptor.cc 13.7 KB
Newer Older
1 2 3 4
// Copyright 2011 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

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 16 17

namespace v8 {
namespace internal {

18 19
namespace {

20 21 22
// Helper function for ToPropertyDescriptor. Comments describe steps for
// "enumerable", other properties are handled the same way.
// Returns false if an exception was thrown.
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 33
    // 6a. Let enum be ToBoolean(Get(Obj, "enumerable")).
    // 6b. ReturnIfAbrupt(enum).
34
    if (!Object::GetProperty(&it).ToHandle(value)) return false;
35 36 37 38 39 40 41 42
  }
  return true;
}

// Helper function for ToPropertyDescriptor. Handles the case of "simple"
// objects: nothing on the prototype chain, just own fast data properties.
// Must not have observable side effects, because the slow path will restart
// the entire conversion!
43
bool ToPropertyDescriptorFastPath(Isolate* isolate, Handle<JSReceiver> obj,
44 45
                                  PropertyDescriptor* desc) {
  if (!obj->IsJSObject()) return false;
46 47 48 49
  Handle<Map> map(Handle<JSObject>::cast(obj)->map(), isolate);
  if (map->instance_type() != JS_OBJECT_TYPE) return false;
  if (map->is_access_check_needed()) return false;
  if (map->prototype() != *isolate->initial_object_prototype()) return false;
50 51 52
  // During bootstrapping, the object_function_prototype_map hasn't been
  // set up yet.
  if (isolate->bootstrapper()->IsActive()) return false;
53
  if (JSObject::cast(map->prototype()).map() !=
54 55 56 57
      isolate->native_context()->object_function_prototype_map()) {
    return false;
  }
  // TODO(jkummerow): support dictionary properties?
58
  if (map->is_dictionary_map()) return false;
59
  Handle<DescriptorArray> descs =
60
      Handle<DescriptorArray>(map->instance_descriptors(isolate), isolate);
61
  for (InternalIndex i : map->IterateOwnDescriptors()) {
62 63
    PropertyDetails details = descs->GetDetails(i);
    Handle<Object> value;
64 65
    if (details.location() == kField) {
      if (details.kind() == kData) {
66 67
        value = JSObject::FastPropertyAt(Handle<JSObject>::cast(obj),
                                         details.representation(),
68
                                         FieldIndex::ForDescriptor(*map, i));
69 70 71 72 73 74 75 76 77
      } else {
        DCHECK_EQ(kAccessor, details.kind());
        // Bail out to slow path.
        return false;
      }

    } else {
      DCHECK_EQ(kDescriptor, details.location());
      if (details.kind() == kData) {
78
        value = handle(descs->GetStrongValue(i), isolate);
79 80
      } else {
        DCHECK_EQ(kAccessor, details.kind());
81 82
        // Bail out to slow path.
        return false;
83
      }
84
    }
85
    Name key = descs->GetKey(i);
86 87
    ReadOnlyRoots roots(isolate);
    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 97 98
      // Bail out to slow path to throw an exception if necessary.
      if (!value->IsCallable()) return false;
      desc->set_get(value);
99
    } else if (key == roots.set_string()) {
100 101 102 103 104 105 106 107 108 109 110 111 112
      // Bail out to slow path to throw an exception if necessary.
      if (!value->IsCallable()) return false;
      desc->set_set(value);
    }
  }
  if ((desc->has_get() || desc->has_set()) &&
      (desc->has_value() || desc->has_writable())) {
    // Bail out to slow path to throw an exception.
    return false;
  }
  return true;
}

113 114
void CreateDataProperty(Handle<JSObject> object, Handle<String> name,
                        Handle<Object> value) {
115 116
  LookupIterator it(object->GetIsolate(), object, name, object,
                    LookupIterator::OWN_SKIP_INTERCEPTOR);
117 118 119 120
  Maybe<bool> result = JSObject::CreateDataProperty(&it, value);
  CHECK(result.IsJust() && result.FromJust());
}

121 122
}  // namespace

123 124 125 126 127
// ES6 6.2.4.4 "FromPropertyDescriptor"
Handle<Object> PropertyDescriptor::ToObject(Isolate* isolate) {
  DCHECK(!(PropertyDescriptor::IsAccessorDescriptor(this) &&
           PropertyDescriptor::IsDataDescriptor(this)));
  Factory* factory = isolate->factory();
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
  if (IsRegularAccessorProperty()) {
    // Fast case for regular accessor properties.
    Handle<JSObject> result = factory->NewJSObjectFromMap(
        isolate->accessor_property_descriptor_map());
    result->InObjectPropertyAtPut(JSAccessorPropertyDescriptor::kGetIndex,
                                  *get());
    result->InObjectPropertyAtPut(JSAccessorPropertyDescriptor::kSetIndex,
                                  *set());
    result->InObjectPropertyAtPut(
        JSAccessorPropertyDescriptor::kEnumerableIndex,
        isolate->heap()->ToBoolean(enumerable()));
    result->InObjectPropertyAtPut(
        JSAccessorPropertyDescriptor::kConfigurableIndex,
        isolate->heap()->ToBoolean(configurable()));
    return result;
  }
  if (IsRegularDataProperty()) {
    // Fast case for regular data properties.
    Handle<JSObject> result =
        factory->NewJSObjectFromMap(isolate->data_property_descriptor_map());
    result->InObjectPropertyAtPut(JSDataPropertyDescriptor::kValueIndex,
                                  *value());
    result->InObjectPropertyAtPut(JSDataPropertyDescriptor::kWritableIndex,
                                  isolate->heap()->ToBoolean(writable()));
    result->InObjectPropertyAtPut(JSDataPropertyDescriptor::kEnumerableIndex,
                                  isolate->heap()->ToBoolean(enumerable()));
    result->InObjectPropertyAtPut(JSDataPropertyDescriptor::kConfigurableIndex,
                                  isolate->heap()->ToBoolean(configurable()));
    return result;
  }
158 159
  Handle<JSObject> result = factory->NewJSObject(isolate->object_function());
  if (has_value()) {
160
    CreateDataProperty(result, factory->value_string(), value());
161 162
  }
  if (has_writable()) {
163
    CreateDataProperty(result, factory->writable_string(),
164 165 166
                       factory->ToBoolean(writable()));
  }
  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 175 176
                       factory->ToBoolean(enumerable()));
  }
  if (has_configurable()) {
177
    CreateDataProperty(result, factory->configurable_string(),
178 179 180 181 182
                       factory->ToBoolean(configurable()));
  }
  return result;
}

183 184 185 186 187 188 189 190
// ES6 6.2.4.5
// Returns false in case of exception.
// static
bool PropertyDescriptor::ToPropertyDescriptor(Isolate* isolate,
                                              Handle<Object> obj,
                                              PropertyDescriptor* desc) {
  // 1. ReturnIfAbrupt(Obj).
  // 2. If Type(Obj) is not Object, throw a TypeError exception.
191
  if (!obj->IsJSReceiver()) {
192 193 194 195 196 197 198
    isolate->Throw(*isolate->factory()->NewTypeError(
        MessageTemplate::kPropertyDescObject, obj));
    return false;
  }
  // 3. Let desc be a new Property Descriptor that initially has no fields.
  DCHECK(desc->is_empty());

199 200
  Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(obj);
  if (ToPropertyDescriptorFastPath(isolate, receiver, desc)) {
201 202 203
    return true;
  }

204 205 206
  // enumerable?
  Handle<Object> enumerable;
  // 4 through 6b.
207
  if (!GetPropertyIfPresent(receiver, isolate->factory()->enumerable_string(),
208 209 210 211 212
                            &enumerable)) {
    return false;
  }
  // 6c. Set the [[Enumerable]] field of desc to enum.
  if (!enumerable.is_null()) {
213
    desc->set_enumerable(enumerable->BooleanValue(isolate));
214 215 216 217 218
  }

  // configurable?
  Handle<Object> configurable;
  // 7 through 9b.
219
  if (!GetPropertyIfPresent(receiver, isolate->factory()->configurable_string(),
220 221 222 223 224
                            &configurable)) {
    return false;
  }
  // 9c. Set the [[Configurable]] field of desc to conf.
  if (!configurable.is_null()) {
225
    desc->set_configurable(configurable->BooleanValue(isolate));
226 227 228 229 230
  }

  // value?
  Handle<Object> value;
  // 10 through 12b.
231 232
  if (!GetPropertyIfPresent(receiver, isolate->factory()->value_string(),
                            &value)) {
233 234 235 236 237 238 239 240
    return false;
  }
  // 12c. Set the [[Value]] field of desc to value.
  if (!value.is_null()) desc->set_value(value);

  // writable?
  Handle<Object> writable;
  // 13 through 15b.
241
  if (!GetPropertyIfPresent(receiver, isolate->factory()->writable_string(),
242 243 244 245
                            &writable)) {
    return false;
  }
  // 15c. Set the [[Writable]] field of desc to writable.
246
  if (!writable.is_null()) desc->set_writable(writable->BooleanValue(isolate));
247 248 249 250

  // getter?
  Handle<Object> getter;
  // 16 through 18b.
251 252
  if (!GetPropertyIfPresent(receiver, isolate->factory()->get_string(),
                            &getter)) {
253 254 255 256 257
    return false;
  }
  if (!getter.is_null()) {
    // 18c. If IsCallable(getter) is false and getter is not undefined,
    // throw a TypeError exception.
258
    if (!getter->IsCallable() && !getter->IsUndefined(isolate)) {
259 260 261
      isolate->Throw(*isolate->factory()->NewTypeError(
          MessageTemplate::kObjectGetterCallable, getter));
      return false;
262
    }
263 264 265 266 267 268
    // 18d. Set the [[Get]] field of desc to getter.
    desc->set_get(getter);
  }
  // setter?
  Handle<Object> setter;
  // 19 through 21b.
269 270
  if (!GetPropertyIfPresent(receiver, isolate->factory()->set_string(),
                            &setter)) {
271 272 273 274 275
    return false;
  }
  if (!setter.is_null()) {
    // 21c. If IsCallable(setter) is false and setter is not undefined,
    // throw a TypeError exception.
276
    if (!setter->IsCallable() && !setter->IsUndefined(isolate)) {
277 278 279
      isolate->Throw(*isolate->factory()->NewTypeError(
          MessageTemplate::kObjectSetterCallable, setter));
      return false;
280
    }
281 282 283 284 285 286 287 288 289 290 291
    // 21d. Set the [[Set]] field of desc to setter.
    desc->set_set(setter);
  }

  // 22. If either desc.[[Get]] or desc.[[Set]] is present, then
  // 22a. If either desc.[[Value]] or desc.[[Writable]] is present,
  // throw a TypeError exception.
  if ((desc->has_get() || desc->has_set()) &&
      (desc->has_value() || desc->has_writable())) {
    isolate->Throw(*isolate->factory()->NewTypeError(
        MessageTemplate::kValueAndAccessor, obj));
292
    return false;
293
  }
294

295 296 297 298
  // 23. Return desc.
  return true;
}

299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
// ES6 6.2.4.6
// static
void PropertyDescriptor::CompletePropertyDescriptor(Isolate* isolate,
                                                    PropertyDescriptor* desc) {
  // 1. ReturnIfAbrupt(Desc).
  // 2. Assert: Desc is a Property Descriptor.
  // 3. Let like be Record{
  //        [[Value]]: undefined, [[Writable]]: false,
  //        [[Get]]: undefined, [[Set]]: undefined,
  //        [[Enumerable]]: false, [[Configurable]]: false}.
  // 4. If either IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true,
  // then:
  if (!IsAccessorDescriptor(desc)) {
    // 4a. If Desc does not have a [[Value]] field, set Desc.[[Value]] to
    //     like.[[Value]].
    if (!desc->has_value()) {
      desc->set_value(isolate->factory()->undefined_value());
    }
    // 4b. If Desc does not have a [[Writable]] field, set Desc.[[Writable]]
    //     to like.[[Writable]].
    if (!desc->has_writable()) desc->set_writable(false);
  } else {
    // 5. Else,
    // 5a. If Desc does not have a [[Get]] field, set Desc.[[Get]] to
    //     like.[[Get]].
    if (!desc->has_get()) {
      desc->set_get(isolate->factory()->undefined_value());
    }
    // 5b. If Desc does not have a [[Set]] field, set Desc.[[Set]] to
    //     like.[[Set]].
    if (!desc->has_set()) {
      desc->set_set(isolate->factory()->undefined_value());
    }
  }
  // 6. If Desc does not have an [[Enumerable]] field, set
  //    Desc.[[Enumerable]] to like.[[Enumerable]].
  if (!desc->has_enumerable()) desc->set_enumerable(false);
  // 7. If Desc does not have a [[Configurable]] field, set
  //    Desc.[[Configurable]] to like.[[Configurable]].
  if (!desc->has_configurable()) desc->set_configurable(false);
  // 8. Return Desc.
}

342 343
Handle<PropertyDescriptorObject> PropertyDescriptor::ToPropertyDescriptorObject(
    Isolate* isolate) {
344 345
  Handle<PropertyDescriptorObject> obj =
      isolate->factory()->NewPropertyDescriptorObject();
346 347 348 349 350 351 352 353 354 355 356 357

  int flags =
      PropertyDescriptorObject::IsEnumerableBit::encode(enumerable_) |
      PropertyDescriptorObject::HasEnumerableBit::encode(has_enumerable_) |
      PropertyDescriptorObject::IsConfigurableBit::encode(configurable_) |
      PropertyDescriptorObject::HasConfigurableBit::encode(has_configurable_) |
      PropertyDescriptorObject::IsWritableBit::encode(writable_) |
      PropertyDescriptorObject::HasWritableBit::encode(has_writable_) |
      PropertyDescriptorObject::HasValueBit::encode(has_value()) |
      PropertyDescriptorObject::HasGetBit::encode(has_get()) |
      PropertyDescriptorObject::HasSetBit::encode(has_set());

358
  obj->set_flags(flags);
359

360 361 362
  if (has_value()) obj->set_value(*value_);
  if (has_get()) obj->set_get(*get_);
  if (has_set()) obj->set_set(*set_);
363 364 365 366

  return obj;
}

367 368
}  // namespace internal
}  // namespace v8