1ad0cd560e
This patch gives DescriptorArray its own visitor id and its own layout that is independent from the layout of WeakFixedArray. This allows us to use raw 16-bit integers for keeping track of the number of descriptors (total, non-slack, and marked). As a side-effect, we save one word per descriptor array on 64-bit. v8:8486 Change-Id: If8389dde446319e5b3491abc948b52539dba235c Reviewed-on: https://chromium-review.googlesource.com/c/1349245 Commit-Queue: Ulan Degenbaev <ulan@chromium.org> Reviewed-by: Igor Sheludko <ishell@chromium.org> Cr-Commit-Position: refs/heads/master@{#57845}
206 lines
7.9 KiB
C++
206 lines
7.9 KiB
C++
// Copyright 2017 the V8 project authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "src/builtins/builtins-utils-gen.h"
|
|
#include "src/builtins/builtins.h"
|
|
#include "src/code-stub-assembler.h"
|
|
#include "src/frame-constants.h"
|
|
#include "src/objects/api-callbacks.h"
|
|
#include "src/objects/descriptor-array.h"
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
|
|
TF_BUILTIN(FastFunctionPrototypeBind, CodeStubAssembler) {
|
|
Label slow(this);
|
|
|
|
// TODO(ishell): use constants from Descriptor once the JSFunction linkage
|
|
// arguments are reordered.
|
|
Node* argc = Parameter(Descriptor::kJSActualArgumentsCount);
|
|
Node* context = Parameter(Descriptor::kContext);
|
|
Node* new_target = Parameter(Descriptor::kJSNewTarget);
|
|
|
|
CodeStubArguments args(this, ChangeInt32ToIntPtr(argc));
|
|
|
|
// Check that receiver has instance type of JS_FUNCTION_TYPE
|
|
Node* receiver = args.GetReceiver();
|
|
GotoIf(TaggedIsSmi(receiver), &slow);
|
|
|
|
Node* receiver_map = LoadMap(receiver);
|
|
{
|
|
Node* instance_type = LoadMapInstanceType(receiver_map);
|
|
GotoIfNot(
|
|
Word32Or(InstanceTypeEqual(instance_type, JS_FUNCTION_TYPE),
|
|
InstanceTypeEqual(instance_type, JS_BOUND_FUNCTION_TYPE)),
|
|
&slow);
|
|
}
|
|
|
|
// Disallow binding of slow-mode functions. We need to figure out whether the
|
|
// length and name property are in the original state.
|
|
Comment("Disallow binding of slow-mode functions");
|
|
GotoIf(IsDictionaryMap(receiver_map), &slow);
|
|
|
|
// Check whether the length and name properties are still present as
|
|
// AccessorInfo objects. In that case, their value can be recomputed even if
|
|
// the actual value on the object changes.
|
|
Comment("Check descriptor array length");
|
|
TNode<DescriptorArray> descriptors = LoadMapDescriptors(receiver_map);
|
|
// Minimum descriptor array length required for fast path.
|
|
const int min_nof_descriptors = i::Max(JSFunction::kLengthDescriptorIndex,
|
|
JSFunction::kNameDescriptorIndex);
|
|
TNode<Int32T> nof_descriptors = LoadNumberOfDescriptors(descriptors);
|
|
GotoIf(
|
|
Int32LessThanOrEqual(nof_descriptors, Int32Constant(min_nof_descriptors)),
|
|
&slow);
|
|
|
|
// Check whether the length and name properties are still present as
|
|
// AccessorInfo objects. In that case, their value can be recomputed even if
|
|
// the actual value on the object changes.
|
|
Comment("Check name and length properties");
|
|
{
|
|
const int length_index = JSFunction::kLengthDescriptorIndex;
|
|
TNode<Name> maybe_length =
|
|
LoadKeyByDescriptorEntry(descriptors, length_index);
|
|
GotoIf(WordNotEqual(maybe_length, LoadRoot(RootIndex::klength_string)),
|
|
&slow);
|
|
|
|
TNode<Object> maybe_length_accessor =
|
|
LoadValueByDescriptorEntry(descriptors, length_index);
|
|
GotoIf(TaggedIsSmi(maybe_length_accessor), &slow);
|
|
Node* length_value_map = LoadMap(CAST(maybe_length_accessor));
|
|
GotoIfNot(IsAccessorInfoMap(length_value_map), &slow);
|
|
|
|
const int name_index = JSFunction::kNameDescriptorIndex;
|
|
TNode<Name> maybe_name = LoadKeyByDescriptorEntry(descriptors, name_index);
|
|
GotoIf(WordNotEqual(maybe_name, LoadRoot(RootIndex::kname_string)), &slow);
|
|
|
|
TNode<Object> maybe_name_accessor =
|
|
LoadValueByDescriptorEntry(descriptors, name_index);
|
|
GotoIf(TaggedIsSmi(maybe_name_accessor), &slow);
|
|
TNode<Map> name_value_map = LoadMap(CAST(maybe_name_accessor));
|
|
GotoIfNot(IsAccessorInfoMap(name_value_map), &slow);
|
|
}
|
|
|
|
// Choose the right bound function map based on whether the target is
|
|
// constructable.
|
|
Comment("Choose the right bound function map");
|
|
VARIABLE(bound_function_map, MachineRepresentation::kTagged);
|
|
{
|
|
Label with_constructor(this);
|
|
VariableList vars({&bound_function_map}, zone());
|
|
Node* native_context = LoadNativeContext(context);
|
|
|
|
Label map_done(this, vars);
|
|
GotoIf(IsConstructorMap(receiver_map), &with_constructor);
|
|
|
|
bound_function_map.Bind(LoadContextElement(
|
|
native_context, Context::BOUND_FUNCTION_WITHOUT_CONSTRUCTOR_MAP_INDEX));
|
|
Goto(&map_done);
|
|
|
|
BIND(&with_constructor);
|
|
bound_function_map.Bind(LoadContextElement(
|
|
native_context, Context::BOUND_FUNCTION_WITH_CONSTRUCTOR_MAP_INDEX));
|
|
Goto(&map_done);
|
|
|
|
BIND(&map_done);
|
|
}
|
|
|
|
// Verify that __proto__ matches that of a the target bound function.
|
|
Comment("Verify that __proto__ matches target bound function");
|
|
Node* prototype = LoadMapPrototype(receiver_map);
|
|
Node* expected_prototype = LoadMapPrototype(bound_function_map.value());
|
|
GotoIf(WordNotEqual(prototype, expected_prototype), &slow);
|
|
|
|
// Allocate the arguments array.
|
|
Comment("Allocate the arguments array");
|
|
VARIABLE(argument_array, MachineRepresentation::kTagged);
|
|
{
|
|
Label empty_arguments(this);
|
|
Label arguments_done(this, &argument_array);
|
|
GotoIf(Uint32LessThanOrEqual(argc, Int32Constant(1)), &empty_arguments);
|
|
TNode<IntPtrT> elements_length =
|
|
Signed(ChangeUint32ToWord(Unsigned(Int32Sub(argc, Int32Constant(1)))));
|
|
TNode<FixedArray> elements = CAST(AllocateFixedArray(
|
|
PACKED_ELEMENTS, elements_length, kAllowLargeObjectAllocation));
|
|
VARIABLE(index, MachineType::PointerRepresentation());
|
|
index.Bind(IntPtrConstant(0));
|
|
VariableList foreach_vars({&index}, zone());
|
|
args.ForEach(foreach_vars,
|
|
[this, elements, &index](Node* arg) {
|
|
StoreFixedArrayElement(elements, index.value(), arg);
|
|
Increment(&index);
|
|
},
|
|
IntPtrConstant(1));
|
|
argument_array.Bind(elements);
|
|
Goto(&arguments_done);
|
|
|
|
BIND(&empty_arguments);
|
|
argument_array.Bind(EmptyFixedArrayConstant());
|
|
Goto(&arguments_done);
|
|
|
|
BIND(&arguments_done);
|
|
}
|
|
|
|
// Determine bound receiver.
|
|
Comment("Determine bound receiver");
|
|
VARIABLE(bound_receiver, MachineRepresentation::kTagged);
|
|
{
|
|
Label has_receiver(this);
|
|
Label receiver_done(this, &bound_receiver);
|
|
GotoIf(Word32NotEqual(argc, Int32Constant(0)), &has_receiver);
|
|
bound_receiver.Bind(UndefinedConstant());
|
|
Goto(&receiver_done);
|
|
|
|
BIND(&has_receiver);
|
|
bound_receiver.Bind(args.AtIndex(0));
|
|
Goto(&receiver_done);
|
|
|
|
BIND(&receiver_done);
|
|
}
|
|
|
|
// Allocate the resulting bound function.
|
|
Comment("Allocate the resulting bound function");
|
|
{
|
|
Node* bound_function = Allocate(JSBoundFunction::kSize);
|
|
StoreMapNoWriteBarrier(bound_function, bound_function_map.value());
|
|
StoreObjectFieldNoWriteBarrier(
|
|
bound_function, JSBoundFunction::kBoundTargetFunctionOffset, receiver);
|
|
StoreObjectFieldNoWriteBarrier(bound_function,
|
|
JSBoundFunction::kBoundThisOffset,
|
|
bound_receiver.value());
|
|
StoreObjectFieldNoWriteBarrier(bound_function,
|
|
JSBoundFunction::kBoundArgumentsOffset,
|
|
argument_array.value());
|
|
Node* empty_fixed_array = EmptyFixedArrayConstant();
|
|
StoreObjectFieldNoWriteBarrier(
|
|
bound_function, JSObject::kPropertiesOrHashOffset, empty_fixed_array);
|
|
StoreObjectFieldNoWriteBarrier(bound_function, JSObject::kElementsOffset,
|
|
empty_fixed_array);
|
|
|
|
args.PopAndReturn(bound_function);
|
|
}
|
|
|
|
BIND(&slow);
|
|
{
|
|
// We are not using Parameter(Descriptor::kJSTarget) and loading the value
|
|
// from the current frame here in order to reduce register pressure on the
|
|
// fast path.
|
|
TNode<JSFunction> target = LoadTargetFromFrame();
|
|
TailCallBuiltin(Builtins::kFunctionPrototypeBind, context, target,
|
|
new_target, argc);
|
|
}
|
|
}
|
|
|
|
// ES6 #sec-function.prototype-@@hasinstance
|
|
TF_BUILTIN(FunctionPrototypeHasInstance, CodeStubAssembler) {
|
|
Node* context = Parameter(Descriptor::kContext);
|
|
Node* f = Parameter(Descriptor::kReceiver);
|
|
Node* v = Parameter(Descriptor::kV);
|
|
Node* result = OrdinaryHasInstance(context, f, v);
|
|
Return(result);
|
|
}
|
|
|
|
} // namespace internal
|
|
} // namespace v8
|