OLD | NEW |
(Empty) | |
| 1 // Copyright 2017 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/builtins/builtins-utils-gen.h" |
| 6 #include "src/builtins/builtins.h" |
| 7 #include "src/code-stub-assembler.h" |
| 8 |
| 9 namespace v8 { |
| 10 namespace internal { |
| 11 |
| 12 TF_BUILTIN(FastFunctionPrototypeBind, CodeStubAssembler) { |
| 13 Label slow(this); |
| 14 |
| 15 Node* argc = Parameter(BuiltinDescriptor::kArgumentsCount); |
| 16 Node* context = Parameter(BuiltinDescriptor::kContext); |
| 17 Node* new_target = Parameter(BuiltinDescriptor::kNewTarget); |
| 18 |
| 19 CodeStubArguments args(this, ChangeInt32ToIntPtr(argc)); |
| 20 |
| 21 // Check that receiver has instance type of JS_FUNCTION_TYPE |
| 22 Node* receiver = args.GetReceiver(); |
| 23 GotoIf(TaggedIsSmi(receiver), &slow); |
| 24 |
| 25 Node* receiver_map = LoadMap(receiver); |
| 26 Node* instance_type = LoadMapInstanceType(receiver_map); |
| 27 GotoIf(Word32NotEqual(instance_type, Int32Constant(JS_FUNCTION_TYPE)), &slow); |
| 28 |
| 29 // Disallow binding of slow-mode functions. We need to figure out whether the |
| 30 // length and name property are in the original state. |
| 31 Comment("Disallow binding of slow-mode functions"); |
| 32 GotoIf(IsDictionaryMap(receiver_map), &slow); |
| 33 |
| 34 // Check whether the length and name properties are still present as |
| 35 // AccessorInfo objects. In that case, their value can be recomputed even if |
| 36 // the actual value on the object changes. |
| 37 Comment("Check descriptor array length"); |
| 38 Node* descriptors = LoadMapDescriptors(receiver_map); |
| 39 Node* descriptors_length = LoadFixedArrayBaseLength(descriptors); |
| 40 GotoIf(SmiLessThanOrEqual(descriptors_length, SmiConstant(1)), &slow); |
| 41 |
| 42 // Check whether the length and name properties are still present as |
| 43 // AccessorInfo objects. In that case, their value can be recomputed even if |
| 44 // the actual value on the object changes. |
| 45 Comment("Check name and length properties"); |
| 46 const int length_index = JSFunction::kLengthDescriptorIndex; |
| 47 Node* maybe_length = LoadFixedArrayElement( |
| 48 descriptors, DescriptorArray::ToKeyIndex(length_index)); |
| 49 GotoIf(WordNotEqual(maybe_length, LoadRoot(Heap::klength_stringRootIndex)), |
| 50 &slow); |
| 51 |
| 52 Node* maybe_length_accessor = LoadFixedArrayElement( |
| 53 descriptors, DescriptorArray::ToValueIndex(length_index)); |
| 54 GotoIf(TaggedIsSmi(maybe_length_accessor), &slow); |
| 55 Node* length_value_map = LoadMap(maybe_length_accessor); |
| 56 GotoIfNot(IsAccessorInfoMap(length_value_map), &slow); |
| 57 |
| 58 const int name_index = JSFunction::kNameDescriptorIndex; |
| 59 Node* maybe_name = LoadFixedArrayElement( |
| 60 descriptors, DescriptorArray::ToKeyIndex(name_index)); |
| 61 GotoIf(WordNotEqual(maybe_name, LoadRoot(Heap::kname_stringRootIndex)), |
| 62 &slow); |
| 63 |
| 64 Node* maybe_name_accessor = LoadFixedArrayElement( |
| 65 descriptors, DescriptorArray::ToValueIndex(name_index)); |
| 66 GotoIf(TaggedIsSmi(maybe_name_accessor), &slow); |
| 67 Node* name_value_map = LoadMap(maybe_name_accessor); |
| 68 GotoIfNot(IsAccessorInfoMap(name_value_map), &slow); |
| 69 |
| 70 // Choose the right bound function map based on whether the target is |
| 71 // constructable. |
| 72 Comment("Choose the right bound function map"); |
| 73 Variable bound_function_map(this, MachineRepresentation::kTagged); |
| 74 Label with_constructor(this); |
| 75 VariableList vars({&bound_function_map}, zone()); |
| 76 Node* native_context = LoadNativeContext(context); |
| 77 |
| 78 Label map_done(this, vars); |
| 79 Node* bit_field = LoadMapBitField(receiver_map); |
| 80 int mask = static_cast<int>(1 << Map::kIsConstructor); |
| 81 GotoIf(IsSetWord32(bit_field, mask), &with_constructor); |
| 82 |
| 83 bound_function_map.Bind(LoadContextElement( |
| 84 native_context, Context::BOUND_FUNCTION_WITHOUT_CONSTRUCTOR_MAP_INDEX)); |
| 85 Goto(&map_done); |
| 86 |
| 87 Bind(&with_constructor); |
| 88 bound_function_map.Bind(LoadContextElement( |
| 89 native_context, Context::BOUND_FUNCTION_WITH_CONSTRUCTOR_MAP_INDEX)); |
| 90 Goto(&map_done); |
| 91 |
| 92 Bind(&map_done); |
| 93 |
| 94 // Verify that __proto__ matches that of a the target bound function. |
| 95 Comment("Verify that __proto__ matches target bound function"); |
| 96 Node* prototype = LoadMapPrototype(receiver_map); |
| 97 Node* expected_prototype = LoadMapPrototype(bound_function_map.value()); |
| 98 GotoIf(WordNotEqual(prototype, expected_prototype), &slow); |
| 99 |
| 100 // Allocate the arguments array. |
| 101 Comment("Allocate the arguments array"); |
| 102 Variable argument_array(this, MachineRepresentation::kTagged); |
| 103 Label empty_arguments(this); |
| 104 Label arguments_done(this, &argument_array); |
| 105 GotoIf(Uint32LessThanOrEqual(argc, Int32Constant(1)), &empty_arguments); |
| 106 Node* elements_length = ChangeUint32ToWord(Int32Sub(argc, Int32Constant(1))); |
| 107 Node* elements = AllocateFixedArray(FAST_ELEMENTS, elements_length); |
| 108 Variable index(this, MachineType::PointerRepresentation()); |
| 109 index.Bind(IntPtrConstant(0)); |
| 110 VariableList foreach_vars({&index}, zone()); |
| 111 args.ForEach(foreach_vars, |
| 112 [this, elements, &index](Node* arg) { |
| 113 StoreFixedArrayElement(elements, index.value(), arg); |
| 114 Increment(index); |
| 115 }, |
| 116 IntPtrConstant(1)); |
| 117 argument_array.Bind(elements); |
| 118 Goto(&arguments_done); |
| 119 |
| 120 Bind(&empty_arguments); |
| 121 argument_array.Bind(EmptyFixedArrayConstant()); |
| 122 Goto(&arguments_done); |
| 123 |
| 124 Bind(&arguments_done); |
| 125 |
| 126 // Determine bound receiver. |
| 127 Comment("Determine bound receiver"); |
| 128 Variable bound_receiver(this, MachineRepresentation::kTagged); |
| 129 Label has_receiver(this); |
| 130 Label receiver_done(this, &bound_receiver); |
| 131 GotoIf(Word32NotEqual(argc, Int32Constant(0)), &has_receiver); |
| 132 bound_receiver.Bind(UndefinedConstant()); |
| 133 Goto(&receiver_done); |
| 134 |
| 135 Bind(&has_receiver); |
| 136 bound_receiver.Bind(args.AtIndex(0)); |
| 137 Goto(&receiver_done); |
| 138 |
| 139 Bind(&receiver_done); |
| 140 |
| 141 // Allocate the resulting bound function. |
| 142 Comment("Allocate the resulting bound function"); |
| 143 Node* bound_function = Allocate(JSBoundFunction::kSize); |
| 144 StoreMapNoWriteBarrier(bound_function, bound_function_map.value()); |
| 145 StoreObjectFieldNoWriteBarrier( |
| 146 bound_function, JSBoundFunction::kBoundTargetFunctionOffset, receiver); |
| 147 StoreObjectFieldNoWriteBarrier(bound_function, |
| 148 JSBoundFunction::kBoundThisOffset, |
| 149 bound_receiver.value()); |
| 150 StoreObjectFieldNoWriteBarrier(bound_function, |
| 151 JSBoundFunction::kBoundArgumentsOffset, |
| 152 argument_array.value()); |
| 153 Node* empty_fixed_array = EmptyFixedArrayConstant(); |
| 154 StoreObjectFieldNoWriteBarrier(bound_function, JSObject::kPropertiesOffset, |
| 155 empty_fixed_array); |
| 156 StoreObjectFieldNoWriteBarrier(bound_function, JSObject::kElementsOffset, |
| 157 empty_fixed_array); |
| 158 |
| 159 args.PopAndReturn(bound_function); |
| 160 Bind(&slow); |
| 161 |
| 162 Node* target = LoadFromFrame(StandardFrameConstants::kFunctionOffset, |
| 163 MachineType::TaggedPointer()); |
| 164 TailCallStub(CodeFactory::FunctionPrototypeBind(isolate()), context, target, |
| 165 new_target, argc); |
| 166 } |
| 167 |
| 168 // ES6 section 19.2.3.6 Function.prototype [ @@hasInstance ] ( V ) |
| 169 TF_BUILTIN(FunctionPrototypeHasInstance, CodeStubAssembler) { |
| 170 Node* f = Parameter(0); |
| 171 Node* v = Parameter(1); |
| 172 Node* context = Parameter(4); |
| 173 Node* result = OrdinaryHasInstance(context, f, v); |
| 174 Return(result); |
| 175 } |
| 176 |
| 177 } // namespace internal |
| 178 } // namespace v8 |
OLD | NEW |