runtime-classes.cc 30.4 KB
Newer Older
1 2 3 4 5
// Copyright 2014 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.

#include <stdlib.h>
6

7 8
#include <limits>

9
#include "src/builtins/accessors.h"
10
#include "src/common/globals.h"
11
#include "src/common/message-template.h"
12
#include "src/debug/debug.h"
13
#include "src/execution/arguments-inl.h"
14
#include "src/execution/frames-inl.h"
15
#include "src/execution/isolate-inl.h"
16 17
#include "src/logging/counters.h"
#include "src/logging/log.h"
18
#include "src/objects/elements.h"
19
#include "src/objects/hash-table-inl.h"
20
#include "src/objects/literal-objects-inl.h"
21
#include "src/objects/lookup-inl.h"
22
#include "src/objects/smi.h"
23
#include "src/objects/struct-inl.h"
24
#include "src/runtime/runtime-utils.h"
25 26 27 28 29 30
#include "src/runtime/runtime.h"

namespace v8 {
namespace internal {


31 32
RUNTIME_FUNCTION(Runtime_ThrowUnsupportedSuperError) {
  HandleScope scope(isolate);
33
  DCHECK_EQ(0, args.length());
34 35
  THROW_NEW_ERROR_RETURN_FAILURE(
      isolate, NewReferenceError(MessageTemplate::kUnsupportedSuper));
36 37 38
}


39 40
RUNTIME_FUNCTION(Runtime_ThrowConstructorNonCallableError) {
  HandleScope scope(isolate);
41
  DCHECK_EQ(1, args.length());
42
  CONVERT_ARG_HANDLE_CHECKED(JSFunction, constructor, 0);
43
  Handle<String> name(constructor->shared().Name(), isolate);
44 45 46 47 48
  if (name->length() == 0) {
    THROW_NEW_ERROR_RETURN_FAILURE(
        isolate,
        NewTypeError(MessageTemplate::kAnonymousConstructorNonCallable));
  }
49
  THROW_NEW_ERROR_RETURN_FAILURE(
50
      isolate, NewTypeError(MessageTemplate::kConstructorNonCallable, name));
51 52 53
}


54 55
RUNTIME_FUNCTION(Runtime_ThrowStaticPrototypeError) {
  HandleScope scope(isolate);
56
  DCHECK_EQ(0, args.length());
57 58
  THROW_NEW_ERROR_RETURN_FAILURE(
      isolate, NewTypeError(MessageTemplate::kStaticPrototype));
59 60
}

61 62 63 64 65 66 67
RUNTIME_FUNCTION(Runtime_ThrowSuperAlreadyCalledError) {
  HandleScope scope(isolate);
  DCHECK_EQ(0, args.length());
  THROW_NEW_ERROR_RETURN_FAILURE(
      isolate, NewReferenceError(MessageTemplate::kSuperAlreadyCalled));
}

68 69 70 71 72 73 74
RUNTIME_FUNCTION(Runtime_ThrowSuperNotCalled) {
  HandleScope scope(isolate);
  DCHECK_EQ(0, args.length());
  THROW_NEW_ERROR_RETURN_FAILURE(
      isolate, NewReferenceError(MessageTemplate::kSuperNotCalled));
}

75 76
namespace {

77 78
Object ThrowNotSuperConstructor(Isolate* isolate, Handle<Object> constructor,
                                Handle<JSFunction> function) {
79
  Handle<String> super_name;
80
  if (constructor->IsJSFunction()) {
81 82
    super_name =
        handle(Handle<JSFunction>::cast(constructor)->shared().Name(), isolate);
83 84 85 86 87 88 89
  } else if (constructor->IsOddball()) {
    DCHECK(constructor->IsNull(isolate));
    super_name = isolate->factory()->null_string();
  } else {
    super_name = Object::NoSideEffectsToString(isolate, constructor);
  }
  // null constructor
90
  if (super_name->length() == 0) {
91 92
    super_name = isolate->factory()->null_string();
  }
93
  Handle<String> function_name(function->shared().Name(), isolate);
94
  // anonymous class
95
  if (function_name->length() == 0) {
96 97 98 99 100 101 102 103 104 105 106 107 108 109
    THROW_NEW_ERROR_RETURN_FAILURE(
        isolate,
        NewTypeError(MessageTemplate::kNotSuperConstructorAnonymousClass,
                     super_name));
  }
  THROW_NEW_ERROR_RETURN_FAILURE(
      isolate, NewTypeError(MessageTemplate::kNotSuperConstructor, super_name,
                            function_name));
}

}  // namespace

RUNTIME_FUNCTION(Runtime_ThrowNotSuperConstructor) {
  HandleScope scope(isolate);
110
  DCHECK_EQ(2, args.length());
111 112 113 114 115
  CONVERT_ARG_HANDLE_CHECKED(Object, constructor, 0);
  CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 1);
  return ThrowNotSuperConstructor(isolate, constructor, function);
}

116 117 118
namespace {

template <typename Dictionary>
119
Handle<Name> KeyToName(Isolate* isolate, Handle<Object> key) {
120
  STATIC_ASSERT((std::is_same<Dictionary, SwissNameDictionary>::value ||
121
                 std::is_same<Dictionary, NameDictionary>::value));
122 123 124 125 126 127 128 129 130 131 132 133 134
  DCHECK(key->IsName());
  return Handle<Name>::cast(key);
}

template <>
Handle<Name> KeyToName<NumberDictionary>(Isolate* isolate, Handle<Object> key) {
  DCHECK(key->IsNumber());
  return isolate->factory()->NumberToString(key);
}

// Gets |index|'th argument which may be a class constructor object, a class
// prototype object or a class method. In the latter case the following
// post-processing may be required:
135
// 1) set method's name to a concatenation of |name_prefix| and |key| if the
136 137 138
//    method's shared function info indicates that method does not have a
//    shared name.
template <typename Dictionary>
139
MaybeHandle<Object> GetMethodAndSetName(
140 141
    Isolate* isolate,
    RuntimeArguments& args,  // NOLINT(runtime/references)
142
    Smi index, Handle<String> name_prefix, Handle<Object> key) {
143
  int int_index = index.value();
144 145 146 147 148 149 150 151

  // Class constructor and prototype values do not require post processing.
  if (int_index < ClassBoilerplate::kFirstDynamicArgumentIndex) {
    return args.at<Object>(int_index);
  }

  Handle<JSFunction> method = args.at<JSFunction>(int_index);

152
  if (!method->shared().HasSharedName()) {
153 154 155 156 157 158 159 160 161 162 163 164 165 166
    // TODO(ishell): method does not have a shared name at this point only if
    // the key is a computed property name. However, the bytecode generator
    // explicitly generates ToName bytecodes to ensure that the computed
    // property name is properly converted to Name. So, we can actually be smart
    // here and avoid converting Smi keys back to Name.
    Handle<Name> name = KeyToName<Dictionary>(isolate, key);
    if (!JSFunction::SetName(method, name, name_prefix)) {
      return MaybeHandle<Object>();
    }
  }
  return method;
}

// Gets |index|'th argument which may be a class constructor object, a class
167 168
// prototype object or a class method.
// This is a simplified version of GetMethodAndSetName()
169 170
// function above that is used when it's guaranteed that the method has
// shared name.
171
Object GetMethodWithSharedName(
172
    Isolate* isolate,
173
    RuntimeArguments& args,  // NOLINT(runtime/references)
174
    Object index) {
175
  DisallowGarbageCollection no_gc;
176 177 178 179 180 181 182 183
  int int_index = Smi::ToInt(index);

  // Class constructor and prototype values do not require post processing.
  if (int_index < ClassBoilerplate::kFirstDynamicArgumentIndex) {
    return args[int_index];
  }

  Handle<JSFunction> method = args.at<JSFunction>(int_index);
184
  DCHECK(method->shared().HasSharedName());
185 186 187 188 189 190 191 192
  return *method;
}

template <typename Dictionary>
Handle<Dictionary> ShallowCopyDictionaryTemplate(
    Isolate* isolate, Handle<Dictionary> dictionary_template) {
  Handle<Map> dictionary_map(dictionary_template->map(), isolate);
  Handle<Dictionary> dictionary =
193
      Dictionary::ShallowCopy(isolate, dictionary_template);
194
  // Clone all AccessorPairs in the dictionary.
195
  for (InternalIndex i : dictionary->IterateEntries()) {
196
    Object value = dictionary->ValueAt(i);
197
    if (value.IsAccessorPair()) {
198
      Handle<AccessorPair> pair(AccessorPair::cast(value), isolate);
199
      pair = AccessorPair::Copy(isolate, pair);
200 201 202 203 204 205 206 207
      dictionary->ValueAtPut(i, *pair);
    }
  }
  return dictionary;
}

template <typename Dictionary>
bool SubstituteValues(Isolate* isolate, Handle<Dictionary> dictionary,
208
                      RuntimeArguments& args,  // NOLINT(runtime/references)
209 210 211 212
                      bool* install_name_accessor = nullptr) {
  Handle<Name> name_string = isolate->factory()->name_string();

  // Replace all indices with proper methods.
213
  ReadOnlyRoots roots(isolate);
214
  for (InternalIndex i : dictionary->IterateEntries()) {
215
    Object maybe_key = dictionary->KeyAt(i);
216
    if (!Dictionary::IsKey(roots, maybe_key)) continue;
217 218 219 220 221 222 223 224
    if (install_name_accessor && *install_name_accessor &&
        (maybe_key == *name_string)) {
      *install_name_accessor = false;
    }
    Handle<Object> key(maybe_key, isolate);
    Handle<Object> value(dictionary->ValueAt(i), isolate);
    if (value->IsAccessorPair()) {
      Handle<AccessorPair> pair = Handle<AccessorPair>::cast(value);
225
      Object tmp = pair->getter();
226
      if (tmp.IsSmi()) {
227 228 229
        Handle<Object> result;
        ASSIGN_RETURN_ON_EXCEPTION_VALUE(
            isolate, result,
230 231 232
            GetMethodAndSetName<Dictionary>(isolate, args, Smi::cast(tmp),
                                            isolate->factory()->get_string(),
                                            key),
233 234 235 236
            false);
        pair->set_getter(*result);
      }
      tmp = pair->setter();
237
      if (tmp.IsSmi()) {
238 239 240
        Handle<Object> result;
        ASSIGN_RETURN_ON_EXCEPTION_VALUE(
            isolate, result,
241 242 243
            GetMethodAndSetName<Dictionary>(isolate, args, Smi::cast(tmp),
                                            isolate->factory()->set_string(),
                                            key),
244 245 246 247 248 249 250
            false);
        pair->set_setter(*result);
      }
    } else if (value->IsSmi()) {
      Handle<Object> result;
      ASSIGN_RETURN_ON_EXCEPTION_VALUE(
          isolate, result,
251 252 253
          GetMethodAndSetName<Dictionary>(isolate, args, Smi::cast(*value),
                                          isolate->factory()->empty_string(),
                                          key),
254 255 256 257 258 259 260
          false);
      dictionary->ValueAtPut(i, *result);
    }
  }
  return true;
}

261
template <typename Dictionary>
262
void UpdateProtectors(Isolate* isolate, Handle<JSObject> receiver,
263
                      Handle<Dictionary> properties_dictionary) {
264 265 266
  ReadOnlyRoots roots(isolate);
  for (InternalIndex i : properties_dictionary->IterateEntries()) {
    Object maybe_key = properties_dictionary->KeyAt(i);
267
    if (!Dictionary::IsKey(roots, maybe_key)) continue;
268 269 270 271 272 273 274 275 276 277 278 279 280 281
    Handle<Name> name(Name::cast(maybe_key), isolate);
    LookupIterator::UpdateProtector(isolate, receiver, name);
  }
}

void UpdateProtectors(Isolate* isolate, Handle<JSObject> receiver,
                      Handle<DescriptorArray> properties_template) {
  int nof_descriptors = properties_template->number_of_descriptors();
  for (InternalIndex i : InternalIndex::Range(nof_descriptors)) {
    Handle<Name> name(properties_template->GetKey(i), isolate);
    LookupIterator::UpdateProtector(isolate, receiver, name);
  }
}

282 283 284 285
bool AddDescriptorsByTemplate(
    Isolate* isolate, Handle<Map> map,
    Handle<DescriptorArray> descriptors_template,
    Handle<NumberDictionary> elements_dictionary_template,
286 287
    Handle<JSObject> receiver,
    RuntimeArguments& args) {  // NOLINT(runtime/references)
288 289 290 291 292 293 294
  int nof_descriptors = descriptors_template->number_of_descriptors();

  Handle<DescriptorArray> descriptors =
      DescriptorArray::Allocate(isolate, nof_descriptors, 0);

  Handle<NumberDictionary> elements_dictionary =
      *elements_dictionary_template ==
295
              ReadOnlyRoots(isolate).empty_slow_element_dictionary()
296 297 298 299
          ? elements_dictionary_template
          : ShallowCopyDictionaryTemplate(isolate,
                                          elements_dictionary_template);

300 301 302
  // Count the number of properties that must be in the instance and
  // create the property array to hold the constants.
  int count = 0;
303
  for (InternalIndex i : InternalIndex::Range(nof_descriptors)) {
304 305 306
    PropertyDetails details = descriptors_template->GetDetails(i);
    if (details.location() == kDescriptor && details.kind() == kData) {
      count++;
307 308
    }
  }
309 310
  Handle<PropertyArray> property_array =
      isolate->factory()->NewPropertyArray(count);
311

312 313
  // Read values from |descriptors_template| and store possibly post-processed
  // values into "instantiated" |descriptors| array.
314
  int field_index = 0;
315
  for (InternalIndex i : InternalIndex::Range(nof_descriptors)) {
316
    Object value = descriptors_template->GetStrongValue(i);
317
    if (value.IsAccessorPair()) {
318 319
      Handle<AccessorPair> pair = AccessorPair::Copy(
          isolate, handle(AccessorPair::cast(value), isolate));
320 321
      value = *pair;
    }
322
    DisallowGarbageCollection no_gc;
323
    Name name = descriptors_template->GetKey(i);
324
    DCHECK(name.IsUniqueName());
325 326 327
    PropertyDetails details = descriptors_template->GetDetails(i);
    if (details.location() == kDescriptor) {
      if (details.kind() == kData) {
328
        if (value.IsSmi()) {
329
          value = GetMethodWithSharedName(isolate, args, value);
330
        }
331 332
        details = details.CopyWithRepresentation(
            value.OptimalRepresentation(isolate));
333 334
      } else {
        DCHECK_EQ(kAccessor, details.kind());
335
        if (value.IsAccessorPair()) {
336
          AccessorPair pair = AccessorPair::cast(value);
337 338
          Object tmp = pair.getter();
          if (tmp.IsSmi()) {
339
            pair.set_getter(GetMethodWithSharedName(isolate, args, tmp));
340
          }
341 342
          tmp = pair.setter();
          if (tmp.IsSmi()) {
343
            pair.set_setter(GetMethodWithSharedName(isolate, args, tmp));
344 345 346 347
          }
        }
      }
    } else {
348
      UNREACHABLE();
349
    }
350
    DCHECK(value.FitsRepresentation(details.representation()));
351
    if (details.location() == kDescriptor && details.kind() == kData) {
352 353 354 355 356 357 358 359 360 361 362 363
      details = PropertyDetails(details.kind(), details.attributes(), kField,
                                PropertyConstness::kConst,
                                details.representation(), field_index)
                    .set_pointer(details.pointer());

      property_array->set(field_index, value);
      field_index++;
      descriptors->Set(i, name, MaybeObject::FromObject(FieldType::Any()),
                       details);
    } else {
      descriptors->Set(i, name, MaybeObject::FromObject(value), details);
    }
364 365
  }

366 367
  UpdateProtectors(isolate, receiver, descriptors_template);

368
  map->InitializeDescriptors(isolate, *descriptors);
369 370
  if (elements_dictionary->NumberOfElements() > 0) {
    if (!SubstituteValues<NumberDictionary>(isolate, elements_dictionary,
371
                                            args)) {
372 373 374 375 376 377 378 379 380 381
      return false;
    }
    map->set_elements_kind(DICTIONARY_ELEMENTS);
  }

  // Atomically commit the changes.
  receiver->synchronized_set_map(*map);
  if (elements_dictionary->NumberOfElements() > 0) {
    receiver->set_elements(*elements_dictionary);
  }
382 383 384
  if (property_array->length() > 0) {
    receiver->SetProperties(*property_array);
  }
385 386 387
  return true;
}

388 389 390 391 392 393 394 395 396 397 398 399 400 401
// TODO(v8:7569): This is a workaround for the Handle vs MaybeHandle difference
// in the return types of the different Add functions:
// OrderedNameDictionary::Add returns MaybeHandle, NameDictionary::Add returns
// Handle.
template <typename T>
Handle<T> ToHandle(Handle<T> h) {
  return h;
}
template <typename T>
Handle<T> ToHandle(MaybeHandle<T> h) {
  return h.ToHandleChecked();
}

template <typename Dictionary>
402 403
bool AddDescriptorsByTemplate(
    Isolate* isolate, Handle<Map> map,
404
    Handle<Dictionary> properties_dictionary_template,
405 406
    Handle<NumberDictionary> elements_dictionary_template,
    Handle<FixedArray> computed_properties, Handle<JSObject> receiver,
407
    bool install_name_accessor,
408
    RuntimeArguments& args) {  // NOLINT(runtime/references)
409 410 411
  int computed_properties_length = computed_properties->length();

  // Shallow-copy properties template.
412
  Handle<Dictionary> properties_dictionary =
413 414 415 416
      ShallowCopyDictionaryTemplate(isolate, properties_dictionary_template);
  Handle<NumberDictionary> elements_dictionary =
      ShallowCopyDictionaryTemplate(isolate, elements_dictionary_template);

417 418
  using ValueKind = ClassBoilerplate::ValueKind;
  using ComputedEntryFlags = ClassBoilerplate::ComputedEntryFlags;
419 420 421 422 423 424 425 426 427

  // Merge computed properties with properties and elements dictionary
  // templates.
  int i = 0;
  while (i < computed_properties_length) {
    int flags = Smi::ToInt(computed_properties->get(i++));

    ValueKind value_kind = ComputedEntryFlags::ValueKindBits::decode(flags);
    int key_index = ComputedEntryFlags::KeyIndexBits::decode(flags);
428
    Smi value = Smi::FromInt(key_index + 1);  // Value follows name.
429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445

    Handle<Object> key = args.at<Object>(key_index);
    DCHECK(key->IsName());
    uint32_t element;
    Handle<Name> name = Handle<Name>::cast(key);
    if (name->AsArrayIndex(&element)) {
      ClassBoilerplate::AddToElementsTemplate(
          isolate, elements_dictionary, element, key_index, value_kind, value);

    } else {
      name = isolate->factory()->InternalizeName(name);
      ClassBoilerplate::AddToPropertiesTemplate(
          isolate, properties_dictionary, name, key_index, value_kind, value);
    }
  }

  // Replace all indices with proper methods.
446 447
  if (!SubstituteValues<Dictionary>(isolate, properties_dictionary, args,
                                    &install_name_accessor)) {
448 449 450 451 452
    return false;
  }
  if (install_name_accessor) {
    PropertyAttributes attribs =
        static_cast<PropertyAttributes>(DONT_ENUM | READ_ONLY);
453 454
    PropertyDetails details(kAccessor, attribs,
                            PropertyDetails::kConstIfDictConstnessTracking);
455
    Handle<Dictionary> dict = ToHandle(Dictionary::Add(
456
        isolate, properties_dictionary, isolate->factory()->name_string(),
457
        isolate->factory()->function_name_accessor(), details));
458 459 460
    CHECK_EQ(*dict, *properties_dictionary);
  }

461 462
  UpdateProtectors(isolate, receiver, properties_dictionary);

463 464
  if (elements_dictionary->NumberOfElements() > 0) {
    if (!SubstituteValues<NumberDictionary>(isolate, elements_dictionary,
465
                                            args)) {
466 467 468 469 470 471 472 473 474 475 476 477 478 479 480
      return false;
    }
    map->set_elements_kind(DICTIONARY_ELEMENTS);
  }

  // Atomically commit the changes.
  receiver->synchronized_set_map(*map);
  receiver->set_raw_properties_or_hash(*properties_dictionary);
  if (elements_dictionary->NumberOfElements() > 0) {
    receiver->set_elements(*elements_dictionary);
  }
  return true;
}

Handle<JSObject> CreateClassPrototype(Isolate* isolate) {
481 482 483 484 485 486 487 488
  // For constant tracking we want to avoid the hassle of handling
  // in-object properties, so create a map with no in-object
  // properties.

  // TODO(ishell) Support caching of zero in-object properties map
  // by ObjectLiteralMapFromCache().
  Handle<Map> map = Map::Create(isolate, 0);
  return isolate->factory()->NewJSObjectFromMap(map);
489 490 491 492 493
}

bool InitClassPrototype(Isolate* isolate,
                        Handle<ClassBoilerplate> class_boilerplate,
                        Handle<JSObject> prototype,
494
                        Handle<HeapObject> prototype_parent,
495
                        Handle<JSFunction> constructor,
496
                        RuntimeArguments& args) {  // NOLINT(runtime/references)
497
  Handle<Map> map(prototype->map(), isolate);
498
  map = Map::CopyDropDescriptors(isolate, map);
499
  map->set_is_prototype_map(true);
500
  Map::SetPrototype(isolate, map, prototype_parent);
501 502 503 504 505 506 507 508 509 510 511
  constructor->set_prototype_or_initial_map(*prototype);
  map->SetConstructor(*constructor);
  Handle<FixedArray> computed_properties(
      class_boilerplate->instance_computed_properties(), isolate);
  Handle<NumberDictionary> elements_dictionary_template(
      NumberDictionary::cast(class_boilerplate->instance_elements_template()),
      isolate);

  Handle<Object> properties_template(
      class_boilerplate->instance_properties_template(), isolate);

512
  if (properties_template->IsDescriptorArray()) {
513 514 515 516 517 518 519 520 521
    Handle<DescriptorArray> descriptors_template =
        Handle<DescriptorArray>::cast(properties_template);

    // The size of the prototype object is known at this point.
    // So we can create it now and then add the rest instance methods to the
    // map.
    return AddDescriptorsByTemplate(isolate, map, descriptors_template,
                                    elements_dictionary_template, prototype,
                                    args);
522 523 524 525 526 527 528 529 530
  } else {
    map->set_is_dictionary_map(true);
    map->set_is_migration_target(false);
    map->set_may_have_interesting_symbols(true);
    map->set_construction_counter(Map::kNoSlackTracking);

    // Class prototypes do not have a name accessor.
    const bool install_name_accessor = false;

531
    if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
532 533
      Handle<SwissNameDictionary> properties_dictionary_template =
          Handle<SwissNameDictionary>::cast(properties_template);
534 535 536 537 538 539 540 541 542 543 544 545
      return AddDescriptorsByTemplate(
          isolate, map, properties_dictionary_template,
          elements_dictionary_template, computed_properties, prototype,
          install_name_accessor, args);
    } else {
      Handle<NameDictionary> properties_dictionary_template =
          Handle<NameDictionary>::cast(properties_template);
      return AddDescriptorsByTemplate(
          isolate, map, properties_dictionary_template,
          elements_dictionary_template, computed_properties, prototype,
          install_name_accessor, args);
    }
546 547 548
  }
}

549 550 551 552
bool InitClassConstructor(
    Isolate* isolate, Handle<ClassBoilerplate> class_boilerplate,
    Handle<HeapObject> constructor_parent, Handle<JSFunction> constructor,
    RuntimeArguments& args) {  // NOLINT(runtime/references)
553
  Handle<Map> map(constructor->map(), isolate);
554
  map = Map::CopyDropDescriptors(isolate, map);
555 556 557
  DCHECK(map->is_prototype_map());

  if (!constructor_parent.is_null()) {
558 559
    // Set map's prototype without enabling prototype setup mode for superclass
    // because it does not make sense.
560
    Map::SetPrototype(isolate, map, constructor_parent, false);
561 562
    // Ensure that setup mode will never be enabled for superclass.
    JSObject::MakePrototypesFast(constructor_parent, kStartAtReceiver, isolate);
563 564 565 566 567 568 569 570 571 572 573
  }

  Handle<NumberDictionary> elements_dictionary_template(
      NumberDictionary::cast(class_boilerplate->static_elements_template()),
      isolate);
  Handle<FixedArray> computed_properties(
      class_boilerplate->static_computed_properties(), isolate);

  Handle<Object> properties_template(
      class_boilerplate->static_properties_template(), isolate);

574 575 576
  if (properties_template->IsDescriptorArray()) {
    Handle<DescriptorArray> descriptors_template =
        Handle<DescriptorArray>::cast(properties_template);
577

578 579 580 581
    return AddDescriptorsByTemplate(isolate, map, descriptors_template,
                                    elements_dictionary_template, constructor,
                                    args);
  } else {
582
    map->set_is_dictionary_map(true);
583
    map->InitializeDescriptors(isolate,
584
                               ReadOnlyRoots(isolate).empty_descriptor_array());
585
    map->set_is_migration_target(false);
586 587 588
    map->set_may_have_interesting_symbols(true);
    map->set_construction_counter(Map::kNoSlackTracking);

589 590
    // All class constructors have a name accessor.
    const bool install_name_accessor = true;
591

592
    if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
593 594
      Handle<SwissNameDictionary> properties_dictionary_template =
          Handle<SwissNameDictionary>::cast(properties_template);
595

596 597 598 599 600 601 602 603 604 605 606 607
      return AddDescriptorsByTemplate(
          isolate, map, properties_dictionary_template,
          elements_dictionary_template, computed_properties, constructor,
          install_name_accessor, args);
    } else {
      Handle<NameDictionary> properties_dictionary_template =
          Handle<NameDictionary>::cast(properties_template);
      return AddDescriptorsByTemplate(
          isolate, map, properties_dictionary_template,
          elements_dictionary_template, computed_properties, constructor,
          install_name_accessor, args);
    }
608 609 610
  }
}

