OLD | NEW |
---|---|
1 // Copyright 2016 the V8 project authors. All rights reserved. | 1 // Copyright 2016 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/builtins/builtins.h" | 5 #include "src/builtins/builtins.h" |
6 #include "src/builtins/builtins-utils.h" | 6 #include "src/builtins/builtins-utils.h" |
7 | 7 |
8 #include "src/code-factory.h" | |
8 #include "src/compiler.h" | 9 #include "src/compiler.h" |
9 #include "src/string-builder.h" | 10 #include "src/string-builder.h" |
10 | 11 |
11 namespace v8 { | 12 namespace v8 { |
12 namespace internal { | 13 namespace internal { |
13 | 14 |
14 namespace { | 15 namespace { |
15 | 16 |
16 // ES6 section 19.2.1.1.1 CreateDynamicFunction | 17 // ES6 section 19.2.1.1.1 CreateDynamicFunction |
17 MaybeHandle<Object> CreateDynamicFunction(Isolate* isolate, | 18 MaybeHandle<Object> CreateDynamicFunction(Isolate* isolate, |
(...skipping 230 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
248 &it, name, it.property_attributes())); | 249 &it, name, it.property_attributes())); |
249 } | 250 } |
250 return *function; | 251 return *function; |
251 } | 252 } |
252 | 253 |
253 } // namespace | 254 } // namespace |
254 | 255 |
255 // ES6 section 19.2.3.2 Function.prototype.bind ( thisArg, ...args ) | 256 // ES6 section 19.2.3.2 Function.prototype.bind ( thisArg, ...args ) |
256 BUILTIN(FunctionPrototypeBind) { return DoFunctionBind(isolate, args); } | 257 BUILTIN(FunctionPrototypeBind) { return DoFunctionBind(isolate, args); } |
257 | 258 |
259 void Builtins::Generate_FastFunctionPrototypeBind( | |
260 compiler::CodeAssemblerState* state) { | |
261 using compiler::Node; | |
262 typedef CodeStubAssembler::Label Label; | |
263 typedef CodeStubAssembler::Variable Variable; | |
264 | |
265 CodeStubAssembler assembler(state); | |
266 Label slow(&assembler); | |
267 | |
268 Node* argc = assembler.Parameter(1); | |
Igor Sheludko
2016/12/01 11:50:41
1) BuiltinDescriptor::kArgumentsCount?
2) argc is
danno
2016/12/01 15:43:33
Done.
| |
269 Node* context = assembler.Parameter(2); | |
Igor Sheludko
2016/12/01 11:50:40
BuiltinDescriptor::kContext
danno
2016/12/01 15:43:33
Done.
| |
270 Node* new_target = assembler.Parameter(0); | |
Igor Sheludko
2016/12/01 11:50:40
BuiltinDescriptor::kNewTarget
danno
2016/12/01 15:43:33
Done.
| |
271 | |
272 CodeStubArguments args(&assembler, argc); | |
273 | |
274 // Check that receiver has instance type of JS_FUNCTION_TYPE | |
275 Node* receiver = args.GetReceiver(); | |
276 assembler.GotoIf(assembler.TaggedIsSmi(receiver), &slow); | |
277 | |
278 Node* receiver_map = assembler.LoadMap(receiver); | |
279 Node* instance_type = assembler.LoadMapInstanceType(receiver_map); | |
280 assembler.GotoIf( | |
281 assembler.Word32NotEqual(instance_type, | |
282 assembler.Int32Constant(JS_FUNCTION_TYPE)), | |
283 &slow); | |
284 | |
285 // Disallow binding of slow-mode functions. We need to figure out whether the | |
286 // length and name property are in the original state. | |
287 assembler.Comment("Disallow binding of slow-mode functions"); | |
288 Node* bit_field3 = assembler.LoadObjectField( | |
289 receiver_map, Map::kBitField3Offset, MachineType::Uint32()); | |
290 int mask = static_cast<int>(Map::DictionaryMap::kMask); | |
291 Node* mask_node = assembler.Int32Constant(mask); | |
292 Node* test = assembler.Word32And(bit_field3, mask_node); | |
293 assembler.GotoIf(assembler.Word32Equal(test, mask_node), &slow); | |
Igor Sheludko
2016/12/01 11:50:40
You can use IsDictionaryMap(receiver_map) here.
danno
2016/12/01 15:43:33
Done.
| |
294 | |
295 // Check whether the length and name properties are still present as | |
296 // AccessorInfo objects. In that case, their value can be recomputed even if | |
297 // the actual value on the object changes. | |
298 assembler.Comment("Check descriptor array length"); | |
299 Node* descriptors = | |
300 assembler.LoadObjectField(receiver_map, Map::kDescriptorsOffset); | |
301 CodeStubAssembler::ParameterMode mode = assembler.OptimalParameterMode(); | |
Igor Sheludko
2016/12/01 11:50:40
descriptors_length is not used anywhere else so yo
danno
2016/12/01 15:43:32
Done.
| |
302 Node* descriptors_length = assembler.UntagParameter( | |
303 assembler.LoadFixedArrayBaseLength(descriptors), mode); | |
304 assembler.GotoIf( | |
305 assembler.IntPtrLessThanOrEqual(descriptors_length, | |
Igor Sheludko
2016/12/01 11:50:40
I prefer to use unsigned comparisons for lengths.
danno
2016/12/01 15:43:33
Acknowledged.
| |
306 assembler.IntPtrOrSmiConstant(1, mode)), | |
307 &slow); | |
308 | |
309 // Check whether the length and name properties are still present as | |
310 // AccessorInfo objects. In that case, their value can be recomputed even if | |
311 // the actual value on the object changes. | |
312 assembler.Comment("Check name and length properties"); | |
313 const int length_index = JSFunction::kLengthDescriptorIndex; | |
314 Node* maybe_length = assembler.LoadFixedArrayElement( | |
315 descriptors, | |
316 assembler.Int32Constant(DescriptorArray::ToKeyIndex(length_index))); | |
Igor Sheludko
2016/12/01 11:50:40
Use IntPtrConstant() and pass INTPTR_PARAMETERS mo
danno
2016/12/01 15:43:33
Done.
| |
317 assembler.GotoIf( | |
318 assembler.WordNotEqual( | |
319 maybe_length, assembler.HeapConstant( | |
Igor Sheludko
2016/12/01 11:50:40
LoadRoot
danno
2016/12/01 15:43:33
Done.
| |
320 assembler.isolate()->factory()->length_string())), | |
321 &slow); | |
322 | |
323 Node* maybe_length_accessor = assembler.LoadFixedArrayElement( | |
324 descriptors, | |
325 assembler.Int32Constant(DescriptorArray::ToValueIndex(length_index))); | |
326 assembler.GotoIf(assembler.TaggedIsSmi(maybe_length_accessor), &slow); | |
327 Node* length_value_map = assembler.LoadMap(maybe_length_accessor); | |
328 assembler.GotoIf( | |
Igor Sheludko
2016/12/01 11:50:40
GotoUnless(IsAccessorInfoMap(length_value_map), ..
danno
2016/12/01 15:43:33
Done.
| |
329 assembler.WordNotEqual( | |
330 length_value_map, | |
331 assembler.HeapConstant( | |
332 assembler.isolate()->factory()->accessor_info_map())), | |
333 &slow); | |
334 | |
335 const int name_index = JSFunction::kNameDescriptorIndex; | |
336 Node* maybe_name = assembler.LoadFixedArrayElement( | |
337 descriptors, | |
338 assembler.Int32Constant(DescriptorArray::ToKeyIndex(name_index))); | |
Igor Sheludko
2016/12/01 11:50:40
Same here.
danno
2016/12/01 15:43:33
Done.
| |
339 assembler.GotoIf( | |
340 assembler.WordNotEqual( | |
341 maybe_name, assembler.HeapConstant( | |
Igor Sheludko
2016/12/01 11:50:40
LoadRoot.
danno
2016/12/01 15:43:33
Done.
| |
342 assembler.isolate()->factory()->name_string())), | |
343 &slow); | |
344 | |
345 Node* maybe_name_accessor = assembler.LoadFixedArrayElement( | |
346 descriptors, | |
347 assembler.Int32Constant(DescriptorArray::ToValueIndex(name_index))); | |
Igor Sheludko
2016/12/01 11:50:40
Same here.
danno
2016/12/01 15:43:33
Done.
| |
348 assembler.GotoIf(assembler.TaggedIsSmi(maybe_name_accessor), &slow); | |
349 Node* name_value_map = assembler.LoadMap(maybe_name_accessor); | |
350 assembler.GotoIf( | |
Igor Sheludko
2016/12/01 11:50:40
Same here.
danno
2016/12/01 15:43:33
Done.
| |
351 assembler.WordNotEqual( | |
352 name_value_map, | |
353 assembler.HeapConstant( | |
354 assembler.isolate()->factory()->accessor_info_map())), | |
355 &slow); | |
356 | |
357 // Choose the right bound function map based on whether the target is | |
358 // constructable. | |
359 assembler.Comment("Choose the right bound function map"); | |
360 Variable bound_function_map(&assembler, MachineRepresentation::kTagged); | |
361 Label with_constructor(&assembler); | |
362 CodeStubAssembler::VariableList vars({&bound_function_map}, assembler.zone()); | |
363 Label map_done(&assembler, vars); | |
364 Node* bit_field = assembler.LoadObjectField( | |
Igor Sheludko
2016/12/01 11:50:40
LoadMapBitField
danno
2016/12/01 15:43:32
Done.
| |
365 receiver_map, Map::kBitFieldOffset, MachineType::Pointer()); | |
366 mask = static_cast<int>(1 << Map::kIsConstructor); | |
367 mask_node = assembler.IntPtrConstant(mask); | |
368 Node* bits = assembler.WordAnd(bit_field, mask_node); | |
369 Node* native_context = assembler.LoadNativeContext(context); | |
370 assembler.GotoIf(assembler.Word32Equal(bits, mask_node), &with_constructor); | |
Igor Sheludko
2016/12/01 11:50:40
IsSetWord32(bit_field, mask)
danno
2016/12/01 15:43:33
Done.
| |
371 | |
372 bound_function_map.Bind(assembler.LoadContextElement( | |
373 native_context, Context::BOUND_FUNCTION_WITHOUT_CONSTRUCTOR_MAP_INDEX)); | |
374 assembler.Goto(&map_done); | |
375 | |
376 assembler.Bind(&with_constructor); | |
377 bound_function_map.Bind(assembler.LoadContextElement( | |
378 native_context, Context::BOUND_FUNCTION_WITH_CONSTRUCTOR_MAP_INDEX)); | |
379 assembler.Goto(&map_done); | |
380 | |
381 assembler.Bind(&map_done); | |
382 | |
383 // Verify that __proto__ matches that of a the target bound function. | |
384 assembler.Comment("Verify that __proto__ matches target bound function"); | |
385 Node* prototype = assembler.LoadMapPrototype(receiver_map); | |
386 Node* expected_prototype = | |
387 assembler.LoadMapPrototype(bound_function_map.value()); | |
388 assembler.GotoIf(assembler.WordNotEqual(prototype, expected_prototype), | |
389 &slow); | |
390 | |
391 // Allocate the arguments array. | |
392 assembler.Comment("Allocate the arguments array"); | |
393 Variable argument_array(&assembler, MachineRepresentation::kTagged); | |
394 Label empty_arguments(&assembler); | |
395 Label arguments_done(&assembler, &argument_array); | |
396 assembler.GotoIf( | |
397 assembler.IntPtrLessThanOrEqual(argc, assembler.IntPtrConstant(1)), | |
Igor Sheludko
2016/12/01 11:50:40
I'd use unsigned comparison.
danno
2016/12/01 15:43:33
Done.
| |
398 &empty_arguments); | |
399 Node* one = assembler.IntPtrConstant(1); | |
Igor Sheludko
2016/12/01 11:50:40
Please either use this in the whole function or in
danno
2016/12/01 15:43:33
Done.
| |
400 Node* elements_length = assembler.IntPtrSub(argc, one); | |
401 Node* elements = assembler.AllocateFixedArray( | |
402 FAST_ELEMENTS, elements_length, CodeStubAssembler::INTPTR_PARAMETERS); | |
403 Variable index(&assembler, MachineType::PointerRepresentation()); | |
404 index.Bind(assembler.IntPtrConstant(0)); | |
405 CodeStubAssembler::VariableList foreach_vars({&index}, assembler.zone()); | |
406 args.ForEach( | |
407 foreach_vars, | |
408 [elements, &index](CodeStubAssembler* assembler, compiler::Node* arg) { | |
409 assembler->StoreFixedArrayElement(elements, index.value(), arg, | |
410 SKIP_WRITE_BARRIER, 0, | |
Igor Sheludko
2016/12/01 11:50:40
I don't think we can skip WB here.
danno
2016/12/01 15:43:33
Done.
| |
411 CodeStubAssembler::INTPTR_PARAMETERS); | |
412 assembler->Increment(index); | |
413 }, | |
414 one); | |
415 argument_array.Bind(elements); | |
416 assembler.Goto(&arguments_done); | |
417 | |
418 assembler.Bind(&empty_arguments); | |
419 argument_array.Bind(assembler.LoadRoot(Heap::kEmptyFixedArrayRootIndex)); | |
Igor Sheludko
2016/12/01 11:50:40
EmptyFixedArrayConstant()
BTW, feel free to add m
danno
2016/12/01 15:43:33
Done.
| |
420 assembler.Goto(&arguments_done); | |
421 | |
422 assembler.Bind(&arguments_done); | |
423 | |
424 // Determine bound receiver. | |
425 assembler.Comment("Determine bound receiver"); | |
426 Variable bound_receiver(&assembler, MachineRepresentation::kTagged); | |
427 Label has_receiver(&assembler); | |
428 Label receiver_done(&assembler, &bound_receiver); | |
429 assembler.GotoIf(assembler.WordNotEqual(argc, assembler.IntPtrConstant(0)), | |
430 &has_receiver); | |
431 bound_receiver.Bind(assembler.LoadRoot(Heap::kUndefinedValueRootIndex)); | |
Igor Sheludko
2016/12/01 11:50:40
UndefinedConstant()
danno
2016/12/01 15:43:33
Done.
| |
432 assembler.Goto(&receiver_done); | |
433 | |
434 assembler.Bind(&has_receiver); | |
435 bound_receiver.Bind(args.AtIndex(0)); | |
436 assembler.Goto(&receiver_done); | |
437 | |
438 assembler.Bind(&receiver_done); | |
439 | |
440 // Allocate the resulting bound function. | |
441 assembler.Comment("Allocate the resulting bound function"); | |
442 Node* bound_function = assembler.Allocate(JSBoundFunction::kSize); | |
443 assembler.StoreMapNoWriteBarrier(bound_function, bound_function_map.value()); | |
Igor Sheludko
2016/12/01 11:50:40
We can't skip this WB. The incremental part of the
Igor Sheludko
2016/12/01 13:46:07
Please ignore this. I was wrong here...
danno
2016/12/01 15:43:32
Acknowledged.
danno
2016/12/01 15:43:33
Done.
| |
444 assembler.StoreObjectFieldNoWriteBarrier( | |
Igor Sheludko
2016/12/01 11:50:40
We can't skip WB here.
Igor Sheludko
2016/12/01 13:46:07
... and here ...
| |
445 bound_function, JSBoundFunction::kBoundTargetFunctionOffset, receiver); | |
446 assembler.StoreObjectFieldNoWriteBarrier(bound_function, | |
Igor Sheludko
2016/12/01 11:50:40
and here
Igor Sheludko
2016/12/01 13:46:07
... and here ...
danno
2016/12/01 15:43:32
Acknowledged.
| |
447 JSBoundFunction::kBoundThisOffset, | |
448 bound_receiver.value()); | |
449 assembler.StoreObjectFieldNoWriteBarrier( | |
Igor Sheludko
2016/12/01 11:50:40
This one we could probably skip if arguments_array
Igor Sheludko
2016/12/01 13:46:07
... and here.
danno
2016/12/01 15:43:33
Acknowledged.
| |
450 bound_function, JSBoundFunction::kBoundArgumentsOffset, | |
451 argument_array.value()); | |
452 Node* empty_fixed_array = assembler.LoadRoot(Heap::kEmptyFixedArrayRootIndex); | |
Igor Sheludko
2016/12/01 11:50:40
EmptyFixedArrayConstant();
danno
2016/12/01 15:43:33
Done.
| |
453 assembler.StoreObjectFieldNoWriteBarrier( | |
454 bound_function, JSObject::kPropertiesOffset, empty_fixed_array); | |
455 assembler.StoreObjectFieldNoWriteBarrier( | |
456 bound_function, JSObject::kElementsOffset, empty_fixed_array); | |
457 | |
458 args.PopAndReturn(bound_function); | |
459 assembler.Bind(&slow); | |
460 | |
461 Node* target = assembler.LoadFromFrame( | |
462 StandardFrameConstants::kFunctionOffset, MachineType::TaggedPointer()); | |
463 assembler.TailCallStub( | |
464 CodeFactory::FunctionPrototypeBind(assembler.isolate()), context, target, | |
465 new_target, argc); | |
Igor Sheludko
2016/12/01 11:50:39
Note: this |argc| must be Int32 again.
danno
2016/12/01 15:43:33
Done.
| |
466 } | |
467 | |
258 // TODO(verwaest): This is a temporary helper until the FastFunctionBind stub | 468 // TODO(verwaest): This is a temporary helper until the FastFunctionBind stub |
259 // can tailcall to the builtin directly. | 469 // can tailcall to the builtin directly. |
260 RUNTIME_FUNCTION(Runtime_FunctionBind) { | 470 RUNTIME_FUNCTION(Runtime_FunctionBind) { |
261 DCHECK_EQ(2, args.length()); | 471 DCHECK_EQ(2, args.length()); |
262 Arguments* incoming = reinterpret_cast<Arguments*>(args[0]); | 472 Arguments* incoming = reinterpret_cast<Arguments*>(args[0]); |
263 // Rewrap the arguments as builtins arguments. | 473 // Rewrap the arguments as builtins arguments. |
264 int argc = incoming->length() + BuiltinArguments::kNumExtraArgsWithReceiver; | 474 int argc = incoming->length() + BuiltinArguments::kNumExtraArgsWithReceiver; |
265 BuiltinArguments caller_args(argc, incoming->arguments() + 1); | 475 BuiltinArguments caller_args(argc, incoming->arguments() + 1); |
266 return DoFunctionBind(isolate, caller_args); | 476 return DoFunctionBind(isolate, caller_args); |
267 } | 477 } |
(...skipping 21 matching lines...) Expand all Loading... | |
289 | 499 |
290 Node* f = assembler.Parameter(0); | 500 Node* f = assembler.Parameter(0); |
291 Node* v = assembler.Parameter(1); | 501 Node* v = assembler.Parameter(1); |
292 Node* context = assembler.Parameter(4); | 502 Node* context = assembler.Parameter(4); |
293 Node* result = assembler.OrdinaryHasInstance(context, f, v); | 503 Node* result = assembler.OrdinaryHasInstance(context, f, v); |
294 assembler.Return(result); | 504 assembler.Return(result); |
295 } | 505 } |
296 | 506 |
297 } // namespace internal | 507 } // namespace internal |
298 } // namespace v8 | 508 } // namespace v8 |
OLD | NEW |