[vm/jit] Introduce dynamic invocation forwarders that perform type checking
This forwarders are used at dynamic call-sites and perform type checking for all non-generic-covariant arguments. This allows to skip the same type checks in the actual method body. This yield on average 10% improvement in performance across the body of benchmarks including dart2js compilation times. Bug: https://github.com/dart-lang/sdk/issues/33257 Change-Id: If3fc94a2e0a6f496ec0633f0b379d053a54a40ca Reviewed-on: https://dart-review.googlesource.com/61244 Commit-Queue: Vyacheslav Egorov <vegorov@google.com> Reviewed-by: Alexander Markov <alexmarkov@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com>
This commit is contained in:
committed by
commit-bot@chromium.org
parent
f9a11ec380
commit
20ca5bbb05
@@ -433,6 +433,8 @@ class FunctionViewElement extends HtmlElement implements Renderable {
|
||||
return 'tag';
|
||||
case M.FunctionKind.signatureFunction:
|
||||
return 'signature function';
|
||||
case M.FunctionKind.dynamicInvocationForwarder:
|
||||
return 'dynamic invocation forwarder';
|
||||
}
|
||||
throw new Exception('Unknown Functionkind ($kind)');
|
||||
}
|
||||
|
||||
@@ -23,7 +23,8 @@ enum FunctionKind {
|
||||
native,
|
||||
stub,
|
||||
tag,
|
||||
signatureFunction
|
||||
signatureFunction,
|
||||
dynamicInvocationForwarder
|
||||
}
|
||||
|
||||
bool isSyntheticFunction(FunctionKind kind) {
|
||||
|
||||
@@ -3112,6 +3112,8 @@ M.FunctionKind stringToFunctionKind(String value) {
|
||||
return M.FunctionKind.tag;
|
||||
case 'SignatureFunction':
|
||||
return M.FunctionKind.signatureFunction;
|
||||
case 'DynamicInvocationForwarder':
|
||||
return M.FunctionKind.dynamicInvocationForwarder;
|
||||
}
|
||||
Logger.root.severe('Unrecognized function kind: $value');
|
||||
throw new FallThroughError();
|
||||
|
||||
@@ -38,6 +38,31 @@ static bool IsFieldInitializer(const Function& function, Zone* zone) {
|
||||
.StartsWith(Symbols::InitPrefix());
|
||||
}
|
||||
|
||||
// Returns true if the given method can skip type checks for all arguments
|
||||
// that are not covariant or generic covariant in its implementation.
|
||||
static bool MethodCanSkipTypeChecksForNonCovariantArguments(
|
||||
const Function& method,
|
||||
const ProcedureAttributesMetadata& attrs) {
|
||||
// Dart 2 type system at non-dynamic call sites statically guarantees that
|
||||
// argument values match declarated parameter types for all non-covariant
|
||||
// and non-generic-covariant parameters. The same applies to type parameters
|
||||
// bounds for type parameters of generic functions.
|
||||
// In JIT mode we dynamically generate trampolines (dynamic invocation
|
||||
// forwarders) that perform type checks when arriving to a method from a
|
||||
// dynamic call-site.
|
||||
// In AOT mode we don't dynamically generate such trampolines but
|
||||
// instead rely on a static analysis to discover which methods can
|
||||
// be invoked dynamically.
|
||||
if (method.name() == Symbols::Call().raw()) {
|
||||
// Currently we consider all call methods to be invoked dynamically and
|
||||
// don't mangle their names.
|
||||
// TODO(vegorov) remove this once we also introduce special type checking
|
||||
// entry point for closures.
|
||||
return false;
|
||||
}
|
||||
return !FLAG_precompiled_mode || !attrs.has_dynamic_invocations;
|
||||
}
|
||||
|
||||
void FunctionNodeHelper::ReadUntilExcluding(Field field) {
|
||||
if (field <= next_read_) return;
|
||||
|
||||
@@ -1690,19 +1715,22 @@ ScopeBuildingResult* StreamingScopeBuilder::BuildScopes() {
|
||||
if (function.IsNonImplicitClosureFunction()) {
|
||||
type_check_mode = kTypeCheckAllParameters;
|
||||
} else if (function.IsImplicitClosureFunction()) {
|
||||
if (!attrs.has_dynamic_invocations) {
|
||||
if (MethodCanSkipTypeChecksForNonCovariantArguments(
|
||||
Function::Handle(Z, function.parent_function()), attrs)) {
|
||||
// This is a tear-off of an instance method that can not be reached
|
||||
// from any dynamic invocation. The method would not check any
|
||||
// parameters except covariant ones and those annotated with
|
||||
// generic-covariant-impl. Which means that we have to check
|
||||
// the rest in the tear-off itself..
|
||||
type_check_mode = kTypeCheckForTearOffOfNonDynamicallyInvokedMethod;
|
||||
// the rest in the tear-off itself.
|
||||
type_check_mode =
|
||||
kTypeCheckEverythingNotCheckedInNonDynamicallyInvokedMethod;
|
||||
}
|
||||
} else {
|
||||
if (function.is_static()) {
|
||||
// In static functions we don't check anything.
|
||||
type_check_mode = kTypeCheckForStaticFunction;
|
||||
} else if (!attrs.has_dynamic_invocations) {
|
||||
} else if (MethodCanSkipTypeChecksForNonCovariantArguments(function,
|
||||
attrs)) {
|
||||
// If the current function is never a target of a dynamic invocation
|
||||
// and this parameter is not marked with generic-covariant-impl
|
||||
// (which means that among all super-interfaces no type parameters
|
||||
@@ -1769,7 +1797,8 @@ ScopeBuildingResult* StreamingScopeBuilder::BuildScopes() {
|
||||
AbstractType::ZoneHandle(Z, function.ParameterTypeAt(pos)));
|
||||
scope_->InsertParameterAt(pos++, result_->setter_value);
|
||||
|
||||
if (is_method && !attrs.has_dynamic_invocations) {
|
||||
if (is_method &&
|
||||
MethodCanSkipTypeChecksForNonCovariantArguments(function, attrs)) {
|
||||
FieldHelper field_helper(builder_);
|
||||
field_helper.ReadUntilIncluding(FieldHelper::kFlags);
|
||||
|
||||
@@ -1783,6 +1812,43 @@ ScopeBuildingResult* StreamingScopeBuilder::BuildScopes() {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RawFunction::kDynamicInvocationForwarder: {
|
||||
if (builder_->PeekTag() == kField) {
|
||||
ASSERT(Field::IsSetterName(String::Handle(function.name())));
|
||||
|
||||
// Create [this] variable.
|
||||
const Class& klass = Class::Handle(Z, function.Owner());
|
||||
result_->this_variable =
|
||||
MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
|
||||
Symbols::This(), H.GetCanonicalType(klass));
|
||||
scope_->InsertParameterAt(0, result_->this_variable);
|
||||
|
||||
// Create setter value variable.
|
||||
result_->setter_value = MakeVariable(
|
||||
TokenPosition::kNoSource, TokenPosition::kNoSource,
|
||||
Symbols::Value(),
|
||||
AbstractType::ZoneHandle(Z, function.ParameterTypeAt(1)));
|
||||
scope_->InsertParameterAt(1, result_->setter_value);
|
||||
} else {
|
||||
builder_->ReadUntilFunctionNode();
|
||||
function_node_helper.ReadUntilExcluding(
|
||||
FunctionNodeHelper::kPositionalParameters);
|
||||
|
||||
// Create [this] variable.
|
||||
intptr_t pos = 0;
|
||||
Class& klass = Class::Handle(Z, function.Owner());
|
||||
result_->this_variable =
|
||||
MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
|
||||
Symbols::This(), H.GetCanonicalType(klass));
|
||||
scope_->InsertParameterAt(pos++, result_->this_variable);
|
||||
|
||||
// Create all positional and named parameters.
|
||||
AddPositionalAndNamedParameters(
|
||||
pos, kTypeCheckEverythingNotCheckedInNonDynamicallyInvokedMethod,
|
||||
attrs);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RawFunction::kMethodExtractor: {
|
||||
// Add a receiver parameter. Though it is captured, we emit code to
|
||||
// explicitly copy it to a fixed offset in a freshly-allocated context
|
||||
@@ -2863,7 +2929,7 @@ void StreamingScopeBuilder::AddVariableDeclarationParameter(
|
||||
variable->set_is_forced_stack();
|
||||
}
|
||||
|
||||
const bool needs_covariant_checke_in_method =
|
||||
const bool needs_covariant_check_in_method =
|
||||
helper.IsCovariant() ||
|
||||
(helper.IsGenericCovariantImpl() && attrs.has_non_this_uses);
|
||||
|
||||
@@ -2871,8 +2937,8 @@ void StreamingScopeBuilder::AddVariableDeclarationParameter(
|
||||
case kTypeCheckAllParameters:
|
||||
variable->set_type_check_mode(LocalVariable::kDoTypeCheck);
|
||||
break;
|
||||
case kTypeCheckForTearOffOfNonDynamicallyInvokedMethod:
|
||||
if (needs_covariant_checke_in_method) {
|
||||
case kTypeCheckEverythingNotCheckedInNonDynamicallyInvokedMethod:
|
||||
if (needs_covariant_check_in_method) {
|
||||
// Don't type check covariant parameters - they will be checked by
|
||||
// a function we forward to. Their types however are not known.
|
||||
variable->set_type_check_mode(LocalVariable::kSkipTypeCheck);
|
||||
@@ -2881,7 +2947,7 @@ void StreamingScopeBuilder::AddVariableDeclarationParameter(
|
||||
}
|
||||
break;
|
||||
case kTypeCheckForNonDynamicallyInvokedMethod:
|
||||
if (needs_covariant_checke_in_method) {
|
||||
if (needs_covariant_check_in_method) {
|
||||
variable->set_type_check_mode(LocalVariable::kDoTypeCheck);
|
||||
} else {
|
||||
// Types of non-covariant parameters are guaranteed to match by
|
||||
@@ -5266,8 +5332,13 @@ FlowGraph* StreamingFlowGraphBuilder::BuildGraphOfFieldAccessor(
|
||||
|
||||
const Function& function = parsed_function()->function();
|
||||
|
||||
bool is_setter = function.IsImplicitSetterFunction();
|
||||
bool is_method = !function.IsStaticFunction();
|
||||
// Instead of building a dynamic invocation forwarder that checks argument
|
||||
// type and then invokes original setter we simply generate the type check
|
||||
// and inlined field store. Scope builder takes care of setting correct
|
||||
// type check mode in this case.
|
||||
const bool is_setter = function.IsDynamicInvocationForwader() ||
|
||||
function.IsImplicitSetterFunction();
|
||||
const bool is_method = !function.IsStaticFunction();
|
||||
Field& field = Field::ZoneHandle(
|
||||
Z, H.LookupFieldByKernelField(field_helper.canonical_name_));
|
||||
|
||||
@@ -5277,6 +5348,10 @@ FlowGraph* StreamingFlowGraphBuilder::BuildGraphOfFieldAccessor(
|
||||
|
||||
Fragment body(normal_entry);
|
||||
if (is_setter) {
|
||||
// We only expect to generate a dynamic invocation forwarder if
|
||||
// the value needs type check.
|
||||
ASSERT(!function.IsDynamicInvocationForwader() ||
|
||||
setter_value->needs_type_check());
|
||||
if (is_method) {
|
||||
body += LoadLocal(scopes()->this_variable);
|
||||
}
|
||||
@@ -5695,18 +5770,17 @@ FlowGraph* StreamingFlowGraphBuilder::BuildGraphOfImplicitClosureFunction(
|
||||
// Tearoffs of static methods needs to perform arguments checks since
|
||||
// static methods they forward to don't do it themselves.
|
||||
AlternativeReadingScope _(&reader_);
|
||||
body += BuildArgumentTypeChecks();
|
||||
body += BuildArgumentTypeChecks(kCheckAllTypeParameterBounds);
|
||||
} else {
|
||||
// Check if parent function was annotated with no-dynamic-invocations.
|
||||
const ProcedureAttributesMetadata attrs =
|
||||
procedure_attributes_metadata_helper_.GetProcedureAttributes(
|
||||
parent.kernel_offset());
|
||||
if (!attrs.has_dynamic_invocations) {
|
||||
if (MethodCanSkipTypeChecksForNonCovariantArguments(parent, attrs)) {
|
||||
// If it was then we might need to build some checks in the
|
||||
// tear-off.
|
||||
AlternativeReadingScope _(&reader_);
|
||||
body +=
|
||||
BuildArgumentTypeChecks(kTypeChecksForNoDynamicInvocationsTearOff);
|
||||
body += BuildArgumentTypeChecks(kCheckNonCovariantTypeParameterBounds);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5813,7 +5887,7 @@ FlowGraph* StreamingFlowGraphBuilder::BuildGraphOfNoSuchMethodForwarder(
|
||||
|
||||
if (function.NeedsArgumentTypeChecks(I)) {
|
||||
AlternativeReadingScope _(&reader_);
|
||||
body += BuildArgumentTypeChecks();
|
||||
body += BuildArgumentTypeChecks(kCheckAllTypeParameterBounds);
|
||||
}
|
||||
|
||||
function_node_helper.ReadUntilExcluding(
|
||||
@@ -6062,8 +6136,84 @@ FlowGraph* StreamingFlowGraphBuilder::BuildGraphOfNoSuchMethodForwarder(
|
||||
B->last_used_block_id_, prologue_info);
|
||||
}
|
||||
|
||||
bool StreamingFlowGraphBuilder::NeedsDynamicInvocationForwarder(
|
||||
const Function& function) {
|
||||
// Setup a [ActiveClassScope] and a [ActiveMemberScope] which will be used
|
||||
// e.g. for type translation.
|
||||
const Class& klass = Class::Handle(zone_, function.Owner());
|
||||
Function& outermost_function = Function::Handle(Z);
|
||||
DiscoverEnclosingElements(Z, function, &outermost_function);
|
||||
|
||||
ActiveClassScope active_class_scope(active_class(), &klass);
|
||||
ActiveMemberScope active_member(active_class(), &outermost_function);
|
||||
ActiveTypeParametersScope active_type_params(active_class(), function, Z);
|
||||
|
||||
SetOffset(function.kernel_offset());
|
||||
|
||||
// Handle setters.
|
||||
if (PeekTag() == kField) {
|
||||
ASSERT(function.IsImplicitSetterFunction());
|
||||
FieldHelper field_helper(this);
|
||||
field_helper.ReadUntilIncluding(FieldHelper::kFlags);
|
||||
return !(field_helper.IsCovariant() ||
|
||||
field_helper.IsGenericCovariantImpl());
|
||||
}
|
||||
|
||||
ReadUntilFunctionNode();
|
||||
|
||||
FunctionNodeHelper function_node_helper(this);
|
||||
function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kTypeParameters);
|
||||
intptr_t num_type_params = ReadListLength();
|
||||
|
||||
for (intptr_t i = 0; i < num_type_params; ++i) {
|
||||
TypeParameterHelper helper(this);
|
||||
helper.ReadUntilExcludingAndSetJustRead(TypeParameterHelper::kBound);
|
||||
AbstractType& bound = T.BuildType(); // read bound
|
||||
helper.Finish();
|
||||
|
||||
if (!bound.IsTopType() && !helper.IsGenericCovariantImpl()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
function_node_helper.SetJustRead(FunctionNodeHelper::kTypeParameters);
|
||||
function_node_helper.ReadUntilExcluding(
|
||||
FunctionNodeHelper::kPositionalParameters);
|
||||
|
||||
// Positional.
|
||||
const intptr_t num_positional_params = ReadListLength();
|
||||
for (intptr_t i = 0; i < num_positional_params; ++i) {
|
||||
VariableDeclarationHelper helper(this);
|
||||
helper.ReadUntilExcluding(VariableDeclarationHelper::kType);
|
||||
AbstractType& type = T.BuildType(); // read type.
|
||||
helper.SetJustRead(VariableDeclarationHelper::kType);
|
||||
helper.ReadUntilExcluding(VariableDeclarationHelper::kEnd);
|
||||
|
||||
if (!type.IsTopType() && !helper.IsGenericCovariantImpl() &&
|
||||
!helper.IsCovariant()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Named.
|
||||
const intptr_t num_named_params = ReadListLength();
|
||||
for (intptr_t i = 0; i < num_named_params; ++i) {
|
||||
VariableDeclarationHelper helper(this);
|
||||
helper.ReadUntilExcluding(VariableDeclarationHelper::kType);
|
||||
AbstractType& type = T.BuildType(); // read type.
|
||||
helper.SetJustRead(VariableDeclarationHelper::kType);
|
||||
helper.ReadUntilExcluding(VariableDeclarationHelper::kEnd);
|
||||
|
||||
if (!type.IsTopType() && !helper.IsGenericCovariantImpl() &&
|
||||
!helper.IsCovariant()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Fragment StreamingFlowGraphBuilder::BuildArgumentTypeChecks(
|
||||
TypeChecksToBuild mode /*= kDefaultTypeChecks*/) {
|
||||
TypeChecksToBuild mode) {
|
||||
FunctionNodeHelper function_node_helper(this);
|
||||
function_node_helper.SetNext(FunctionNodeHelper::kTypeParameters);
|
||||
const Function& dart_function = parsed_function()->function();
|
||||
@@ -6081,45 +6231,65 @@ Fragment StreamingFlowGraphBuilder::BuildArgumentTypeChecks(
|
||||
ASSERT(!forwarding_target->IsNull());
|
||||
}
|
||||
|
||||
// Type parameters
|
||||
if (mode == kDefaultTypeChecks) {
|
||||
intptr_t num_type_params = ReadListLength();
|
||||
TypeArguments& forwarding_params = TypeArguments::Handle(Z);
|
||||
if (forwarding_target != NULL) {
|
||||
forwarding_params = forwarding_target->type_parameters();
|
||||
ASSERT(forwarding_params.Length() == num_type_params);
|
||||
}
|
||||
TypeParameter& forwarding_param = TypeParameter::Handle(Z);
|
||||
for (intptr_t i = 0; i < num_type_params; ++i) {
|
||||
TypeParameterHelper helper(this);
|
||||
helper.ReadUntilExcludingAndSetJustRead(TypeParameterHelper::kBound);
|
||||
String& name = H.DartSymbolObfuscate(helper.name_index_);
|
||||
AbstractType& bound = T.BuildType(); // read bound
|
||||
helper.Finish();
|
||||
|
||||
if (forwarding_target != NULL) {
|
||||
forwarding_param ^= forwarding_params.TypeAt(i);
|
||||
bound = forwarding_param.bound();
|
||||
}
|
||||
|
||||
if (I->strong() && !bound.IsObjectType() && !bound.IsDynamicType() &&
|
||||
(I->reify_generic_functions() || dart_function.IsFactory())) {
|
||||
TypeParameter& param = TypeParameter::Handle(Z);
|
||||
if (dart_function.IsFactory()) {
|
||||
param ^= TypeArguments::Handle(
|
||||
Class::Handle(dart_function.Owner()).type_parameters())
|
||||
.TypeAt(i);
|
||||
} else {
|
||||
param ^=
|
||||
TypeArguments::Handle(dart_function.type_parameters()).TypeAt(i);
|
||||
}
|
||||
ASSERT(param.IsFinalized());
|
||||
body += CheckTypeArgumentBound(param, bound, name);
|
||||
}
|
||||
}
|
||||
function_node_helper.SetJustRead(FunctionNodeHelper::kTypeParameters);
|
||||
intptr_t num_type_params = ReadListLength();
|
||||
TypeArguments& forwarding_params = TypeArguments::Handle(Z);
|
||||
if (forwarding_target != NULL) {
|
||||
forwarding_params = forwarding_target->type_parameters();
|
||||
ASSERT(forwarding_params.Length() == num_type_params);
|
||||
}
|
||||
|
||||
const bool has_reified_type_arguments =
|
||||
I->strong() && I->reify_generic_functions();
|
||||
|
||||
TypeParameter& forwarding_param = TypeParameter::Handle(Z);
|
||||
for (intptr_t i = 0; i < num_type_params; ++i) {
|
||||
TypeParameterHelper helper(this);
|
||||
helper.ReadUntilExcludingAndSetJustRead(TypeParameterHelper::kBound);
|
||||
String& name = H.DartSymbolObfuscate(helper.name_index_);
|
||||
AbstractType& bound = T.BuildType(); // read bound
|
||||
helper.Finish();
|
||||
|
||||
if (!has_reified_type_arguments) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (forwarding_target != NULL) {
|
||||
forwarding_param ^= forwarding_params.TypeAt(i);
|
||||
bound = forwarding_param.bound();
|
||||
}
|
||||
|
||||
if (bound.IsTopType()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case kCheckAllTypeParameterBounds:
|
||||
break;
|
||||
case kCheckCovariantTypeParameterBounds:
|
||||
if (!helper.IsGenericCovariantImpl()) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case kCheckNonCovariantTypeParameterBounds:
|
||||
if (helper.IsGenericCovariantImpl()) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
TypeParameter& param = TypeParameter::Handle(Z);
|
||||
if (dart_function.IsFactory()) {
|
||||
param ^= TypeArguments::Handle(
|
||||
Class::Handle(dart_function.Owner()).type_parameters())
|
||||
.TypeAt(i);
|
||||
} else {
|
||||
param ^= TypeArguments::Handle(dart_function.type_parameters()).TypeAt(i);
|
||||
}
|
||||
ASSERT(param.IsFinalized());
|
||||
body += CheckTypeArgumentBound(param, bound, name);
|
||||
}
|
||||
|
||||
function_node_helper.SetJustRead(FunctionNodeHelper::kTypeParameters);
|
||||
function_node_helper.ReadUntilExcluding(
|
||||
FunctionNodeHelper::kPositionalParameters);
|
||||
|
||||
@@ -6173,6 +6343,163 @@ Fragment StreamingFlowGraphBuilder::BuildArgumentTypeChecks(
|
||||
return body;
|
||||
}
|
||||
|
||||
Fragment StreamingFlowGraphBuilder::PushAllArguments(PushedArguments* pushed) {
|
||||
ASSERT(I->strong());
|
||||
|
||||
FunctionNodeHelper function_node_helper(this);
|
||||
function_node_helper.SetNext(FunctionNodeHelper::kTypeParameters);
|
||||
|
||||
Fragment body;
|
||||
|
||||
const intptr_t num_type_params = ReadListLength();
|
||||
if (num_type_params > 0) {
|
||||
// Skip type arguments.
|
||||
for (intptr_t i = 0; i < num_type_params; ++i) {
|
||||
TypeParameterHelper helper(this);
|
||||
helper.Finish();
|
||||
}
|
||||
|
||||
if (I->reify_generic_functions()) {
|
||||
body += LoadLocal(parsed_function()->function_type_arguments());
|
||||
body += PushArgument();
|
||||
pushed->type_args_len = num_type_params;
|
||||
}
|
||||
}
|
||||
function_node_helper.SetJustRead(FunctionNodeHelper::kTypeParameters);
|
||||
function_node_helper.ReadUntilExcluding(
|
||||
FunctionNodeHelper::kPositionalParameters);
|
||||
|
||||
// Push receiver.
|
||||
body += LoadLocal(scopes()->this_variable);
|
||||
body += PushArgument();
|
||||
|
||||
// Push positional parameters.
|
||||
const intptr_t num_positional_params = ReadListLength();
|
||||
for (intptr_t i = 0; i < num_positional_params; ++i) {
|
||||
// ith variable offset.
|
||||
const intptr_t offset = ReaderOffset();
|
||||
SkipVariableDeclaration();
|
||||
|
||||
LocalVariable* param = LookupVariable(offset + data_program_offset_);
|
||||
body += LoadLocal(param);
|
||||
body += PushArgument();
|
||||
}
|
||||
|
||||
// Push named parameters.
|
||||
const intptr_t num_named_params = ReadListLength();
|
||||
pushed->argument_names = Array::New(num_named_params, Heap::kOld);
|
||||
for (intptr_t i = 0; i < num_named_params; ++i) {
|
||||
// ith variable offset.
|
||||
const intptr_t offset = ReaderOffset();
|
||||
SkipVariableDeclaration();
|
||||
|
||||
LocalVariable* param = LookupVariable(offset + data_program_offset_);
|
||||
pushed->argument_names.SetAt(i, param->name());
|
||||
body += LoadLocal(param);
|
||||
body += PushArgument();
|
||||
}
|
||||
|
||||
pushed->argument_count = num_positional_params + num_named_params + 1;
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
FlowGraph* StreamingFlowGraphBuilder::BuildGraphOfDynamicInvocationForwarder() {
|
||||
// The prologue builder needs the default parameter values.
|
||||
SetupDefaultParameterValues();
|
||||
|
||||
const Function& dart_function = parsed_function()->function();
|
||||
TargetEntryInstr* normal_entry = flow_graph_builder_->BuildTargetEntry();
|
||||
PrologueInfo prologue_info(-1, -1);
|
||||
BlockEntryInstr* instruction_cursor =
|
||||
flow_graph_builder_->BuildPrologue(normal_entry, &prologue_info);
|
||||
|
||||
flow_graph_builder_->graph_entry_ = new (Z) GraphEntryInstr(
|
||||
*parsed_function(), normal_entry, flow_graph_builder_->osr_id_);
|
||||
|
||||
Fragment body;
|
||||
if (!dart_function.is_native()) {
|
||||
body += flow_graph_builder_->CheckStackOverflowInPrologue(
|
||||
dart_function.token_pos());
|
||||
}
|
||||
|
||||
ASSERT(parsed_function()->node_sequence()->scope()->num_context_variables() ==
|
||||
0);
|
||||
|
||||
FunctionNodeHelper function_node_helper(this);
|
||||
function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kTypeParameters);
|
||||
const intptr_t type_parameters_offset = ReaderOffset();
|
||||
function_node_helper.ReadUntilExcluding(
|
||||
FunctionNodeHelper::kPositionalParameters);
|
||||
intptr_t first_parameter_offset = -1;
|
||||
{
|
||||
AlternativeReadingScope alt(&reader_);
|
||||
intptr_t list_length = ReadListLength(); // read number of positionals.
|
||||
if (list_length > 0) {
|
||||
first_parameter_offset = ReaderOffset() + data_program_offset_;
|
||||
}
|
||||
}
|
||||
// Current position: About to read list of positionals.
|
||||
|
||||
// Should never build a dynamic invocation forwarder for equality
|
||||
// operator.
|
||||
ASSERT(dart_function.name() != Symbols::EqualOperator().raw());
|
||||
|
||||
// Even if the caller did not pass argument vector we would still
|
||||
// call the target with instantiate-to-bounds type arguments.
|
||||
body += BuildDefaultTypeHandling(dart_function, type_parameters_offset);
|
||||
|
||||
String& name = String::Handle(Z, dart_function.name());
|
||||
name = Function::DemangleDynamicInvocationForwarderName(name);
|
||||
const Class& owner = Class::Handle(Z, dart_function.Owner());
|
||||
const Function& target =
|
||||
Function::ZoneHandle(Z, owner.LookupDynamicFunction(name));
|
||||
ASSERT(!target.IsNull());
|
||||
|
||||
// Build argument type checks that complement those that are emitted in the
|
||||
// target.
|
||||
{
|
||||
AlternativeReadingScope alt(&reader_);
|
||||
SetOffset(type_parameters_offset);
|
||||
body += BuildArgumentTypeChecks(kCheckNonCovariantTypeParameterBounds);
|
||||
}
|
||||
|
||||
// Push all arguments and invoke the original method.
|
||||
PushedArguments pushed = {0, 0, Array::ZoneHandle(Z)};
|
||||
{
|
||||
AlternativeReadingScope alt(&reader_);
|
||||
SetOffset(type_parameters_offset);
|
||||
body += PushAllArguments(&pushed);
|
||||
}
|
||||
body += StaticCall(TokenPosition::kNoSource, target, pushed.argument_count,
|
||||
pushed.argument_names, ICData::kNoRebind, nullptr,
|
||||
pushed.type_args_len);
|
||||
|
||||
// Some IL optimization passes assume that result of operator []= invocation
|
||||
// is never used, so we drop it and replace with an explicit null constant.
|
||||
if (name.raw() == Symbols::AssignIndexToken().raw()) {
|
||||
body += Drop();
|
||||
body += NullConstant();
|
||||
}
|
||||
|
||||
body += Return(TokenPosition::kNoSource);
|
||||
|
||||
instruction_cursor->LinkTo(body.entry);
|
||||
|
||||
GraphEntryInstr* graph_entry = flow_graph_builder_->graph_entry_;
|
||||
// When compiling for OSR, use a depth first search to find the OSR
|
||||
// entry and make graph entry jump to it instead of normal entry.
|
||||
// Catch entries are always considered reachable, even if they
|
||||
// become unreachable after OSR.
|
||||
if (flow_graph_builder_->osr_id_ != Compiler::kNoOSRDeoptId) {
|
||||
graph_entry->RelinkToOsrEntry(Z,
|
||||
flow_graph_builder_->last_used_block_id_ + 1);
|
||||
}
|
||||
return new (Z)
|
||||
FlowGraph(*parsed_function(), graph_entry,
|
||||
flow_graph_builder_->last_used_block_id_, prologue_info);
|
||||
}
|
||||
|
||||
FlowGraph* StreamingFlowGraphBuilder::BuildGraphOfFunction(bool constructor) {
|
||||
// The prologue builder needs the default parameter values.
|
||||
SetupDefaultParameterValues();
|
||||
@@ -6288,17 +6615,25 @@ FlowGraph* StreamingFlowGraphBuilder::BuildGraphOfFunction(bool constructor) {
|
||||
body = Fragment(body.entry, non_null_entry);
|
||||
}
|
||||
|
||||
// If we run in checked mode or strong mode, we have to check the type of the
|
||||
// passed arguments.
|
||||
// If we run in checked mode or strong mode, we have to check the type of
|
||||
// the passed arguments.
|
||||
if (dart_function.NeedsArgumentTypeChecks(I)) {
|
||||
// Check if parent function was annotated with no-dynamic-invocations.
|
||||
const ProcedureAttributesMetadata attrs =
|
||||
procedure_attributes_metadata_helper_.GetProcedureAttributes(
|
||||
dart_function.kernel_offset());
|
||||
|
||||
AlternativeReadingScope _(&reader_);
|
||||
SetOffset(type_parameters_offset);
|
||||
body += BuildArgumentTypeChecks();
|
||||
body += BuildArgumentTypeChecks(
|
||||
MethodCanSkipTypeChecksForNonCovariantArguments(dart_function, attrs)
|
||||
? kCheckCovariantTypeParameterBounds
|
||||
: kCheckAllTypeParameterBounds);
|
||||
}
|
||||
|
||||
function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kBody);
|
||||
|
||||
bool has_body = ReadTag() == kSomething; // read first part of body.
|
||||
const bool has_body = ReadTag() == kSomething; // read first part of body.
|
||||
|
||||
if (dart_function.is_native()) {
|
||||
body += flow_graph_builder_->NativeFunctionBody(first_parameter_offset,
|
||||
@@ -6511,9 +6846,10 @@ FlowGraph* StreamingFlowGraphBuilder::BuildGraphOfFunction(bool constructor) {
|
||||
flow_graph_builder_->last_used_block_id_, prologue_info);
|
||||
}
|
||||
|
||||
FlowGraph* StreamingFlowGraphBuilder::BuildGraph(intptr_t kernel_offset) {
|
||||
FlowGraph* StreamingFlowGraphBuilder::BuildGraph() {
|
||||
ASSERT(Error::Handle(Z, H.thread()->sticky_error()).IsNull());
|
||||
const Function& function = parsed_function()->function();
|
||||
const intptr_t kernel_offset = function.kernel_offset();
|
||||
|
||||
// Setup a [ActiveClassScope] and a [ActiveMemberScope] which will be used
|
||||
// e.g. for type translation.
|
||||
@@ -6585,6 +6921,13 @@ FlowGraph* StreamingFlowGraphBuilder::BuildGraph(intptr_t kernel_offset) {
|
||||
? BuildGraphOfFieldInitializer()
|
||||
: BuildGraphOfFieldAccessor(scopes()->setter_value);
|
||||
}
|
||||
case RawFunction::kDynamicInvocationForwarder:
|
||||
if (PeekTag() == kField) {
|
||||
return BuildGraphOfFieldAccessor(scopes()->setter_value);
|
||||
} else {
|
||||
ReadUntilFunctionNode(parsed_function());
|
||||
return BuildGraphOfDynamicInvocationForwarder();
|
||||
}
|
||||
case RawFunction::kMethodExtractor:
|
||||
return flow_graph_builder_->BuildGraphOfMethodExtractor(function);
|
||||
case RawFunction::kNoSuchMethodDispatcher:
|
||||
@@ -8262,8 +8605,15 @@ Fragment StreamingFlowGraphBuilder::BuildPropertySet(TokenPosition* p) {
|
||||
} else {
|
||||
const intptr_t kTypeArgsLen = 0;
|
||||
const intptr_t kNumArgsChecked = 1;
|
||||
|
||||
const String* mangled_name = &setter_name;
|
||||
if (!FLAG_precompiled_mode && I->strong() && H.IsRoot(itarget_name)) {
|
||||
mangled_name = &String::ZoneHandle(
|
||||
Z, Function::CreateDynamicInvocationForwarderName(setter_name));
|
||||
}
|
||||
|
||||
instructions +=
|
||||
InstanceCall(position, setter_name, Token::kSET, kTypeArgsLen, 2,
|
||||
InstanceCall(position, *mangled_name, Token::kSET, kTypeArgsLen, 2,
|
||||
Array::null_array(), kNumArgsChecked, *interface_target,
|
||||
/* result_type = */ NULL);
|
||||
}
|
||||
@@ -8820,10 +9170,22 @@ Fragment StreamingFlowGraphBuilder::BuildMethodInvocation(TokenPosition* p) {
|
||||
argument_names, ICData::kNoRebind, &result_type,
|
||||
type_args_len);
|
||||
} else {
|
||||
const String* mangled_name = &name;
|
||||
// Do not mangle == or call:
|
||||
// * operator == takes an Object so its either not checked or checked
|
||||
// at the entry because the parameter is marked covariant, neither of
|
||||
// those cases require a dynamic invocation forwarder;
|
||||
// * we assume that all closures are entered in a checked way.
|
||||
if (!FLAG_precompiled_mode && I->strong() &&
|
||||
(name.raw() != Symbols::EqualOperator().raw()) &&
|
||||
(name.raw() != Symbols::Call().raw()) && H.IsRoot(itarget_name)) {
|
||||
mangled_name = &String::ZoneHandle(
|
||||
Z, Function::CreateDynamicInvocationForwarderName(name));
|
||||
}
|
||||
instructions +=
|
||||
InstanceCall(position, name, token_kind, type_args_len, argument_count,
|
||||
argument_names, checked_argument_count, *interface_target,
|
||||
&result_type);
|
||||
InstanceCall(position, *mangled_name, token_kind, type_args_len,
|
||||
argument_count, argument_names, checked_argument_count,
|
||||
*interface_target, &result_type);
|
||||
}
|
||||
|
||||
// Drop temporaries preserving result on the top of the stack.
|
||||
|
||||
@@ -115,6 +115,10 @@ class TypeParameterHelper {
|
||||
|
||||
void Finish() { ReadUntilExcluding(kEnd); }
|
||||
|
||||
bool IsGenericCovariantImpl() {
|
||||
return (flags_ & kIsGenericCovariantImpl) != 0;
|
||||
}
|
||||
|
||||
TokenPosition position_;
|
||||
uint8_t flags_;
|
||||
StringIndex name_index_;
|
||||
@@ -814,8 +818,8 @@ class StreamingScopeBuilder {
|
||||
|
||||
// Only parameters *not* marked as covariant or generic-covariant-impl will
|
||||
// be checked. The rest would be checked in the method itself.
|
||||
// Inverse of kTypeCheckOnlyGenericCovariantImplParameters.
|
||||
kTypeCheckForTearOffOfNonDynamicallyInvokedMethod,
|
||||
// Inverse of kTypeCheckForNonDynamicallyInvokedMethod.
|
||||
kTypeCheckEverythingNotCheckedInNonDynamicallyInvokedMethod,
|
||||
|
||||
// No parameters will be checked.
|
||||
kTypeCheckForStaticFunction,
|
||||
@@ -1273,7 +1277,9 @@ class StreamingFlowGraphBuilder : public KernelReaderHelper {
|
||||
|
||||
virtual ~StreamingFlowGraphBuilder() {}
|
||||
|
||||
FlowGraph* BuildGraph(intptr_t kernel_offset);
|
||||
bool NeedsDynamicInvocationForwarder(const Function& function);
|
||||
|
||||
FlowGraph* BuildGraph();
|
||||
|
||||
void ReportUnexpectedTag(const char* variant, Tag tag) override;
|
||||
|
||||
@@ -1296,8 +1302,6 @@ class StreamingFlowGraphBuilder : public KernelReaderHelper {
|
||||
// it crosses a procedure node for a concrete forwarding stub.
|
||||
void ReadUntilFunctionNode(ParsedFunction* set_forwarding_stub = NULL);
|
||||
|
||||
enum DispatchCategory { Interface, ViaThis, Closure, DynamicDispatch };
|
||||
|
||||
private:
|
||||
void LoadAndSetupTypeParameters(ActiveClass* active_class,
|
||||
const Object& set_on,
|
||||
@@ -1320,6 +1324,7 @@ class StreamingFlowGraphBuilder : public KernelReaderHelper {
|
||||
Fragment BuildInitializers(const Class& parent_class);
|
||||
FlowGraph* BuildGraphOfImplicitClosureFunction(const Function& function);
|
||||
FlowGraph* BuildGraphOfFunction(bool constructor);
|
||||
FlowGraph* BuildGraphOfDynamicInvocationForwarder();
|
||||
FlowGraph* BuildGraphOfNoSuchMethodForwarder(
|
||||
const Function& function,
|
||||
bool is_implicit_closure_function,
|
||||
@@ -1418,15 +1423,23 @@ class StreamingFlowGraphBuilder : public KernelReaderHelper {
|
||||
const InferredTypeMetadata* result_type = NULL);
|
||||
|
||||
enum TypeChecksToBuild {
|
||||
kDefaultTypeChecks,
|
||||
kTypeChecksForNoDynamicInvocationsTearOff
|
||||
kCheckAllTypeParameterBounds,
|
||||
kCheckNonCovariantTypeParameterBounds,
|
||||
kCheckCovariantTypeParameterBounds,
|
||||
};
|
||||
|
||||
// Does not move the cursor.
|
||||
Fragment BuildDefaultTypeHandling(const Function& function,
|
||||
intptr_t type_parameters_offset);
|
||||
|
||||
Fragment BuildArgumentTypeChecks(TypeChecksToBuild mode = kDefaultTypeChecks);
|
||||
struct PushedArguments {
|
||||
intptr_t type_args_len;
|
||||
intptr_t argument_count;
|
||||
Array& argument_names;
|
||||
};
|
||||
Fragment PushAllArguments(PushedArguments* pushed);
|
||||
|
||||
Fragment BuildArgumentTypeChecks(TypeChecksToBuild mode);
|
||||
|
||||
Fragment ThrowException(TokenPosition position);
|
||||
Fragment BooleanNegate();
|
||||
|
||||
@@ -2061,6 +2061,28 @@ void FlowGraphBuilder::InlineBailout(const char* reason) {
|
||||
}
|
||||
}
|
||||
|
||||
bool FlowGraphBuilder::NeedsDynamicInvocationForwarder(
|
||||
const Function& function) {
|
||||
ASSERT(Isolate::Current()->strong());
|
||||
|
||||
Thread* thread = Thread::Current();
|
||||
Zone* zone_ = thread->zone();
|
||||
|
||||
TranslationHelper helper(thread);
|
||||
Script& script = Script::Handle(Z, function.script());
|
||||
helper.InitFromScript(script);
|
||||
|
||||
const Class& owner_class = Class::Handle(Z, function.Owner());
|
||||
ActiveClass active_class;
|
||||
ActiveClassScope active_class_scope(&active_class, &owner_class);
|
||||
|
||||
StreamingFlowGraphBuilder streaming_flow_graph_builder(
|
||||
&helper, script, Z, ExternalTypedData::Handle(Z, function.KernelData()),
|
||||
function.KernelDataProgramOffset(), &active_class);
|
||||
|
||||
return streaming_flow_graph_builder.NeedsDynamicInvocationForwarder(function);
|
||||
}
|
||||
|
||||
FlowGraph* FlowGraphBuilder::BuildGraph() {
|
||||
const Function& function = parsed_function_->function();
|
||||
|
||||
@@ -2080,8 +2102,7 @@ FlowGraph* FlowGraphBuilder::BuildGraph() {
|
||||
this, ExternalTypedData::Handle(Z, function.KernelData()),
|
||||
function.KernelDataProgramOffset());
|
||||
streaming_flow_graph_builder_ = &streaming_flow_graph_builder;
|
||||
FlowGraph* result =
|
||||
streaming_flow_graph_builder_->BuildGraph(function.kernel_offset());
|
||||
FlowGraph* result = streaming_flow_graph_builder_->BuildGraph();
|
||||
streaming_flow_graph_builder_ = NULL;
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -217,6 +217,7 @@ class ActiveClass {
|
||||
function_kind == RawFunction::kGetterFunction ||
|
||||
function_kind == RawFunction::kSetterFunction ||
|
||||
function_kind == RawFunction::kMethodExtractor ||
|
||||
function_kind == RawFunction::kDynamicInvocationForwarder ||
|
||||
member->IsFactory();
|
||||
}
|
||||
|
||||
@@ -705,6 +706,12 @@ class FlowGraphBuilder : public BaseFlowGraphBuilder {
|
||||
|
||||
FlowGraph* BuildGraph();
|
||||
|
||||
// Returns true if the given function needs dynamic invocation forwarder:
|
||||
// that is if any of the arguments require checking on the dynamic
|
||||
// call-site: if function has no parameters or has only covariant parameters
|
||||
// as such function already checks all of its parameters.
|
||||
static bool NeedsDynamicInvocationForwarder(const Function& function);
|
||||
|
||||
private:
|
||||
BlockEntryInstr* BuildPrologue(TargetEntryInstr* normal_entry,
|
||||
PrologueInfo* prologue_info);
|
||||
|
||||
@@ -687,6 +687,13 @@ class Isolate : public BaseIsolate {
|
||||
isolate_flags_ = IsServiceIsolateBit::update(value, isolate_flags_);
|
||||
}
|
||||
|
||||
bool is_kernel_isolate() const {
|
||||
return IsKernelIsolateBit::decode(isolate_flags_);
|
||||
}
|
||||
void set_is_kernel_isolate(bool value) {
|
||||
isolate_flags_ = IsKernelIsolateBit::update(value, isolate_flags_);
|
||||
}
|
||||
|
||||
bool should_load_vmservice() const {
|
||||
return ShouldLoadVmServiceBit::decode(isolate_flags_);
|
||||
}
|
||||
@@ -844,6 +851,7 @@ class Isolate : public BaseIsolate {
|
||||
V(ErrorsFatal) \
|
||||
V(IsRunnable) \
|
||||
V(IsServiceIsolate) \
|
||||
V(IsKernelIsolate) \
|
||||
V(CompilationAllowed) \
|
||||
V(AllClassesFinalized) \
|
||||
V(RemappingCids) \
|
||||
|
||||
@@ -265,6 +265,9 @@ bool KernelIsolate::Exists() {
|
||||
|
||||
void KernelIsolate::SetKernelIsolate(Isolate* isolate) {
|
||||
MonitorLocker ml(monitor_);
|
||||
if (isolate != nullptr) {
|
||||
isolate->set_is_kernel_isolate(true);
|
||||
}
|
||||
isolate_ = isolate;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -115,7 +115,7 @@ intptr_t Log::cursor() const {
|
||||
|
||||
bool Log::ShouldLogForIsolate(const Isolate* isolate) {
|
||||
if (FLAG_isolate_log_filter == NULL) {
|
||||
if (isolate->is_service_isolate()) {
|
||||
if (isolate->is_service_isolate() || isolate->is_kernel_isolate()) {
|
||||
// By default, do not log for the service isolate.
|
||||
return false;
|
||||
}
|
||||
|
||||
+133
-20
@@ -2714,27 +2714,54 @@ void Class::CalculateFieldOffsets() const {
|
||||
set_next_field_offset(offset);
|
||||
}
|
||||
|
||||
struct InvocationDispatcherCacheLayout {
|
||||
enum { kNameIndex = 0, kArgsDescIndex, kFunctionIndex, kEntrySize };
|
||||
};
|
||||
|
||||
void Class::AddInvocationDispatcher(const String& target_name,
|
||||
const Array& args_desc,
|
||||
const Function& dispatcher) const {
|
||||
// Search for a free entry.
|
||||
Array& cache = Array::Handle(invocation_dispatcher_cache());
|
||||
intptr_t i = 0;
|
||||
while (i < cache.Length() && cache.At(i) != Object::null()) {
|
||||
i += InvocationDispatcherCacheLayout::kEntrySize;
|
||||
}
|
||||
|
||||
if (i == cache.Length()) {
|
||||
// Allocate new larger cache.
|
||||
intptr_t new_len =
|
||||
(cache.Length() == 0)
|
||||
? static_cast<intptr_t>(InvocationDispatcherCacheLayout::kEntrySize)
|
||||
: cache.Length() * 2;
|
||||
cache ^= Array::Grow(cache, new_len);
|
||||
set_invocation_dispatcher_cache(cache);
|
||||
}
|
||||
cache.SetAt(i + InvocationDispatcherCacheLayout::kNameIndex, target_name);
|
||||
cache.SetAt(i + InvocationDispatcherCacheLayout::kArgsDescIndex, args_desc);
|
||||
cache.SetAt(i + InvocationDispatcherCacheLayout::kFunctionIndex, dispatcher);
|
||||
}
|
||||
|
||||
RawFunction* Class::GetInvocationDispatcher(const String& target_name,
|
||||
const Array& args_desc,
|
||||
RawFunction::Kind kind,
|
||||
bool create_if_absent) const {
|
||||
enum { kNameIndex = 0, kArgsDescIndex, kFunctionIndex, kEntrySize };
|
||||
|
||||
ASSERT(kind == RawFunction::kNoSuchMethodDispatcher ||
|
||||
kind == RawFunction::kInvokeFieldDispatcher);
|
||||
kind == RawFunction::kInvokeFieldDispatcher ||
|
||||
kind == RawFunction::kDynamicInvocationForwarder);
|
||||
Function& dispatcher = Function::Handle();
|
||||
Array& cache = Array::Handle(invocation_dispatcher_cache());
|
||||
ASSERT(!cache.IsNull());
|
||||
String& name = String::Handle();
|
||||
Array& desc = Array::Handle();
|
||||
intptr_t i = 0;
|
||||
for (; i < cache.Length(); i += kEntrySize) {
|
||||
name ^= cache.At(i + kNameIndex);
|
||||
for (; i < cache.Length(); i += InvocationDispatcherCacheLayout::kEntrySize) {
|
||||
name ^= cache.At(i + InvocationDispatcherCacheLayout::kNameIndex);
|
||||
if (name.IsNull()) break; // Reached last entry.
|
||||
if (!name.Equals(target_name)) continue;
|
||||
desc ^= cache.At(i + kArgsDescIndex);
|
||||
desc ^= cache.At(i + InvocationDispatcherCacheLayout::kArgsDescIndex);
|
||||
if (desc.raw() != args_desc.raw()) continue;
|
||||
dispatcher ^= cache.At(i + kFunctionIndex);
|
||||
dispatcher ^= cache.At(i + InvocationDispatcherCacheLayout::kFunctionIndex);
|
||||
if (dispatcher.kind() == kind) {
|
||||
// Found match.
|
||||
ASSERT(dispatcher.IsFunction());
|
||||
@@ -2743,18 +2770,8 @@ RawFunction* Class::GetInvocationDispatcher(const String& target_name,
|
||||
}
|
||||
|
||||
if (dispatcher.IsNull() && create_if_absent) {
|
||||
if (i == cache.Length()) {
|
||||
// Allocate new larger cache.
|
||||
intptr_t new_len = (cache.Length() == 0)
|
||||
? static_cast<intptr_t>(kEntrySize)
|
||||
: cache.Length() * 2;
|
||||
cache ^= Array::Grow(cache, new_len);
|
||||
set_invocation_dispatcher_cache(cache);
|
||||
}
|
||||
dispatcher ^= CreateInvocationDispatcher(target_name, args_desc, kind);
|
||||
cache.SetAt(i + kNameIndex, target_name);
|
||||
cache.SetAt(i + kArgsDescIndex, args_desc);
|
||||
cache.SetAt(i + kFunctionIndex, dispatcher);
|
||||
AddInvocationDispatcher(target_name, args_desc, dispatcher);
|
||||
}
|
||||
return dispatcher.raw();
|
||||
}
|
||||
@@ -2874,6 +2891,81 @@ RawFunction* Function::GetMethodExtractor(const String& getter_name) const {
|
||||
return result.raw();
|
||||
}
|
||||
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
RawFunction* Function::CreateDynamicInvocationForwarder(
|
||||
const String& mangled_name) const {
|
||||
Thread* thread = Thread::Current();
|
||||
Zone* zone = thread->zone();
|
||||
|
||||
Function& forwarder = Function::Handle(zone);
|
||||
forwarder ^= Object::Clone(*this, Heap::kOld);
|
||||
|
||||
forwarder.set_name(mangled_name);
|
||||
forwarder.set_kind(RawFunction::kDynamicInvocationForwarder);
|
||||
forwarder.set_is_debuggable(false);
|
||||
|
||||
// TODO(vegorov) for error reporting reasons it is better to make this
|
||||
// function visible and instead use a TailCall to invoke the target.
|
||||
// Our TailCall instruction is not ready for such usage though it
|
||||
// blocks inlining and can't take Function-s only Code objects.
|
||||
forwarder.set_is_visible(false);
|
||||
|
||||
forwarder.ClearICDataArray();
|
||||
forwarder.ClearCode();
|
||||
forwarder.set_usage_counter(0);
|
||||
forwarder.set_deoptimization_counter(0);
|
||||
forwarder.set_optimized_instruction_count(0);
|
||||
forwarder.set_inlining_depth(0);
|
||||
forwarder.set_optimized_call_site_count(0);
|
||||
forwarder.set_kernel_offset(kernel_offset());
|
||||
|
||||
return forwarder.raw();
|
||||
}
|
||||
|
||||
bool Function::IsDynamicInvocationForwaderName(const String& name) {
|
||||
return name.StartsWith(Symbols::DynamicPrefix());
|
||||
}
|
||||
|
||||
RawString* Function::CreateDynamicInvocationForwarderName(const String& name) {
|
||||
return Symbols::FromConcat(Thread::Current(), Symbols::DynamicPrefix(), name);
|
||||
}
|
||||
|
||||
RawString* Function::DemangleDynamicInvocationForwarderName(
|
||||
const String& name) {
|
||||
const intptr_t kDynamicPrefixLength = 4; // "dyn:"
|
||||
ASSERT(Symbols::DynamicPrefix().Length() == kDynamicPrefixLength);
|
||||
return Symbols::New(Thread::Current(), name, kDynamicPrefixLength,
|
||||
name.Length() - kDynamicPrefixLength);
|
||||
}
|
||||
|
||||
RawFunction* Function::GetDynamicInvocationForwarder(
|
||||
const String& mangled_name,
|
||||
bool allow_add /* = true */) const {
|
||||
ASSERT(IsDynamicInvocationForwaderName(mangled_name));
|
||||
const Class& owner = Class::Handle(Owner());
|
||||
Function& result = Function::Handle(owner.GetInvocationDispatcher(
|
||||
mangled_name, Array::null_array(),
|
||||
RawFunction::kDynamicInvocationForwarder, /*create_if_absent=*/false));
|
||||
|
||||
if (!result.IsNull()) {
|
||||
return result.raw();
|
||||
}
|
||||
|
||||
// Check if function actually needs a dynamic invocation forwarder.
|
||||
if (!kernel::FlowGraphBuilder::NeedsDynamicInvocationForwarder(*this)) {
|
||||
result = raw();
|
||||
} else if (allow_add) {
|
||||
result = CreateDynamicInvocationForwarder(mangled_name);
|
||||
}
|
||||
|
||||
if (allow_add) {
|
||||
owner.AddInvocationDispatcher(mangled_name, Array::null_array(), result);
|
||||
}
|
||||
|
||||
return result.raw();
|
||||
}
|
||||
#endif
|
||||
|
||||
bool AbstractType::InstantiateAndTestSubtype(
|
||||
AbstractType* subtype,
|
||||
AbstractType* supertype,
|
||||
@@ -6042,6 +6134,8 @@ const char* Function::KindToCString(RawFunction::Kind kind) {
|
||||
case RawFunction::kIrregexpFunction:
|
||||
return "IrregexpFunction";
|
||||
break;
|
||||
case RawFunction::kDynamicInvocationForwarder:
|
||||
return "DynamicInvocationForwarder";
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return NULL;
|
||||
@@ -7128,7 +7222,6 @@ RawFunction* Function::Clone(const Class& new_owner) const {
|
||||
clone.set_optimized_instruction_count(0);
|
||||
clone.set_inlining_depth(0);
|
||||
clone.set_optimized_call_site_count(0);
|
||||
clone.set_kernel_offset(kernel_offset());
|
||||
|
||||
if (new_owner.NumTypeParameters() > 0) {
|
||||
// Adjust uninstantiated types to refer to type parameters of the new owner.
|
||||
@@ -7969,6 +8062,9 @@ const char* Function::ToCString() const {
|
||||
case RawFunction::kNoSuchMethodDispatcher:
|
||||
kind_str = " no-such-method-dispatcher";
|
||||
break;
|
||||
case RawFunction::kDynamicInvocationForwarder:
|
||||
kind_str = " dynamic-invocation-forwader";
|
||||
break;
|
||||
case RawFunction::kInvokeFieldDispatcher:
|
||||
kind_str = "invoke-field-dispatcher";
|
||||
break;
|
||||
@@ -13694,8 +13790,18 @@ bool ICData::AddSmiSmiCheckForFastSmiStubs() const {
|
||||
const String& name = String::Handle(target_name());
|
||||
const Class& smi_class = Class::Handle(Smi::Class());
|
||||
Zone* zone = Thread::Current()->zone();
|
||||
const Function& smi_op_target =
|
||||
Function& smi_op_target =
|
||||
Function::Handle(Resolver::ResolveDynamicAnyArgs(zone, smi_class, name));
|
||||
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
if (smi_op_target.IsNull() &&
|
||||
Function::IsDynamicInvocationForwaderName(name)) {
|
||||
const String& demangled =
|
||||
String::Handle(Function::DemangleDynamicInvocationForwarderName(name));
|
||||
smi_op_target = Resolver::ResolveDynamicAnyArgs(zone, smi_class, demangled);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (NumberOfChecksIs(0)) {
|
||||
GrowableArray<intptr_t> class_ids(2);
|
||||
class_ids.Add(kSmiCid);
|
||||
@@ -13753,6 +13859,13 @@ void ICData::AddTarget(const Function& target) const {
|
||||
}
|
||||
|
||||
bool ICData::ValidateInterceptor(const Function& target) const {
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
const String& name = String::Handle(target_name());
|
||||
if (Function::IsDynamicInvocationForwaderName(name)) {
|
||||
return Function::DemangleDynamicInvocationForwarderName(name) ==
|
||||
target.name();
|
||||
}
|
||||
#endif
|
||||
ObjectStore* store = Isolate::Current()->object_store();
|
||||
ASSERT((target.raw() == store->simple_instance_of_true_function()) ||
|
||||
(target.raw() == store->simple_instance_of_false_function()));
|
||||
|
||||
@@ -1442,6 +1442,10 @@ class Class : public Object {
|
||||
void CheckReload(const Class& replacement,
|
||||
IsolateReloadContext* context) const;
|
||||
|
||||
void AddInvocationDispatcher(const String& target_name,
|
||||
const Array& args_desc,
|
||||
const Function& dispatcher) const;
|
||||
|
||||
private:
|
||||
bool CanReloadFinalized(const Class& replacement,
|
||||
IsolateReloadContext* context) const;
|
||||
@@ -2302,6 +2306,10 @@ class Function : public Object {
|
||||
return kind() == RawFunction::kInvokeFieldDispatcher;
|
||||
}
|
||||
|
||||
bool IsDynamicInvocationForwader() const {
|
||||
return kind() == RawFunction::kDynamicInvocationForwarder;
|
||||
}
|
||||
|
||||
bool IsImplicitGetterOrSetter() const {
|
||||
return kind() == RawFunction::kImplicitGetter ||
|
||||
kind() == RawFunction::kImplicitSetter ||
|
||||
@@ -2367,6 +2375,7 @@ class Function : public Object {
|
||||
case RawFunction::kMethodExtractor:
|
||||
case RawFunction::kNoSuchMethodDispatcher:
|
||||
case RawFunction::kInvokeFieldDispatcher:
|
||||
case RawFunction::kDynamicInvocationForwarder:
|
||||
return true;
|
||||
case RawFunction::kClosureFunction:
|
||||
case RawFunction::kImplicitClosureFunction:
|
||||
@@ -2400,6 +2409,7 @@ class Function : public Object {
|
||||
case RawFunction::kMethodExtractor:
|
||||
case RawFunction::kNoSuchMethodDispatcher:
|
||||
case RawFunction::kInvokeFieldDispatcher:
|
||||
case RawFunction::kDynamicInvocationForwarder:
|
||||
return false;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
@@ -2609,6 +2619,7 @@ class Function : public Object {
|
||||
case RawFunction::kImplicitSetter:
|
||||
case RawFunction::kNoSuchMethodDispatcher:
|
||||
case RawFunction::kInvokeFieldDispatcher:
|
||||
case RawFunction::kDynamicInvocationForwarder:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
@@ -2770,6 +2781,17 @@ class Function : public Object {
|
||||
RawFunction* CreateMethodExtractor(const String& getter_name) const;
|
||||
RawFunction* GetMethodExtractor(const String& getter_name) const;
|
||||
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
static bool IsDynamicInvocationForwaderName(const String& name);
|
||||
static RawString* DemangleDynamicInvocationForwarderName(const String& name);
|
||||
static RawString* CreateDynamicInvocationForwarderName(const String& name);
|
||||
|
||||
RawFunction* CreateDynamicInvocationForwarder(
|
||||
const String& mangled_name) const;
|
||||
RawFunction* GetDynamicInvocationForwarder(const String& mangled_name,
|
||||
bool allow_add = true) const;
|
||||
#endif
|
||||
|
||||
// Allocate new function object, clone values from this function. The
|
||||
// owner of the clone is new_owner.
|
||||
RawFunction* Clone(const Class& new_owner) const;
|
||||
|
||||
@@ -867,6 +867,9 @@ class RawFunction : public RawObject {
|
||||
kNoSuchMethodDispatcher, // invokes noSuchMethod.
|
||||
kInvokeFieldDispatcher, // invokes a field as a closure.
|
||||
kIrregexpFunction, // represents a generated irregexp matcher function.
|
||||
kDynamicInvocationForwarder, // represents forwarder which performs type
|
||||
// checks for arguments of a dynamic
|
||||
// invocation.
|
||||
};
|
||||
|
||||
enum AsyncModifier {
|
||||
|
||||
+18
-4
@@ -69,16 +69,30 @@ RawFunction* Resolver::ResolveDynamicAnyArgs(Zone* zone,
|
||||
THR_Print("ResolveDynamic '%s' for class %s\n", function_name.ToCString(),
|
||||
String::Handle(zone, cls.Name()).ToCString());
|
||||
}
|
||||
Function& function = Function::Handle(zone);
|
||||
|
||||
String& demangled = String::Handle(zone);
|
||||
|
||||
const bool is_getter = Field::IsGetterName(function_name);
|
||||
String& field_name = String::Handle(zone);
|
||||
if (is_getter) {
|
||||
field_name ^= Field::NameFromGetter(function_name);
|
||||
demangled ^= Field::NameFromGetter(function_name);
|
||||
}
|
||||
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
if (Function::IsDynamicInvocationForwaderName(function_name)) {
|
||||
ASSERT(!FLAG_precompiled_mode);
|
||||
demangled ^=
|
||||
Function::DemangleDynamicInvocationForwarderName(function_name);
|
||||
function =
|
||||
ResolveDynamicAnyArgs(zone, receiver_class, demangled, allow_add);
|
||||
return function.IsNull() ? function.raw()
|
||||
: function.GetDynamicInvocationForwarder(
|
||||
function_name, allow_add);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Now look for an instance function whose name matches function_name
|
||||
// in the class.
|
||||
Function& function = Function::Handle(zone);
|
||||
while (!cls.IsNull()) {
|
||||
function ^= cls.LookupDynamicFunction(function_name);
|
||||
if (!function.IsNull()) {
|
||||
@@ -87,7 +101,7 @@ RawFunction* Resolver::ResolveDynamicAnyArgs(Zone* zone,
|
||||
// Getter invocation might actually be a method extraction.
|
||||
if (FLAG_lazy_dispatchers) {
|
||||
if (is_getter && function.IsNull()) {
|
||||
function ^= cls.LookupDynamicFunction(field_name);
|
||||
function ^= cls.LookupDynamicFunction(demangled);
|
||||
if (!function.IsNull() && allow_add) {
|
||||
// We were looking for the getter but found a method with the same
|
||||
// name. Create a method extractor and return it.
|
||||
|
||||
@@ -1024,6 +1024,16 @@ RawFunction* InlineCacheMissHelper(const Instance& receiver,
|
||||
const String& target_name) {
|
||||
const Class& receiver_class = Class::Handle(receiver.clazz());
|
||||
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
// Handle noSuchMethod for dyn:methodName by getting a noSuchMethod dispatcher
|
||||
// (or a call-through getter for methodName).
|
||||
if (Function::IsDynamicInvocationForwaderName(target_name)) {
|
||||
const String& demangled = String::Handle(
|
||||
Function::DemangleDynamicInvocationForwarderName(target_name));
|
||||
return InlineCacheMissHelper(receiver, args_descriptor, demangled);
|
||||
}
|
||||
#endif
|
||||
|
||||
Function& result = Function::Handle();
|
||||
if (!ResolveCallThroughGetter(receiver, receiver_class, target_name,
|
||||
args_descriptor, &result)) {
|
||||
|
||||
@@ -367,6 +367,7 @@ class ObjectPointerVisitor;
|
||||
V(GetterPrefix, "get:") \
|
||||
V(SetterPrefix, "set:") \
|
||||
V(InitPrefix, "init:") \
|
||||
V(DynamicPrefix, "dyn:") \
|
||||
V(Index, "index") \
|
||||
V(DartScheme, "dart:") \
|
||||
V(DartSchemePrivate, "dart:_") \
|
||||
|
||||
Reference in New Issue
Block a user