611 612 613
MaybeHandle<Object> DefineClass(
    Isolate* isolate, Handle<ClassBoilerplate> class_boilerplate,
    Handle<Object> super_class, Handle<JSFunction> constructor,
614
    RuntimeArguments& args) {  // NOLINT(runtime/references)
615
  Handle<Object> prototype_parent;
616
  Handle<HeapObject> constructor_parent;
617

618
  if (super_class->IsTheHole(isolate)) {
619 620
    prototype_parent = isolate->initial_object_prototype();
  } else {
621
    if (super_class->IsNull(isolate)) {
622
      prototype_parent = isolate->factory()->null_value();
623
    } else if (super_class->IsConstructor()) {
624
      DCHECK(!super_class->IsJSFunction() ||
625
             !IsResumableFunction(
626
                 Handle<JSFunction>::cast(super_class)->shared().kind()));
627
      ASSIGN_RETURN_ON_EXCEPTION(
628 629
          isolate, prototype_parent,
          Runtime::GetObjectProperty(isolate, super_class,
630
                                     isolate->factory()->prototype_string()),
631
          Object);
632 633
      if (!prototype_parent->IsNull(isolate) &&
          !prototype_parent->IsJSReceiver()) {
634
        THROW_NEW_ERROR(
635
            isolate, NewTypeError(MessageTemplate::kPrototypeParentNotAnObject,
636 637
                                  prototype_parent),
            Object);
638
      }
639 640 641
      // Create new handle to avoid |constructor_parent| corruption because of
      // |super_class| handle value overwriting via storing to
      // args[ClassBoilerplate::kPrototypeArgumentIndex] below.
642
      constructor_parent = handle(HeapObject::cast(*super_class), isolate);
643
    } else {
644 645 646 647
      THROW_NEW_ERROR(isolate,
                      NewTypeError(MessageTemplate::kExtendsValueNotConstructor,
                                   super_class),
                      Object);
648 649 650
    }
  }

651 652
  Handle<JSObject> prototype = CreateClassPrototype(isolate);
  DCHECK_EQ(*constructor, args[ClassBoilerplate::kConstructorArgumentIndex]);
653
  args.set_at(ClassBoilerplate::kPrototypeArgumentIndex, *prototype);
654

655 656 657
  if (!InitClassConstructor(isolate, class_boilerplate, constructor_parent,
                            constructor, args) ||
      !InitClassPrototype(isolate, class_boilerplate, prototype,
658 659
                          Handle<HeapObject>::cast(prototype_parent),
                          constructor, args)) {
660 661
    DCHECK(isolate->has_pending_exception());
    return MaybeHandle<Object>();
662
  }
663
  if (FLAG_log_maps) {
664
    Handle<Map> empty_map;
Camillo Bruni's avatar
Camillo Bruni committed
665
    LOG(isolate,
666 667
        MapEvent("InitialMap", empty_map, handle(constructor->map(), isolate),
                 "init class constructor",
668 669
                 SharedFunctionInfo::DebugName(
                     handle(constructor->shared(), isolate))));
670 671 672
    LOG(isolate,
        MapEvent("InitialMap", empty_map, handle(prototype->map(), isolate),
                 "init class prototype"));
Camillo Bruni's avatar
Camillo Bruni committed
673 674
  }

675
  return prototype;
676 677
}

