Home | History | Annotate | Download | only in runtime
      1 // Copyright 2014 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/runtime/runtime-utils.h"
      6 
      7 #include "src/allocation-site-scopes.h"
      8 #include "src/arguments.h"
      9 #include "src/ast/ast.h"
     10 #include "src/ast/compile-time-value.h"
     11 #include "src/isolate-inl.h"
     12 #include "src/runtime/runtime.h"
     13 
     14 namespace v8 {
     15 namespace internal {
     16 
     17 static Handle<Map> ComputeObjectLiteralMap(
     18     Handle<Context> context,
     19     Handle<BoilerplateDescription> boilerplate_description,
     20     bool* is_result_from_cache) {
     21   int number_of_properties = boilerplate_description->backing_store_size();
     22   Isolate* isolate = context->GetIsolate();
     23   return isolate->factory()->ObjectLiteralMapFromCache(
     24       context, number_of_properties, is_result_from_cache);
     25 }
     26 
     27 MUST_USE_RESULT static MaybeHandle<Object> CreateLiteralBoilerplate(
     28     Isolate* isolate, Handle<FeedbackVector> vector,
     29     Handle<BoilerplateDescription> boilerplate_description);
     30 
     31 MUST_USE_RESULT static MaybeHandle<Object> CreateObjectLiteralBoilerplate(
     32     Isolate* isolate, Handle<FeedbackVector> vector,
     33     Handle<BoilerplateDescription> boilerplate_description,
     34     bool should_have_fast_elements) {
     35   Handle<Context> context = isolate->native_context();
     36 
     37   // In case we have function literals, we want the object to be in
     38   // slow properties mode for now. We don't go in the map cache because
     39   // maps with constant functions can't be shared if the functions are
     40   // not the same (which is the common case).
     41   bool is_result_from_cache = false;
     42   Handle<Map> map = ComputeObjectLiteralMap(context, boilerplate_description,
     43                                             &is_result_from_cache);
     44 
     45   PretenureFlag pretenure_flag =
     46       isolate->heap()->InNewSpace(*vector) ? NOT_TENURED : TENURED;
     47 
     48   Handle<JSObject> boilerplate =
     49       isolate->factory()->NewJSObjectFromMap(map, pretenure_flag);
     50 
     51   // Normalize the elements of the boilerplate to save space if needed.
     52   if (!should_have_fast_elements) JSObject::NormalizeElements(boilerplate);
     53 
     54   // Add the constant properties to the boilerplate.
     55   int length = boilerplate_description->size();
     56   bool should_transform =
     57       !is_result_from_cache && boilerplate->HasFastProperties();
     58   bool should_normalize = should_transform;
     59   if (should_normalize) {
     60     // TODO(verwaest): We might not want to ever normalize here.
     61     JSObject::NormalizeProperties(boilerplate, KEEP_INOBJECT_PROPERTIES, length,
     62                                   "Boilerplate");
     63   }
     64   // TODO(verwaest): Support tracking representations in the boilerplate.
     65   for (int index = 0; index < length; index++) {
     66     Handle<Object> key(boilerplate_description->name(index), isolate);
     67     Handle<Object> value(boilerplate_description->value(index), isolate);
     68     if (value->IsBoilerplateDescription()) {
     69       // The value contains the boilerplate properties of a
     70       // simple object or array literal.
     71       Handle<BoilerplateDescription> boilerplate =
     72           Handle<BoilerplateDescription>::cast(value);
     73       ASSIGN_RETURN_ON_EXCEPTION(
     74           isolate, value,
     75           CreateLiteralBoilerplate(isolate, vector, boilerplate), Object);
     76     }
     77     MaybeHandle<Object> maybe_result;
     78     uint32_t element_index = 0;
     79     if (key->ToArrayIndex(&element_index)) {
     80       // Array index (uint32).
     81       if (value->IsUninitialized(isolate)) {
     82         value = handle(Smi::kZero, isolate);
     83       }
     84       maybe_result = JSObject::SetOwnElementIgnoreAttributes(
     85           boilerplate, element_index, value, NONE);
     86     } else {
     87       Handle<String> name = Handle<String>::cast(key);
     88       DCHECK(!name->AsArrayIndex(&element_index));
     89       maybe_result = JSObject::SetOwnPropertyIgnoreAttributes(boilerplate, name,
     90                                                               value, NONE);
     91     }
     92     RETURN_ON_EXCEPTION(isolate, maybe_result, Object);
     93   }
     94 
     95   // Transform to fast properties if necessary. For object literals with
     96   // containing function literals we defer this operation until after all
     97   // computed properties have been assigned so that we can generate
     98   // constant function properties.
     99   if (should_transform) {
    100     JSObject::MigrateSlowToFast(boilerplate,
    101                                 boilerplate->map()->unused_property_fields(),
    102                                 "FastLiteral");
    103   }
    104   return boilerplate;
    105 }
    106 
    107 static MaybeHandle<Object> CreateArrayLiteralBoilerplate(
    108     Isolate* isolate, Handle<FeedbackVector> vector,
    109     Handle<ConstantElementsPair> elements) {
    110   // Create the JSArray.
    111   Handle<JSFunction> constructor = isolate->array_function();
    112 
    113   PretenureFlag pretenure_flag =
    114       isolate->heap()->InNewSpace(*vector) ? NOT_TENURED : TENURED;
    115 
    116   Handle<JSArray> object = Handle<JSArray>::cast(
    117       isolate->factory()->NewJSObject(constructor, pretenure_flag));
    118 
    119   ElementsKind constant_elements_kind =
    120       static_cast<ElementsKind>(elements->elements_kind());
    121   Handle<FixedArrayBase> constant_elements_values(elements->constant_values());
    122 
    123   {
    124     DisallowHeapAllocation no_gc;
    125     DCHECK(IsFastElementsKind(constant_elements_kind));
    126     Context* native_context = isolate->context()->native_context();
    127     Object* map =
    128         native_context->get(Context::ArrayMapIndex(constant_elements_kind));
    129     object->set_map(Map::cast(map));
    130   }
    131 
    132   Handle<FixedArrayBase> copied_elements_values;
    133   if (IsFastDoubleElementsKind(constant_elements_kind)) {
    134     copied_elements_values = isolate->factory()->CopyFixedDoubleArray(
    135         Handle<FixedDoubleArray>::cast(constant_elements_values));
    136   } else {
    137     DCHECK(IsFastSmiOrObjectElementsKind(constant_elements_kind));
    138     const bool is_cow = (constant_elements_values->map() ==
    139                          isolate->heap()->fixed_cow_array_map());
    140     if (is_cow) {
    141       copied_elements_values = constant_elements_values;
    142 #if DEBUG
    143       Handle<FixedArray> fixed_array_values =
    144           Handle<FixedArray>::cast(copied_elements_values);
    145       for (int i = 0; i < fixed_array_values->length(); i++) {
    146         DCHECK(!fixed_array_values->get(i)->IsFixedArray());
    147       }
    148 #endif
    149     } else {
    150       Handle<FixedArray> fixed_array_values =
    151           Handle<FixedArray>::cast(constant_elements_values);
    152       Handle<FixedArray> fixed_array_values_copy =
    153           isolate->factory()->CopyFixedArray(fixed_array_values);
    154       copied_elements_values = fixed_array_values_copy;
    155       FOR_WITH_HANDLE_SCOPE(
    156           isolate, int, i = 0, i, i < fixed_array_values->length(), i++, {
    157             if (fixed_array_values->get(i)->IsBoilerplateDescription()) {
    158               // The value contains the boilerplate properties of a
    159               // simple object or array literal.
    160               Handle<BoilerplateDescription> boilerplate(
    161                   BoilerplateDescription::cast(fixed_array_values->get(i)));
    162               Handle<Object> result;
    163               ASSIGN_RETURN_ON_EXCEPTION(
    164                   isolate, result,
    165                   CreateLiteralBoilerplate(isolate, vector, boilerplate),
    166                   Object);
    167               fixed_array_values_copy->set(i, *result);
    168             }
    169           });
    170     }
    171   }
    172   object->set_elements(*copied_elements_values);
    173   object->set_length(Smi::FromInt(copied_elements_values->length()));
    174 
    175   JSObject::ValidateElements(object);
    176   return object;
    177 }
    178 
    179 MUST_USE_RESULT static MaybeHandle<Object> CreateLiteralBoilerplate(
    180     Isolate* isolate, Handle<FeedbackVector> vector,
    181     Handle<BoilerplateDescription> array) {
    182   Handle<HeapObject> elements = CompileTimeValue::GetElements(array);
    183   switch (CompileTimeValue::GetLiteralType(array)) {
    184     case CompileTimeValue::OBJECT_LITERAL_FAST_ELEMENTS: {
    185       Handle<BoilerplateDescription> props =
    186           Handle<BoilerplateDescription>::cast(elements);
    187       return CreateObjectLiteralBoilerplate(isolate, vector, props, true);
    188     }
    189     case CompileTimeValue::OBJECT_LITERAL_SLOW_ELEMENTS: {
    190       Handle<BoilerplateDescription> props =
    191           Handle<BoilerplateDescription>::cast(elements);
    192       return CreateObjectLiteralBoilerplate(isolate, vector, props, false);
    193     }
    194     case CompileTimeValue::ARRAY_LITERAL: {
    195       Handle<ConstantElementsPair> elems =
    196           Handle<ConstantElementsPair>::cast(elements);
    197       return CreateArrayLiteralBoilerplate(isolate, vector, elems);
    198     }
    199     default:
    200       UNREACHABLE();
    201       return MaybeHandle<Object>();
    202   }
    203 }
    204 
    205 
    206 RUNTIME_FUNCTION(Runtime_CreateRegExpLiteral) {
    207   HandleScope scope(isolate);
    208   DCHECK_EQ(4, args.length());
    209   CONVERT_ARG_HANDLE_CHECKED(JSFunction, closure, 0);
    210   CONVERT_SMI_ARG_CHECKED(index, 1);
    211   CONVERT_ARG_HANDLE_CHECKED(String, pattern, 2);
    212   CONVERT_SMI_ARG_CHECKED(flags, 3);
    213   FeedbackSlot literal_slot(FeedbackVector::ToSlot(index));
    214 
    215   // Check if boilerplate exists. If not, create it first.
    216   Handle<Object> boilerplate(closure->feedback_vector()->Get(literal_slot),
    217                              isolate);
    218   if (boilerplate->IsUndefined(isolate)) {
    219     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
    220         isolate, boilerplate, JSRegExp::New(pattern, JSRegExp::Flags(flags)));
    221     closure->feedback_vector()->Set(literal_slot, *boilerplate);
    222   }
    223   return *JSRegExp::Copy(Handle<JSRegExp>::cast(boilerplate));
    224 }
    225 
    226 
    227 RUNTIME_FUNCTION(Runtime_CreateObjectLiteral) {
    228   HandleScope scope(isolate);
    229   DCHECK_EQ(4, args.length());
    230   CONVERT_ARG_HANDLE_CHECKED(JSFunction, closure, 0);
    231   CONVERT_SMI_ARG_CHECKED(literals_index, 1);
    232   CONVERT_ARG_HANDLE_CHECKED(BoilerplateDescription, boilerplate_description,
    233                              2);
    234   CONVERT_SMI_ARG_CHECKED(flags, 3);
    235   Handle<FeedbackVector> vector(closure->feedback_vector(), isolate);
    236   bool should_have_fast_elements = (flags & ObjectLiteral::kFastElements) != 0;
    237   bool enable_mementos = (flags & ObjectLiteral::kDisableMementos) == 0;
    238 
    239   FeedbackSlot literals_slot(FeedbackVector::ToSlot(literals_index));
    240   CHECK(literals_slot.ToInt() < vector->slot_count());
    241 
    242   // Check if boilerplate exists. If not, create it first.
    243   Handle<Object> literal_site(vector->Get(literals_slot), isolate);
    244   Handle<AllocationSite> site;
    245   Handle<JSObject> boilerplate;
    246   if (literal_site->IsUndefined(isolate)) {
    247     Handle<Object> raw_boilerplate;
    248     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
    249         isolate, raw_boilerplate,
    250         CreateObjectLiteralBoilerplate(isolate, vector, boilerplate_description,
    251                                        should_have_fast_elements));
    252     boilerplate = Handle<JSObject>::cast(raw_boilerplate);
    253 
    254     AllocationSiteCreationContext creation_context(isolate);
    255     site = creation_context.EnterNewScope();
    256     RETURN_FAILURE_ON_EXCEPTION(
    257         isolate, JSObject::DeepWalk(boilerplate, &creation_context));
    258     creation_context.ExitScope(site, boilerplate);
    259 
    260     // Update the functions literal and return the boilerplate.
    261     vector->Set(literals_slot, *site);
    262   } else {
    263     site = Handle<AllocationSite>::cast(literal_site);
    264     boilerplate =
    265         Handle<JSObject>(JSObject::cast(site->transition_info()), isolate);
    266   }
    267 
    268   AllocationSiteUsageContext usage_context(isolate, site, enable_mementos);
    269   usage_context.EnterNewScope();
    270   MaybeHandle<Object> maybe_copy =
    271       JSObject::DeepCopy(boilerplate, &usage_context);
    272   usage_context.ExitScope(site, boilerplate);
    273   RETURN_RESULT_OR_FAILURE(isolate, maybe_copy);
    274 }
    275 
    276 MUST_USE_RESULT static MaybeHandle<AllocationSite> GetLiteralAllocationSite(
    277     Isolate* isolate, Handle<FeedbackVector> vector, FeedbackSlot literals_slot,
    278     Handle<ConstantElementsPair> elements) {
    279   // Check if boilerplate exists. If not, create it first.
    280   Handle<Object> literal_site(vector->Get(literals_slot), isolate);
    281   Handle<AllocationSite> site;
    282   if (literal_site->IsUndefined(isolate)) {
    283     Handle<Object> boilerplate;
    284     ASSIGN_RETURN_ON_EXCEPTION(
    285         isolate, boilerplate,
    286         CreateArrayLiteralBoilerplate(isolate, vector, elements),
    287         AllocationSite);
    288 
    289     AllocationSiteCreationContext creation_context(isolate);
    290     site = creation_context.EnterNewScope();
    291     if (JSObject::DeepWalk(Handle<JSObject>::cast(boilerplate),
    292                            &creation_context).is_null()) {
    293       return Handle<AllocationSite>::null();
    294     }
    295     creation_context.ExitScope(site, Handle<JSObject>::cast(boilerplate));
    296 
    297     vector->Set(literals_slot, *site);
    298   } else {
    299     site = Handle<AllocationSite>::cast(literal_site);
    300   }
    301 
    302   return site;
    303 }
    304 
    305 static MaybeHandle<JSObject> CreateArrayLiteralImpl(
    306     Isolate* isolate, Handle<FeedbackVector> vector, FeedbackSlot literals_slot,
    307     Handle<ConstantElementsPair> elements, int flags) {
    308   CHECK(literals_slot.ToInt() < vector->slot_count());
    309   Handle<AllocationSite> site;
    310   ASSIGN_RETURN_ON_EXCEPTION(
    311       isolate, site,
    312       GetLiteralAllocationSite(isolate, vector, literals_slot, elements),
    313       JSObject);
    314 
    315   bool enable_mementos = (flags & ArrayLiteral::kDisableMementos) == 0;
    316   Handle<JSObject> boilerplate(JSObject::cast(site->transition_info()));
    317   AllocationSiteUsageContext usage_context(isolate, site, enable_mementos);
    318   usage_context.EnterNewScope();
    319   JSObject::DeepCopyHints hints = (flags & ArrayLiteral::kShallowElements) == 0
    320                                       ? JSObject::kNoHints
    321                                       : JSObject::kObjectIsShallow;
    322   MaybeHandle<JSObject> copy =
    323       JSObject::DeepCopy(boilerplate, &usage_context, hints);
    324   usage_context.ExitScope(site, boilerplate);
    325   return copy;
    326 }
    327 
    328 
    329 RUNTIME_FUNCTION(Runtime_CreateArrayLiteral) {
    330   HandleScope scope(isolate);
    331   DCHECK_EQ(4, args.length());
    332   CONVERT_ARG_HANDLE_CHECKED(JSFunction, closure, 0);
    333   CONVERT_SMI_ARG_CHECKED(literals_index, 1);
    334   CONVERT_ARG_HANDLE_CHECKED(ConstantElementsPair, elements, 2);
    335   CONVERT_SMI_ARG_CHECKED(flags, 3);
    336 
    337   FeedbackSlot literals_slot(FeedbackVector::ToSlot(literals_index));
    338   Handle<FeedbackVector> vector(closure->feedback_vector(), isolate);
    339   RETURN_RESULT_OR_FAILURE(
    340       isolate,
    341       CreateArrayLiteralImpl(isolate, vector, literals_slot, elements, flags));
    342 }
    343 
    344 
    345 RUNTIME_FUNCTION(Runtime_CreateArrayLiteralStubBailout) {
    346   HandleScope scope(isolate);
    347   DCHECK_EQ(3, args.length());
    348   CONVERT_ARG_HANDLE_CHECKED(JSFunction, closure, 0);
    349   CONVERT_SMI_ARG_CHECKED(literals_index, 1);
    350   CONVERT_ARG_HANDLE_CHECKED(ConstantElementsPair, elements, 2);
    351 
    352   Handle<FeedbackVector> vector(closure->feedback_vector(), isolate);
    353   FeedbackSlot literals_slot(FeedbackVector::ToSlot(literals_index));
    354   RETURN_RESULT_OR_FAILURE(
    355       isolate, CreateArrayLiteralImpl(isolate, vector, literals_slot, elements,
    356                                       ArrayLiteral::kShallowElements));
    357 }
    358 
    359 }  // namespace internal
    360 }  // namespace v8
    361