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 |