678
}  // namespace
679 680 681

RUNTIME_FUNCTION(Runtime_DefineClass) {
  HandleScope scope(isolate);
682 683
  DCHECK_LE(ClassBoilerplate::kFirstDynamicArgumentIndex, args.length());
  CONVERT_ARG_HANDLE_CHECKED(ClassBoilerplate, class_boilerplate, 0);
684
  CONVERT_ARG_HANDLE_CHECKED(JSFunction, constructor, 1);
685 686
  CONVERT_ARG_HANDLE_CHECKED(Object, super_class, 2);
  DCHECK_EQ(class_boilerplate->arguments_count(), args.length());
687

688
  RETURN_RESULT_OR_FAILURE(
689 690
      isolate,
      DefineClass(isolate, class_boilerplate, super_class, constructor, args));
691 692
}

693
namespace {
694

695 696
enum class SuperMode { kLoad, kStore };

697 698 699 700
MaybeHandle<JSReceiver> GetSuperHolder(Isolate* isolate,
                                       Handle<JSObject> home_object,
                                       SuperMode mode,
                                       LookupIterator::Key* key) {
701
  if (home_object->IsAccessCheckNeeded() &&
702
      !isolate->MayAccess(handle(isolate->context(), isolate), home_object)) {
703
    isolate->ReportFailedAccessCheck(home_object);
704
    RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, JSReceiver);
705 706 707 708
  }

