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 14namespace v8 { 15namespace internal { 16 17static 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 36MUST_USE_RESULT static MaybeHandle<Object> CreateLiteralBoilerplate( 37 Isolate* isolate, Handle<LiteralsArray> literals, 38 Handle<FixedArray> constant_properties); 39 40MUST_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::kZero, 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 114static 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 186MUST_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 204RUNTIME_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 223RUNTIME_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 271MUST_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 302static 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 326RUNTIME_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 341RUNTIME_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