| OLD | NEW |
| 1 // Copyright 2014 the V8 project authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "src/runtime/runtime-utils.h" | 5 #include "src/runtime/runtime-utils.h" |
| 6 | 6 |
| 7 #include "src/arguments.h" | 7 #include "src/arguments.h" |
| 8 #include "src/conversions-inl.h" | 8 #include "src/conversions-inl.h" |
| 9 #include "src/elements.h" | 9 #include "src/elements.h" |
| 10 #include "src/factory.h" | 10 #include "src/factory.h" |
| (...skipping 215 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 226 // TODO(adamk): Remove this step when the contract of %GetArrayKeys | 226 // TODO(adamk): Remove this step when the contract of %GetArrayKeys |
| 227 // is changed to let this happen on the JS side. | 227 // is changed to let this happen on the JS side. |
| 228 Handle<FixedArray> keys = accumulator.GetKeys(KEEP_NUMBERS); | 228 Handle<FixedArray> keys = accumulator.GetKeys(KEEP_NUMBERS); |
| 229 for (int i = 0; i < keys->length(); i++) { | 229 for (int i = 0; i < keys->length(); i++) { |
| 230 if (NumberToUint32(keys->get(i)) >= length) keys->set_undefined(i); | 230 if (NumberToUint32(keys->get(i)) >= length) keys->set_undefined(i); |
| 231 } | 231 } |
| 232 return *isolate->factory()->NewJSArrayWithElements(keys); | 232 return *isolate->factory()->NewJSArrayWithElements(keys); |
| 233 } | 233 } |
| 234 | 234 |
| 235 | 235 |
| 236 static Object* ArrayConstructorCommon(Isolate* isolate, | 236 namespace { |
| 237 Handle<JSFunction> constructor, | 237 |
| 238 Handle<JSFunction> new_target, | 238 Object* ArrayConstructorCommon(Isolate* isolate, Handle<JSFunction> constructor, |
| 239 Handle<AllocationSite> site, | 239 Handle<JSReceiver> new_target, |
| 240 Arguments* caller_args) { | 240 Handle<AllocationSite> site, |
| 241 Arguments* caller_args) { |
| 241 Factory* factory = isolate->factory(); | 242 Factory* factory = isolate->factory(); |
| 242 | 243 |
| 244 // If called through new, new.target can be: |
| 245 // - a subclass of constructor, |
| 246 // - a proxy wrapper around constructor, or |
| 247 // - the constructor itself. |
| 248 // If called through Reflect.construct, it's guaranteed to be a constructor by |
| 249 // REFLECT_CONSTRUCT_PREPARE. |
| 250 DCHECK(new_target->IsConstructor()); |
| 251 |
| 243 bool holey = false; | 252 bool holey = false; |
| 244 bool can_use_type_feedback = true; | 253 bool can_use_type_feedback = !site.is_null(); |
| 245 bool can_inline_array_constructor = true; | 254 bool can_inline_array_constructor = true; |
| 246 if (caller_args->length() == 1) { | 255 if (caller_args->length() == 1) { |
| 247 Handle<Object> argument_one = caller_args->at<Object>(0); | 256 Handle<Object> argument_one = caller_args->at<Object>(0); |
| 248 if (argument_one->IsSmi()) { | 257 if (argument_one->IsSmi()) { |
| 249 int value = Handle<Smi>::cast(argument_one)->value(); | 258 int value = Handle<Smi>::cast(argument_one)->value(); |
| 250 if (value < 0 || | 259 if (value < 0 || |
| 251 JSArray::SetLengthWouldNormalize(isolate->heap(), value)) { | 260 JSArray::SetLengthWouldNormalize(isolate->heap(), value)) { |
| 252 // the array is a dictionary in this case. | 261 // the array is a dictionary in this case. |
| 253 can_use_type_feedback = false; | 262 can_use_type_feedback = false; |
| 254 } else if (value != 0) { | 263 } else if (value != 0) { |
| 255 holey = true; | 264 holey = true; |
| 256 if (value >= JSArray::kInitialMaxFastElementArray) { | 265 if (value >= JSArray::kInitialMaxFastElementArray) { |
| 257 can_inline_array_constructor = false; | 266 can_inline_array_constructor = false; |
| 258 } | 267 } |
| 259 } | 268 } |
| 260 } else { | 269 } else { |
| 261 // Non-smi length argument produces a dictionary | 270 // Non-smi length argument produces a dictionary |
| 262 can_use_type_feedback = false; | 271 can_use_type_feedback = false; |
| 263 } | 272 } |
| 264 } | 273 } |
| 265 | 274 |
| 266 Handle<JSArray> array; | 275 // TODO(verwaest): new_target could be a proxy. Read new.target.prototype in |
| 267 if (!site.is_null() && can_use_type_feedback) { | 276 // that case. |
| 268 ElementsKind to_kind = site->GetElementsKind(); | 277 Handle<JSFunction> original_function = Handle<JSFunction>::cast(new_target); |
| 269 if (holey && !IsFastHoleyElementsKind(to_kind)) { | |
| 270 to_kind = GetHoleyElementsKind(to_kind); | |
| 271 // Update the allocation site info to reflect the advice alteration. | |
| 272 site->SetElementsKind(to_kind); | |
| 273 } | |
| 274 | 278 |
| 275 // We should allocate with an initial map that reflects the allocation site | 279 JSFunction::EnsureHasInitialMap(constructor); |
| 276 // advice. Therefore we use AllocateJSObjectFromMap instead of passing | |
| 277 // the constructor. | |
| 278 Handle<Map> initial_map(constructor->initial_map(), isolate); | |
| 279 if (to_kind != initial_map->elements_kind()) { | |
| 280 initial_map = Map::AsElementsKind(initial_map, to_kind); | |
| 281 } | |
| 282 | 280 |
| 283 // If we don't care to track arrays of to_kind ElementsKind, then | 281 // TODO(verwaest): original_function could have non-instance-prototype |
| 284 // don't emit a memento for them. | 282 // (non-JSReceiver), requiring fallback to the intrinsicDefaultProto. |
| 285 Handle<AllocationSite> allocation_site; | 283 Handle<Map> initial_map = |
| 286 if (AllocationSite::GetMode(to_kind) == TRACK_ALLOCATION_SITE) { | 284 JSFunction::EnsureDerivedHasInitialMap(original_function, constructor); |
| 287 allocation_site = site; | |
| 288 } | |
| 289 | 285 |
| 290 array = Handle<JSArray>::cast( | 286 ElementsKind to_kind = can_use_type_feedback ? site->GetElementsKind() |
| 291 factory->NewJSObjectFromMap(initial_map, NOT_TENURED, allocation_site)); | 287 : initial_map->elements_kind(); |
| 292 } else { | 288 if (holey && !IsFastHoleyElementsKind(to_kind)) { |
| 293 array = Handle<JSArray>::cast(factory->NewJSObject(constructor)); | 289 to_kind = GetHoleyElementsKind(to_kind); |
| 290 // Update the allocation site info to reflect the advice alteration. |
| 291 if (!site.is_null()) site->SetElementsKind(to_kind); |
| 292 } |
| 294 | 293 |
| 295 // We might need to transition to holey | 294 // We should allocate with an initial map that reflects the allocation site |
| 296 ElementsKind kind = constructor->initial_map()->elements_kind(); | 295 // advice. Therefore we use AllocateJSObjectFromMap instead of passing |
| 297 if (holey && !IsFastHoleyElementsKind(kind)) { | 296 // the constructor. |
| 298 kind = GetHoleyElementsKind(kind); | 297 if (to_kind != initial_map->elements_kind()) { |
| 299 JSObject::TransitionElementsKind(array, kind); | 298 initial_map = Map::AsElementsKind(initial_map, to_kind); |
| 300 } | |
| 301 } | 299 } |
| 302 | 300 |
| 301 // If we don't care to track arrays of to_kind ElementsKind, then |
| 302 // don't emit a memento for them. |
| 303 Handle<AllocationSite> allocation_site; |
| 304 if (AllocationSite::GetMode(to_kind) == TRACK_ALLOCATION_SITE) { |
| 305 allocation_site = site; |
| 306 } |
| 307 |
| 308 Handle<JSArray> array = Handle<JSArray>::cast( |
| 309 factory->NewJSObjectFromMap(initial_map, NOT_TENURED, allocation_site)); |
| 310 |
| 303 factory->NewJSArrayStorage(array, 0, 0, DONT_INITIALIZE_ARRAY_ELEMENTS); | 311 factory->NewJSArrayStorage(array, 0, 0, DONT_INITIALIZE_ARRAY_ELEMENTS); |
| 304 | 312 |
| 305 ElementsKind old_kind = array->GetElementsKind(); | 313 ElementsKind old_kind = array->GetElementsKind(); |
| 306 RETURN_FAILURE_ON_EXCEPTION( | 314 RETURN_FAILURE_ON_EXCEPTION( |
| 307 isolate, ArrayConstructInitializeElements(array, caller_args)); | 315 isolate, ArrayConstructInitializeElements(array, caller_args)); |
| 308 if (!site.is_null() && | 316 if (!site.is_null() && |
| 309 (old_kind != array->GetElementsKind() || !can_use_type_feedback || | 317 (old_kind != array->GetElementsKind() || !can_use_type_feedback || |
| 310 !can_inline_array_constructor)) { | 318 !can_inline_array_constructor)) { |
| 311 // The arguments passed in caused a transition. This kind of complexity | 319 // The arguments passed in caused a transition. This kind of complexity |
| 312 // can't be dealt with in the inlined hydrogen array constructor case. | 320 // can't be dealt with in the inlined hydrogen array constructor case. |
| 313 // We must mark the allocationsite as un-inlinable. | 321 // We must mark the allocationsite as un-inlinable. |
| 314 site->SetDoNotInlineCall(); | 322 site->SetDoNotInlineCall(); |
| 315 } | 323 } |
| 316 | 324 |
| 317 // Set up the prototoype using original function. | |
| 318 // TODO(dslomov): instead of setting the __proto__, | |
| 319 // use and cache the correct map. | |
| 320 if (*new_target != *constructor) { | |
| 321 if (new_target->has_instance_prototype()) { | |
| 322 Handle<Object> prototype(new_target->instance_prototype(), isolate); | |
| 323 MAYBE_RETURN(JSObject::SetPrototype(array, prototype, false, | |
| 324 Object::THROW_ON_ERROR), | |
| 325 isolate->heap()->exception()); | |
| 326 } | |
| 327 } | |
| 328 | |
| 329 return *array; | 325 return *array; |
| 330 } | 326 } |
| 331 | 327 |
| 328 } // namespace |
| 329 |
| 330 |
| 331 RUNTIME_FUNCTION(Runtime_NewArray) { |
| 332 HandleScope scope(isolate); |
| 333 DCHECK_LE(3, args.length()); |
| 334 int const argc = args.length() - 3; |
| 335 // TODO(bmeurer): Remove this Arguments nonsense. |
| 336 Arguments argv(argc, args.arguments() - 1); |
| 337 CONVERT_ARG_HANDLE_CHECKED(JSFunction, constructor, 0); |
| 338 CONVERT_ARG_HANDLE_CHECKED(JSReceiver, new_target, argc + 1); |
| 339 CONVERT_ARG_HANDLE_CHECKED(HeapObject, type_info, argc + 2); |
| 340 // TODO(bmeurer): Use MaybeHandle to pass around the AllocationSite. |
| 341 Handle<AllocationSite> site = type_info->IsAllocationSite() |
| 342 ? Handle<AllocationSite>::cast(type_info) |
| 343 : Handle<AllocationSite>::null(); |
| 344 return ArrayConstructorCommon(isolate, constructor, new_target, site, &argv); |
| 345 } |
| 346 |
| 332 | 347 |
| 333 RUNTIME_FUNCTION(Runtime_ArrayConstructor) { | 348 RUNTIME_FUNCTION(Runtime_ArrayConstructor) { |
| 334 HandleScope scope(isolate); | 349 HandleScope scope(isolate); |
| 335 // If we get 2 arguments then they are the stub parameters (constructor, type | 350 // If we get 2 arguments then they are the stub parameters (constructor, type |
| 336 // info). If we get 4, then the first one is a pointer to the arguments | 351 // info). If we get 4, then the first one is a pointer to the arguments |
| 337 // passed by the caller, and the last one is the length of the arguments | 352 // passed by the caller, and the last one is the length of the arguments |
| 338 // passed to the caller (redundant, but useful to check on the deoptimizer | 353 // passed to the caller (redundant, but useful to check on the deoptimizer |
| 339 // with an assert). | 354 // with an assert). |
| 340 Arguments empty_args(0, NULL); | 355 Arguments empty_args(0, NULL); |
| 341 bool no_caller_args = args.length() == 2; | 356 bool no_caller_args = args.length() == 2; |
| (...skipping 15 matching lines...) Expand all Loading... |
| 357 *type_info != isolate->heap()->undefined_value()) { | 372 *type_info != isolate->heap()->undefined_value()) { |
| 358 site = Handle<AllocationSite>::cast(type_info); | 373 site = Handle<AllocationSite>::cast(type_info); |
| 359 DCHECK(!site->SitePointsToLiteral()); | 374 DCHECK(!site->SitePointsToLiteral()); |
| 360 } | 375 } |
| 361 | 376 |
| 362 return ArrayConstructorCommon(isolate, constructor, constructor, site, | 377 return ArrayConstructorCommon(isolate, constructor, constructor, site, |
| 363 caller_args); | 378 caller_args); |
| 364 } | 379 } |
| 365 | 380 |
| 366 | 381 |
| 367 RUNTIME_FUNCTION(Runtime_ArrayConstructorWithSubclassing) { | |
| 368 HandleScope scope(isolate); | |
| 369 int args_length = args.length(); | |
| 370 CHECK(args_length >= 2); | |
| 371 | |
| 372 // This variables and checks work around -Werror=strict-overflow. | |
| 373 int pre_last_arg_index = args_length - 2; | |
| 374 int last_arg_index = args_length - 1; | |
| 375 CHECK(pre_last_arg_index >= 0); | |
| 376 CHECK(last_arg_index >= 0); | |
| 377 | |
| 378 CONVERT_ARG_HANDLE_CHECKED(JSFunction, constructor, pre_last_arg_index); | |
| 379 CONVERT_ARG_HANDLE_CHECKED(JSFunction, new_target, last_arg_index); | |
| 380 Arguments caller_args(args_length - 2, args.arguments()); | |
| 381 return ArrayConstructorCommon(isolate, constructor, new_target, | |
| 382 Handle<AllocationSite>::null(), &caller_args); | |
| 383 } | |
| 384 | |
| 385 | |
| 386 RUNTIME_FUNCTION(Runtime_InternalArrayConstructor) { | 382 RUNTIME_FUNCTION(Runtime_InternalArrayConstructor) { |
| 387 HandleScope scope(isolate); | 383 HandleScope scope(isolate); |
| 388 Arguments empty_args(0, NULL); | 384 Arguments empty_args(0, NULL); |
| 389 bool no_caller_args = args.length() == 1; | 385 bool no_caller_args = args.length() == 1; |
| 390 DCHECK(no_caller_args || args.length() == 3); | 386 DCHECK(no_caller_args || args.length() == 3); |
| 391 int parameters_start = no_caller_args ? 0 : 1; | 387 int parameters_start = no_caller_args ? 0 : 1; |
| 392 Arguments* caller_args = | 388 Arguments* caller_args = |
| 393 no_caller_args ? &empty_args : reinterpret_cast<Arguments*>(args[0]); | 389 no_caller_args ? &empty_args : reinterpret_cast<Arguments*>(args[0]); |
| 394 CONVERT_ARG_HANDLE_CHECKED(JSFunction, constructor, parameters_start); | 390 CONVERT_ARG_HANDLE_CHECKED(JSFunction, constructor, parameters_start); |
| 395 #ifdef DEBUG | 391 #ifdef DEBUG |
| (...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 492 | 488 |
| 493 RUNTIME_FUNCTION(Runtime_FastOneByteArrayJoin) { | 489 RUNTIME_FUNCTION(Runtime_FastOneByteArrayJoin) { |
| 494 SealHandleScope shs(isolate); | 490 SealHandleScope shs(isolate); |
| 495 DCHECK(args.length() == 2); | 491 DCHECK(args.length() == 2); |
| 496 // Returning undefined means that this fast path fails and one has to resort | 492 // Returning undefined means that this fast path fails and one has to resort |
| 497 // to a slow path. | 493 // to a slow path. |
| 498 return isolate->heap()->undefined_value(); | 494 return isolate->heap()->undefined_value(); |
| 499 } | 495 } |
| 500 } // namespace internal | 496 } // namespace internal |
| 501 } // namespace v8 | 497 } // namespace v8 |
| OLD | NEW |