  PrototypeIterator iter(isolate, home_object);
  Handle<Object> proto = PrototypeIterator::GetCurrent(iter);
709
  if (!proto->IsJSReceiver()) {
710 711 712
    MessageTemplate message = mode == SuperMode::kLoad
                                  ? MessageTemplate::kNonObjectPropertyLoad
                                  : MessageTemplate::kNonObjectPropertyStore;
713
    Handle<Name> name = key->GetName(isolate);
714
    THROW_NEW_ERROR(isolate, NewTypeError(message, name, proto), JSReceiver);
715
  }
716 717
  return Handle<JSReceiver>::cast(proto);
}
718

719 720
MaybeHandle<Object> LoadFromSuper(Isolate* isolate, Handle<Object> receiver,
                                  Handle<JSObject> home_object,
721
                                  LookupIterator::Key* key) {
722 723 724
  Handle<JSReceiver> holder;
  ASSIGN_RETURN_ON_EXCEPTION(
      isolate, holder,
725 726
      GetSuperHolder(isolate, home_object, SuperMode::kLoad, key), Object);
  LookupIterator it(isolate, receiver, *key, holder);
727
  Handle<Object> result;
728
  ASSIGN_RETURN_ON_EXCEPTION(isolate, result, Object::GetProperty(&it), Object);
729
  return result;
730 731
}

