| Index: src/builtins/builtins-array.cc
|
| diff --git a/src/builtins/builtins-array.cc b/src/builtins/builtins-array.cc
|
| index 9bfd68ff2f67ba8aa063d8bb46fe6a62a18f71b7..198c263723f8cdb7ded549171edf59da9013e64a 100644
|
| --- a/src/builtins/builtins-array.cc
|
| +++ b/src/builtins/builtins-array.cc
|
| @@ -150,8 +150,9 @@ MUST_USE_RESULT static Object* CallJsIntrinsic(Isolate* isolate,
|
| isolate,
|
| Execution::Call(isolate, function, args.receiver(), argc, argv.start()));
|
| }
|
| +} // namespace
|
|
|
| -Object* DoArrayPush(Isolate* isolate, BuiltinArguments args) {
|
| +BUILTIN(ArrayPush) {
|
| HandleScope scope(isolate);
|
| Handle<Object> receiver = args.receiver();
|
| if (!EnsureJSArrayWithWritableFastElements(isolate, receiver, &args, 1)) {
|
| @@ -174,19 +175,163 @@ Object* DoArrayPush(Isolate* isolate, BuiltinArguments args) {
|
| int new_length = accessor->Push(array, &args, to_add);
|
| return Smi::FromInt(new_length);
|
| }
|
| -} // namespace
|
|
|
| -BUILTIN(ArrayPush) { return DoArrayPush(isolate, args); }
|
| -
|
| -// TODO(verwaest): This is a temporary helper until the FastArrayPush stub can
|
| -// tailcall to the builtin directly.
|
| -RUNTIME_FUNCTION(Runtime_ArrayPush) {
|
| - DCHECK_EQ(2, args.length());
|
| - Arguments* incoming = reinterpret_cast<Arguments*>(args[0]);
|
| - // Rewrap the arguments as builtins arguments.
|
| - int argc = incoming->length() + BuiltinArguments::kNumExtraArgsWithReceiver;
|
| - BuiltinArguments caller_args(argc, incoming->arguments() + 1);
|
| - return DoArrayPush(isolate, caller_args);
|
| +void Builtins::Generate_FastArrayPush(compiler::CodeAssemblerState* state) {
|
| + typedef compiler::Node Node;
|
| + typedef CodeStubAssembler::Label Label;
|
| + typedef CodeStubAssembler::Variable Variable;
|
| + CodeStubAssembler assembler(state);
|
| + Variable arg_index(&assembler, MachineType::PointerRepresentation());
|
| + Label default_label(&assembler, &arg_index);
|
| + Label smi_transition(&assembler);
|
| + Label object_push_pre(&assembler);
|
| + Label object_push(&assembler, &arg_index);
|
| + Label double_push(&assembler, &arg_index);
|
| + Label double_transition(&assembler);
|
| + Label runtime(&assembler, Label::kDeferred);
|
| +
|
| + Node* argc = assembler.Parameter(1);
|
| + Node* context = assembler.Parameter(2);
|
| + Node* new_target = assembler.Parameter(0);
|
| +
|
| + CodeStubArguments args(&assembler, argc);
|
| + Node* receiver = args.GetReceiver();
|
| + Node* kind = nullptr;
|
| +
|
| + Label fast(&assembler);
|
| + {
|
| + assembler.BranchIfFastJSArray(
|
| + receiver, context, CodeStubAssembler::FastJSArrayAccessMode::ANY_ACCESS,
|
| + &fast, &runtime);
|
| + }
|
| +
|
| + assembler.Bind(&fast);
|
| + {
|
| + // Disallow pushing onto prototypes. It might be the JSArray prototype.
|
| + // Disallow pushing onto non-extensible objects.
|
| + assembler.Comment("Disallow pushing onto prototypes");
|
| + Node* map = assembler.LoadMap(receiver);
|
| + Node* bit_field2 = assembler.LoadMapBitField2(map);
|
| + int mask = static_cast<int>(Map::IsPrototypeMapBits::kMask) |
|
| + (1 << Map::kIsExtensible);
|
| + Node* test = assembler.Word32And(bit_field2, assembler.Int32Constant(mask));
|
| + assembler.GotoIf(
|
| + assembler.Word32NotEqual(
|
| + test, assembler.Int32Constant(1 << Map::kIsExtensible)),
|
| + &runtime);
|
| +
|
| + // Disallow pushing onto arrays in dictionary named property mode. We need
|
| + // to figure out whether the length property is still writable.
|
| + assembler.Comment(
|
| + "Disallow pushing onto arrays in dictionary named property mode");
|
| + Node* bit_field3 = assembler.LoadMapBitField3(map);
|
| + assembler.GotoIf(assembler.IsSetWord32<Map::DictionaryMap>(bit_field3),
|
| + &runtime);
|
| +
|
| + // Check whether the length property is writable. The length property is the
|
| + // only default named property on arrays. It's nonconfigurable, hence is
|
| + // guaranteed to stay the first property.
|
| + Node* descriptors = assembler.LoadMapDescriptors(map);
|
| + Node* details = assembler.LoadFixedArrayElement(
|
| + descriptors,
|
| + assembler.Int32Constant(DescriptorArray::ToDetailsIndex(0)));
|
| + mask = READ_ONLY << PropertyDetails::AttributesField::kShift;
|
| + Node* mask_node = assembler.SmiConstant(mask);
|
| + test = assembler.WordAnd(details, mask_node);
|
| + assembler.GotoIf(assembler.WordEqual(test, mask_node), &runtime);
|
| +
|
| + arg_index.Bind(assembler.IntPtrConstant(0));
|
| + kind = assembler.DecodeWord32<Map::ElementsKindBits>(bit_field2);
|
| +
|
| + assembler.GotoIf(
|
| + assembler.IntPtrGreaterThan(
|
| + kind, assembler.IntPtrConstant(FAST_HOLEY_SMI_ELEMENTS)),
|
| + &object_push_pre);
|
| +
|
| + Node* new_length = assembler.BuildAppendJSArray(
|
| + FAST_SMI_ELEMENTS, context, receiver, args, arg_index, &smi_transition);
|
| + args.PopAndReturn(new_length);
|
| + }
|
| +
|
| + // If the argument is not a smi, then use a heavyweight SetProperty to
|
| + // transition the array for only the single next element. If the argument is
|
| + // a smi, the failure is due to some other reason and we should fall back on
|
| + // the most generic implementation for the rest of the array.
|
| + assembler.Bind(&smi_transition);
|
| + {
|
| + Node* arg = args.AtIndex(arg_index.value());
|
| + assembler.GotoIf(assembler.TaggedIsSmi(arg), &default_label);
|
| + Node* length = assembler.LoadJSArrayLength(receiver);
|
| + // TODO(danno): Use the KeyedStoreGeneric stub here when possible,
|
| + // calling into the runtime to do the elements transition is overkill.
|
| + assembler.CallRuntime(Runtime::kSetProperty, context, receiver, length, arg,
|
| + assembler.SmiConstant(STRICT));
|
| + assembler.Increment(arg_index);
|
| + assembler.GotoIfNotNumber(arg, &object_push);
|
| + assembler.Goto(&double_push);
|
| + }
|
| +
|
| + assembler.Bind(&object_push_pre);
|
| + {
|
| + assembler.Branch(assembler.IntPtrGreaterThan(
|
| + kind, assembler.IntPtrConstant(FAST_HOLEY_ELEMENTS)),
|
| + &double_push, &object_push);
|
| + }
|
| +
|
| + assembler.Bind(&object_push);
|
| + {
|
| + Node* new_length = assembler.BuildAppendJSArray(
|
| + FAST_ELEMENTS, context, receiver, args, arg_index, &default_label);
|
| + args.PopAndReturn(new_length);
|
| + }
|
| +
|
| + assembler.Bind(&double_push);
|
| + {
|
| + Node* new_length =
|
| + assembler.BuildAppendJSArray(FAST_DOUBLE_ELEMENTS, context, receiver,
|
| + args, arg_index, &double_transition);
|
| + args.PopAndReturn(new_length);
|
| + }
|
| +
|
| + // If the argument is not a double, then use a heavyweight SetProperty to
|
| + // transition the array for only the single next element. If the argument is
|
| + // a double, the failure is due to some other reason and we should fall back
|
| + // on the most generic implementation for the rest of the array.
|
| + assembler.Bind(&double_transition);
|
| + {
|
| + Node* arg = args.AtIndex(arg_index.value());
|
| + assembler.GotoIfNumber(arg, &default_label);
|
| + Node* length = assembler.LoadJSArrayLength(receiver);
|
| + // TODO(danno): Use the KeyedStoreGeneric stub here when possible,
|
| + // calling into the runtime to do the elements transition is overkill.
|
| + assembler.CallRuntime(Runtime::kSetProperty, context, receiver, length, arg,
|
| + assembler.SmiConstant(STRICT));
|
| + assembler.Increment(arg_index);
|
| + assembler.Goto(&object_push);
|
| + }
|
| +
|
| + // Fallback that stores un-processed arguments using the full, heavyweight
|
| + // SetProperty machinery.
|
| + assembler.Bind(&default_label);
|
| + {
|
| + args.ForEach(
|
| + [receiver, context, &arg_index](CodeStubAssembler* assembler,
|
| + Node* arg) {
|
| + Node* length = assembler->LoadJSArrayLength(receiver);
|
| + assembler->CallRuntime(Runtime::kSetProperty, context, receiver,
|
| + length, arg, assembler->SmiConstant(STRICT));
|
| + },
|
| + arg_index.value());
|
| + args.PopAndReturn(assembler.LoadJSArrayLength(receiver));
|
| + }
|
| +
|
| + assembler.Bind(&runtime);
|
| + {
|
| + Node* target = assembler.LoadFromFrame(
|
| + StandardFrameConstants::kFunctionOffset, MachineType::TaggedPointer());
|
| + assembler.TailCallStub(CodeFactory::ArrayPush(assembler.isolate()), context,
|
| + target, new_target, argc);
|
| + }
|
| }
|
|
|
| BUILTIN(ArrayPop) {
|
| @@ -1294,7 +1439,9 @@ void Builtins::Generate_ArrayIncludes(compiler::CodeAssemblerState* state) {
|
|
|
| // Take slow path if not a JSArray, if retrieving elements requires
|
| // traversing prototype, or if access checks are required.
|
| - assembler.BranchIfFastJSArray(array, context, &init_len, &call_runtime);
|
| + assembler.BranchIfFastJSArray(
|
| + array, context, CodeStubAssembler::FastJSArrayAccessMode::INBOUNDS_READ,
|
| + &init_len, &call_runtime);
|
|
|
| assembler.Bind(&init_len);
|
| {
|
| @@ -1735,7 +1882,9 @@ void Builtins::Generate_ArrayIndexOf(compiler::CodeAssemblerState* state) {
|
|
|
| // Take slow path if not a JSArray, if retrieving elements requires
|
| // traversing prototype, or if access checks are required.
|
| - assembler.BranchIfFastJSArray(array, context, &init_len, &call_runtime);
|
| + assembler.BranchIfFastJSArray(
|
| + array, context, CodeStubAssembler::FastJSArrayAccessMode::INBOUNDS_READ,
|
| + &init_len, &call_runtime);
|
|
|
| assembler.Bind(&init_len);
|
| {
|
|
|