[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:
Vyacheslav Egorov
2018-06-25 07:27:04 +00:00
committed by commit-bot@chromium.org
parent f9a11ec380
commit 20ca5bbb05
16 changed files with 681 additions and 99 deletions
@@ -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();
+23 -2
View File
@@ -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);
+8
View File
@@ -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) \
+3
View File
@@ -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
View File
@@ -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
View File
@@ -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()));
+22
View File
@@ -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;
+3
View File
@@ -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
View File
@@ -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.
+10
View File
@@ -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)) {
+1
View File
@@ -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:_") \