732
}  // anonymous namespace
733

734 735
RUNTIME_FUNCTION(Runtime_LoadFromSuper) {
  HandleScope scope(isolate);
736
  DCHECK_EQ(3, args.length());
737 738 739 740
  CONVERT_ARG_HANDLE_CHECKED(Object, receiver, 0);
  CONVERT_ARG_HANDLE_CHECKED(JSObject, home_object, 1);
  CONVERT_ARG_HANDLE_CHECKED(Name, name, 2);

741 742
  LookupIterator::Key key(isolate, name);

743
  RETURN_RESULT_OR_FAILURE(isolate,
744
                           LoadFromSuper(isolate, receiver, home_object, &key));
745 746 747 748 749
}


RUNTIME_FUNCTION(Runtime_LoadKeyedFromSuper) {
  HandleScope scope(isolate);
750
  DCHECK_EQ(3, args.length());
751 752
  CONVERT_ARG_HANDLE_CHECKED(Object, receiver, 0);
  CONVERT_ARG_HANDLE_CHECKED(JSObject, home_object, 1);
753 754
  // TODO(ishell): To improve performance, consider performing the to-string
  // conversion of {key} before calling into the runtime.
755 756
  CONVERT_ARG_HANDLE_CHECKED(Object, key, 2);

757 758 759
  bool success;
  LookupIterator::Key lookup_key(isolate, key, &success);
  if (!success) return ReadOnlyRoots(isolate).exception();
760

761 762
  RETURN_RESULT_OR_FAILURE(
      isolate, LoadFromSuper(isolate, receiver, home_object, &lookup_key));
763 764
}

