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