765
namespace {
766

767
MaybeHandle<Object> StoreToSuper(Isolate* isolate, Handle<JSObject> home_object,
768 769 770
                                 Handle<Object> receiver,
                                 LookupIterator::Key* key, Handle<Object> value,
                                 StoreOrigin store_origin) {
771 772 773
  Handle<JSReceiver> holder;
  ASSIGN_RETURN_ON_EXCEPTION(
      isolate, holder,
774 775 776
      GetSuperHolder(isolate, home_object, SuperMode::kStore, key), Object);
  LookupIterator it(isolate, receiver, *key, holder);
  MAYBE_RETURN(Object::SetSuperProperty(&it, value, store_origin),
777 778
               MaybeHandle<Object>());
  return value;
779 780
}

781
}  // anonymous namespace
782

783
RUNTIME_FUNCTION(Runtime_StoreToSuper) {
784
  HandleScope scope(isolate);
785
  DCHECK_EQ(4, args.length());
786 787 788 789 790
  CONVERT_ARG_HANDLE_CHECKED(Object, receiver, 0);
  CONVERT_ARG_HANDLE_CHECKED(JSObject, home_object, 1);
  CONVERT_ARG_HANDLE_CHECKED(Name, name, 2);
  CONVERT_ARG_HANDLE_CHECKED(Object, value, 3);

791
  LookupIterator::Key key(isolate, name);
792

793 794 795
  RETURN_RESULT_OR_FAILURE(
      isolate, StoreToSuper(isolate, home_object, receiver, &key, value,
                            StoreOrigin::kNamed));
796 797
}

798
RUNTIME_FUNCTION(Runtime_StoreKeyedToSuper) {
799
  HandleScope scope(isolate);
800
  DCHECK_EQ(4, args.length());
801 802
  CONVERT_ARG_HANDLE_CHECKED(Object, receiver, 0);
  CONVERT_ARG_HANDLE_CHECKED(JSObject, home_object, 1);
803 804
  // TODO(ishell): To improve performance, consider performing the to-string
  // conversion of {key} before calling into the runtime.
805 806 807
  CONVERT_ARG_HANDLE_CHECKED(Object, key, 2);
  CONVERT_ARG_HANDLE_CHECKED(Object, value, 3);

808 809 810 811
  bool success;
  LookupIterator::Key lookup_key(isolate, key, &success);
  if (!success) return ReadOnlyRoots(isolate).exception();

812
  RETURN_RESULT_OR_FAILURE(
813 814
      isolate, StoreToSuper(isolate, home_object, receiver, &lookup_key, value,
                            StoreOrigin::kMaybeKeyed));
815
}
816

817 818
}  // namespace internal
}  // namespace v8