[vm] Create more Function helper methods for code clarity.
Creates HasParent() as a predicate for Function objects with a non-null
parent function and makes IsLocalFunction() specific to local closures
(i.e. explicit closures with non-null parent functions).
Creates HasSavedArgumentsDescriptor() as a predicate to determine when
saved_args_desc() can be used, to avoid the caller having to do explicit
predicate checks for each dispatcher type that contains one.
Also simplifies Function::PrintName, creates different suffixes for a
SyncGenClosureMaker and its associateed SyncGenClosure, adds suffixes
for skipping more than one generated body when printing the parent name,
and changes ArgumentsDescriptor::PrintTo to match the compact syntax
Function::PrintName used for signifying the arguments descriptor for
certain dispatchers.
TEST=pkg/vm_snapshot_analysis/test/instruction_sizes_test
Cq-Include-Trybots: luci.dart.try:app-kernel-linux-release-x64-try,vm-kernel-precomp-linux-release-x64-try,pkg-linux-release-try
Change-Id: I0ac1226d1ffe1d81743b5c6788da170d5a4010c5
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/191560
Commit-Queue: Tess Strickland <sstrickl@google.com>
Reviewed-by: Daco Harkes <dacoharkes@google.com>
diff --git a/pkg/vm_snapshot_analysis/test/instruction_sizes_test.dart b/pkg/vm_snapshot_analysis/test/instruction_sizes_test.dart
index ddb2ac4..5347acc 100644
--- a/pkg/vm_snapshot_analysis/test/instruction_sizes_test.dart
+++ b/pkg/vm_snapshot_analysis/test/instruction_sizes_test.dart
@@ -50,6 +50,12 @@
}
}
+class D {
+ static dynamic tornOff() sync* {
+ yield const K(5);
+ }
+}
+
@pragma('vm:never-inline')
Function tearOff(dynamic o) {
return o.tornOff;
@@ -61,6 +67,7 @@
}
print(tearOff(args.isEmpty ? A() : B()));
print(C.tornOff);
+ print(D.tornOff);
}
"""
};
@@ -107,6 +114,12 @@
}
}
+class D {
+ static dynamic tornOff() sync* {
+ yield const K(5);
+ }
+}
+
@pragma('vm:never-inline')
Function tearOff(dynamic o) {
return o.tornOff;
@@ -116,6 +129,7 @@
// modified
print(tearOff(args.isEmpty ? A() : B()));
print(C.tornOff);
+ print(D.tornOff);
}
"""
};
@@ -154,6 +168,12 @@
}
}
+class D {
+ static dynamic tornOff() sync* {
+ yield const K(5);
+ }
+}
+
@pragma('vm:never-inline')
Function tearOff(dynamic o) {
return o.tornOff;
@@ -165,6 +185,7 @@
}
print(tearOff(args.isEmpty ? A() : B()));
print(C.tornOff);
+ print(D.tornOff);
}
"""
};
@@ -285,6 +306,10 @@
// with {body}.
expect(inputDartSymbolNames['C'], contains('[tear-off] tornOff'));
+ // Presence of sync* modifier should not cause tear-off name to end
+ // with {body}.
+ expect(inputDartSymbolNames['D'], contains('[tear-off] tornOff'));
+
// Check that output does not contain '[unknown stub]'
expect(symbolRawNames[''][''], isNot(contains('[unknown stub]')),
reason: 'All stubs must be named');
@@ -306,6 +331,7 @@
expect(inputLib.children, contains('A'));
expect(inputLib.children, contains('B'));
expect(inputLib.children, contains('C'));
+ expect(inputLib.children, contains('D'));
final topLevel = inputLib.children[''];
expect(topLevel.children, contains('makeSomeClosures'));
@@ -336,6 +362,16 @@
expect(inputLib.children['C'].children, contains(name));
expect(inputLib.children['C'].children[name].children, isEmpty);
}
+
+ for (var name in [
+ 'tornOff{body}',
+ 'tornOff{body depth 2}',
+ 'tornOff',
+ '[tear-off] tornOff'
+ ]) {
+ expect(inputLib.children['D'].children, contains(name));
+ expect(inputLib.children['D'].children[name].children, isEmpty);
+ }
});
});
@@ -356,6 +392,10 @@
bySymbol.buckets,
contains(bySymbol.bucketFor(
'package:input', 'package:input/input.dart', 'C', 'tornOff')));
+ expect(
+ bySymbol.buckets,
+ contains(bySymbol.bucketFor(
+ 'package:input', 'package:input/input.dart', 'D', 'tornOff')));
final byClass = computeHistogram(info, HistogramType.byClass);
expect(
@@ -370,6 +410,10 @@
byClass.buckets,
contains(byClass.bucketFor('package:input',
'package:input/input.dart', 'C', 'does-not-matter')));
+ expect(
+ byClass.buckets,
+ contains(byClass.bucketFor('package:input',
+ 'package:input/input.dart', 'D', 'does-not-matter')));
final byLibrary = computeHistogram(info, HistogramType.byLibrary);
expect(
@@ -538,6 +582,21 @@
.where((n) => n.type == 'Class')
.map((n) => n.name);
expect(classesOwnedByC, equals(['C']));
+
+ final classD = inputLib.children['D'];
+ expect(classD.children, contains('tornOff'));
+ for (var name in ['tornOff{body}', '[tear-off] tornOff']) {
+ expect(classD.children['tornOff'].children, contains(name));
+ }
+ expect(classD.children['tornOff'].children['tornOff{body}'].children,
+ contains('tornOff{body depth 2}'));
+
+ // Verify that [ProgramInfoNode] owns its corresponding snapshot [Node].
+ final classesOwnedByD = info.snapshotInfo.snapshot.nodes
+ .where((n) => info.snapshotInfo.ownerOf(n) == classD)
+ .where((n) => n.type == 'Class')
+ .map((n) => n.name);
+ expect(classesOwnedByD, equals(['D']));
});
});
@@ -592,6 +651,10 @@
bySymbol.buckets,
contains(bySymbol.bucketFor(
'package:input', 'package:input/input.dart', 'C', 'tornOff')));
+ expect(
+ bySymbol.buckets,
+ contains(bySymbol.bucketFor(
+ 'package:input', 'package:input/input.dart', 'D', 'tornOff')));
final byClass = computeHistogram(info, HistogramType.byClass);
expect(
@@ -606,6 +669,10 @@
byClass.buckets,
contains(byClass.bucketFor('package:input',
'package:input/input.dart', 'C', 'does-not-matter')));
+ expect(
+ byClass.buckets,
+ contains(byClass.bucketFor('package:input',
+ 'package:input/input.dart', 'D', 'does-not-matter')));
final byLibrary = computeHistogram(info, HistogramType.byLibrary);
expect(
diff --git a/runtime/vm/clustered_snapshot.cc b/runtime/vm/clustered_snapshot.cc
index 81908d7..af19ffa 100644
--- a/runtime/vm/clustered_snapshot.cc
+++ b/runtime/vm/clustered_snapshot.cc
@@ -911,11 +911,11 @@
AutoTraceObjectName(func, MakeDisambiguatedFunctionName(s, func));
WriteFromTo(func);
if (kind == Snapshot::kFullAOT) {
- WriteField(func, code());
+ WriteCompressedField(func, code);
} else if (s->kind() == Snapshot::kFullJIT) {
- NOT_IN_PRECOMPILED(WriteField(func, unoptimized_code()));
- WriteField(func, code());
- WriteField(func, ic_data_array());
+ NOT_IN_PRECOMPILED(WriteCompressedField(func, unoptimized_code));
+ WriteCompressedField(func, code);
+ WriteCompressedField(func, ic_data_array);
}
if (kind != Snapshot::kFullAOT) {
@@ -1087,11 +1087,11 @@
ClosureDataPtr data = objects_[i];
AutoTraceObject(data);
if (s->kind() != Snapshot::kFullAOT) {
- WriteField(data, context_scope());
+ WriteCompressedField(data, context_scope);
}
- WriteField(data, parent_function());
- WriteField(data, closure());
- WriteField(data, default_type_arguments());
+ WriteCompressedField(data, parent_function);
+ WriteCompressedField(data, closure);
+ WriteCompressedField(data, default_type_arguments);
s->WriteUnsigned(
static_cast<intptr_t>(data->untag()->default_type_arguments_kind_));
}
@@ -1268,16 +1268,16 @@
FieldPtr field = objects_[i];
AutoTraceObjectName(field, field->untag()->name());
- WriteField(field, name());
- WriteField(field, owner());
- WriteField(field, type());
+ WriteCompressedField(field, name);
+ WriteCompressedField(field, owner);
+ WriteCompressedField(field, type);
// Write out the initializer function and initial value if not in AOT.
- WriteField(field, initializer_function());
+ WriteCompressedField(field, initializer_function);
if (kind != Snapshot::kFullAOT) {
- WriteField(field, guarded_list_length());
+ WriteCompressedField(field, guarded_list_length);
}
if (kind == Snapshot::kFullJIT) {
- WriteField(field, dependent_code());
+ WriteCompressedField(field, dependent_code);
}
if (kind != Snapshot::kFullAOT) {
@@ -2814,7 +2814,7 @@
AutoTraceObject(handlers);
const intptr_t length = handlers->untag()->num_entries_;
s->WriteUnsigned(length);
- WriteField(handlers, handled_types_data());
+ WriteCompressedField(handlers, handled_types_data);
for (intptr_t j = 0; j < length; j++) {
const ExceptionHandlerInfo& info = handlers->untag()->data()[j];
s->Write<uint32_t>(info.handler_pc_offset);
@@ -3362,7 +3362,7 @@
for (intptr_t i = 0; i < count; i++) {
LoadingUnitPtr unit = objects_[i];
AutoTraceObject(unit);
- WriteField(unit, parent());
+ WriteCompressedField(unit, parent);
s->Write<int32_t>(unit->untag()->id_);
}
}
diff --git a/runtime/vm/clustered_snapshot.h b/runtime/vm/clustered_snapshot.h
index 403b206..0888bbb 100644
--- a/runtime/vm/clustered_snapshot.h
+++ b/runtime/vm/clustered_snapshot.h
@@ -502,6 +502,8 @@
#define PushFromTo(obj, ...) s->PushFromTo(obj, ##__VA_ARGS__);
#define WriteField(obj, field) s->WritePropertyRef(obj->untag()->field, #field)
+#define WriteCompressedField(obj, name) \
+ s->WritePropertyRef(obj->untag()->name(), #name "_")
class SerializerWritingObjectScope {
public:
diff --git a/runtime/vm/compiler/assembler/disassembler.cc b/runtime/vm/compiler/assembler/disassembler.cc
index 9e19290..e950867 100644
--- a/runtime/vm/compiler/assembler/disassembler.cc
+++ b/runtime/vm/compiler/assembler/disassembler.cc
@@ -463,8 +463,7 @@
TextBuffer buffer(128);
const char* function_fullname = function.ToFullyQualifiedCString();
buffer.Printf("%s", Function::KindToCString(function.kind()));
- if (function.IsInvokeFieldDispatcher() ||
- function.IsNoSuchMethodDispatcher()) {
+ if (function.HasSavedArgumentsDescriptor()) {
const auto& args_desc_array = Array::Handle(function.saved_args_desc());
const ArgumentsDescriptor args_desc(args_desc_array);
buffer.AddString(", ");
diff --git a/runtime/vm/compiler/backend/il_printer.cc b/runtime/vm/compiler/backend/il_printer.cc
index 981a536..b42d9de 100644
--- a/runtime/vm/compiler/backend/il_printer.cc
+++ b/runtime/vm/compiler/backend/il_printer.cc
@@ -55,8 +55,7 @@
Function::KindToCString(function_.kind()));
// Output saved arguments descriptor information for dispatchers that
// have it, so it's easy to see which dispatcher this graph represents.
- if (function_.IsInvokeFieldDispatcher() ||
- function_.IsNoSuchMethodDispatcher()) {
+ if (function_.HasSavedArgumentsDescriptor()) {
const auto& args_desc_array = Array::Handle(function_.saved_args_desc());
const ArgumentsDescriptor args_desc(args_desc_array);
THR_Print(", %s", args_desc.ToCString());
diff --git a/runtime/vm/compiler/frontend/base_flow_graph_builder.h b/runtime/vm/compiler/frontend/base_flow_graph_builder.h
index e55d218..917e820 100644
--- a/runtime/vm/compiler/frontend/base_flow_graph_builder.h
+++ b/runtime/vm/compiler/frontend/base_flow_graph_builder.h
@@ -434,8 +434,7 @@
// Returns whether this function has a saved arguments descriptor array.
bool has_saved_args_desc_array() {
- return function_.IsInvokeFieldDispatcher() ||
- function_.IsNoSuchMethodDispatcher();
+ return function_.HasSavedArgumentsDescriptor();
}
// Returns the saved arguments descriptor array for functions that have them.
diff --git a/runtime/vm/compiler/frontend/scope_builder.cc b/runtime/vm/compiler/frontend/scope_builder.cc
index 14d1b20..f55386b 100644
--- a/runtime/vm/compiler/frontend/scope_builder.cc
+++ b/runtime/vm/compiler/frontend/scope_builder.cc
@@ -81,7 +81,7 @@
enclosing_scope->set_context_level(0);
enclosing_scope->AddVariable(receiver_variable);
enclosing_scope->AddContextVariable(receiver_variable);
- } else if (function.IsLocalFunction()) {
+ } else if (function.HasParent()) {
enclosing_scope = LocalScope::RestoreOuterScope(
ContextScope::Handle(Z, function.context_scope()));
}
diff --git a/runtime/vm/dart_entry.cc b/runtime/vm/dart_entry.cc
index ec04ca2..058d039 100644
--- a/runtime/vm/dart_entry.cc
+++ b/runtime/vm/dart_entry.cc
@@ -412,24 +412,28 @@
return names.ptr();
}
-void ArgumentsDescriptor::PrintTo(BaseTextBuffer* buffer) const {
- buffer->Printf("%" Pd " arg%s", Count(), Count() == 1 ? "" : "s");
+void ArgumentsDescriptor::PrintTo(BaseTextBuffer* buffer,
+ bool show_named_positions) const {
if (TypeArgsLen() > 0) {
- buffer->Printf(", %" Pd " type arg%s", TypeArgsLen(),
- TypeArgsLen() == 1 ? "" : "s");
+ buffer->Printf("<%" Pd ">", TypeArgsLen());
}
+ buffer->Printf("(%" Pd "", Count());
if (NamedCount() > 0) {
- buffer->AddString(", names [");
+ buffer->AddString(" {");
auto& str = String::Handle();
for (intptr_t i = 0; i < NamedCount(); i++) {
if (i != 0) {
buffer->AddString(", ");
}
str = NameAt(i);
- buffer->Printf("'%s' (%" Pd ")", str.ToCString(), PositionAt(i));
+ buffer->Printf("%s", str.ToCString());
+ if (show_named_positions) {
+ buffer->Printf(" (%" Pd ")", PositionAt(i));
+ }
}
- buffer->Printf("]");
+ buffer->Printf("}");
}
+ buffer->Printf(")");
}
const char* ArgumentsDescriptor::ToCString() const {
diff --git a/runtime/vm/dart_entry.h b/runtime/vm/dart_entry.h
index 4b5fd32..2a33464 100644
--- a/runtime/vm/dart_entry.h
+++ b/runtime/vm/dart_entry.h
@@ -46,7 +46,7 @@
bool MatchesNameAt(intptr_t i, const String& other) const;
// Returns array of argument names in the arguments order.
ArrayPtr GetArgumentNames() const;
- void PrintTo(BaseTextBuffer* buffer) const;
+ void PrintTo(BaseTextBuffer* buffer, bool show_named_positions = false) const;
const char* ToCString() const;
// Generated code support.
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index a0af589..8d31193 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -7382,11 +7382,11 @@
}
bool Function::IsInFactoryScope() const {
- if (!IsLocalFunction()) {
+ if (!HasParent()) {
return IsFactory();
}
Function& outer_function = Function::Handle(parent_function());
- while (outer_function.IsLocalFunction()) {
+ while (outer_function.HasParent()) {
outer_function = outer_function.parent_function();
}
return outer_function.IsFactory();
@@ -9408,16 +9408,31 @@
return printer.buffer();
}
-void Function::PrintName(const NameFormattingParams& params,
- BaseTextBuffer* printer) const {
- // If |this| is the generated asynchronous body closure, use the
- // name of the parent function.
- Function& fun = Function::Handle(ptr());
-
+static void FunctionPrintNameHelper(const Function& fun,
+ const NameFormattingParams& params,
+ BaseTextBuffer* printer) {
+ if (fun.IsLocalFunction()) {
+ if (params.include_parent_name) {
+ const auto& parent = Function::Handle(fun.parent_function());
+ parent.PrintName(params, printer);
+ // A function's scrubbed name and its user visible name are identical.
+ printer->AddString(".");
+ }
+ if (params.disambiguate_names &&
+ fun.name() == Symbols::AnonymousClosure().ptr()) {
+ printer->Printf("<anonymous closure @%" Pd ">", fun.token_pos().Pos());
+ } else {
+ printer->AddString(fun.NameCString(params.name_visibility));
+ }
+ return;
+ }
if (params.disambiguate_names) {
if (fun.IsInvokeFieldDispatcher()) {
printer->AddString("[invoke-field] ");
}
+ if (fun.IsNoSuchMethodDispatcher()) {
+ printer->AddString("[no-such-method] ");
+ }
if (fun.IsImplicitClosureFunction()) {
printer->AddString("[tear-off] ");
}
@@ -9426,53 +9441,13 @@
}
}
- if (fun.IsNonImplicitClosureFunction()) {
- // Sniff the parent function.
- fun = fun.parent_function();
- ASSERT(!fun.IsNull());
- if (!fun.IsAsyncGenerator() && !fun.IsAsyncFunction() &&
- !fun.IsSyncGenerator()) {
- // Parent function is not the generator of an asynchronous body closure,
- // start at |this|.
- fun = ptr();
- }
- }
- if (IsClosureFunction()) {
- if (fun.IsLocalFunction() && !fun.IsImplicitClosureFunction()) {
- Function& parent = Function::Handle(fun.parent_function());
- if (parent.IsAsyncClosure() || parent.IsSyncGenClosureMaker() ||
- parent.IsAsyncGenClosure()) {
- // Skip the closure and use the real function name found in
- // the parent.
- parent = parent.parent_function();
- }
- if (params.include_parent_name) {
- parent.PrintName(params, printer);
- // A function's scrubbed name and its user visible name are identical.
- printer->AddString(".");
- }
- if (params.disambiguate_names &&
- fun.name() == Symbols::AnonymousClosure().ptr()) {
- printer->Printf("<anonymous closure @%" Pd ">", fun.token_pos().Pos());
- } else {
- printer->AddString(fun.NameCString(params.name_visibility));
- }
- // If we skipped rewritten async/async*/sync* body then append a suffix
- // to the end of the name.
- if (fun.ptr() != ptr() && params.disambiguate_names) {
- printer->AddString("{body}");
- }
- return;
- }
- }
-
if (fun.kind() == UntaggedFunction::kConstructor) {
printer->AddString("new ");
} else if (params.include_class_name) {
- const Class& cls = Class::Handle(Owner());
+ const Class& cls = Class::Handle(fun.Owner());
if (!cls.IsTopLevel()) {
const Class& mixin = Class::Handle(cls.Mixin());
- printer->AddString(params.name_visibility == kUserVisibleName
+ printer->AddString(params.name_visibility == Object::kUserVisibleName
? mixin.UserVisibleNameCString()
: cls.NameCString(params.name_visibility));
printer->AddString(".");
@@ -9481,35 +9456,37 @@
printer->AddString(fun.NameCString(params.name_visibility));
- // If we skipped rewritten async/async*/sync* body then append a suffix
- // to the end of the name.
- if (fun.ptr() != ptr() && params.disambiguate_names) {
- printer->AddString("{body}");
+ // Dispatchers that are created with an arguments descriptor need both the
+ // name and the saved arguments descriptor to disambiguate.
+ if (params.disambiguate_names && fun.HasSavedArgumentsDescriptor()) {
+ const auto& args_desc_array = Array::Handle(fun.saved_args_desc());
+ const ArgumentsDescriptor args_desc(args_desc_array);
+ args_desc.PrintTo(printer);
}
+}
- // Field dispatchers are specialized for an argument descriptor so there
- // might be multiples of them with the same name but different argument
- // descriptors. Add a suffix to disambiguate.
- if (params.disambiguate_names && fun.IsInvokeFieldDispatcher()) {
- printer->AddString(" ");
- if (NumTypeParameters() != 0) {
- printer->Printf("<%" Pd ">", fun.NumTypeParameters());
+void Function::PrintName(const NameFormattingParams& params,
+ BaseTextBuffer* printer) const {
+ if (!IsLocalFunction()) {
+ FunctionPrintNameHelper(*this, params, printer);
+ return;
+ }
+ auto& fun = Function::Handle(ptr());
+ intptr_t fun_depth = 0;
+ // If |this| is a generated body closure, start with the closest
+ // non-generated parent function.
+ while (fun.is_generated_body()) {
+ fun = fun.parent_function();
+ fun_depth++;
+ }
+ FunctionPrintNameHelper(fun, params, printer);
+ // If we skipped generated bodies then append a suffix to the end.
+ if (fun_depth > 0 && params.disambiguate_names) {
+ printer->AddString("{body");
+ if (fun_depth > 1) {
+ printer->Printf(" depth %" Pd "", fun_depth);
}
- printer->AddString("(");
- printer->Printf("%" Pd "", fun.num_fixed_parameters());
- if (fun.NumOptionalPositionalParameters() != 0) {
- printer->Printf(" [%" Pd "]", fun.NumOptionalPositionalParameters());
- }
- if (fun.HasOptionalNamedParameters()) {
- printer->AddString(" {");
- String& name = String::Handle();
- for (intptr_t i = 0; i < fun.NumOptionalNamedParameters(); i++) {
- name = fun.ParameterNameAt(fun.num_fixed_parameters() + i);
- printer->Printf("%s%s", i > 0 ? ", " : "", name.ToCString());
- }
- printer->AddString("}");
- }
- printer->AddString(")");
+ printer->AddString("}");
}
}
@@ -9800,7 +9777,7 @@
bool Function::PrologueNeedsArgumentsDescriptor() const {
// These functions have a saved compile-time arguments descriptor that is
// used in lieu of the runtime arguments descriptor in generated IL.
- if (IsInvokeFieldDispatcher() || IsNoSuchMethodDispatcher()) {
+ if (HasSavedArgumentsDescriptor()) {
return false;
}
// The prologue of those functions need to examine the arg descriptor for
@@ -9890,7 +9867,7 @@
default:
UNREACHABLE();
}
- if (IsNoSuchMethodDispatcher() || IsInvokeFieldDispatcher()) {
+ if (HasSavedArgumentsDescriptor()) {
const auto& args_desc_array = Array::Handle(zone, saved_args_desc());
const ArgumentsDescriptor args_desc(args_desc_array);
buffer.AddChar('[');
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 0708f3d..0512afe 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -2724,6 +2724,10 @@
void set_saved_args_desc(const Array& array) const;
ArrayPtr saved_args_desc() const;
+ bool HasSavedArgumentsDescriptor() const {
+ return IsInvokeFieldDispatcher() || IsNoSuchMethodDispatcher();
+ }
+
void set_accessor_field(const Field& value) const;
FieldPtr accessor_field() const;
@@ -3328,8 +3332,13 @@
return IsImplicitClosureFunction() && !is_static();
}
- // Returns true if this function represents a local function.
- bool IsLocalFunction() const { return parent_function() != Function::null(); }
+ // Returns true if this function has a parent function.
+ bool HasParent() const { return parent_function() != Function::null(); }
+
+ // Returns true if this function is a local function.
+ bool IsLocalFunction() const {
+ return !IsImplicitClosureFunction() && HasParent();
+ }
// Returns true if this function represents an ffi trampoline.
bool IsFfiTrampoline() const {
@@ -3438,7 +3447,7 @@
// }
// }
bool IsSyncGenClosure() const {
- return (parent_function() != Function::null()) &&
+ return is_generated_body() &&
Function::Handle(parent_function()).IsSyncGenClosureMaker();
}
diff --git a/runtime/vm/runtime_entry.cc b/runtime/vm/runtime_entry.cc
index e3b428e..604900f 100644
--- a/runtime/vm/runtime_entry.cc
+++ b/runtime/vm/runtime_entry.cc
@@ -616,8 +616,7 @@
}
const Function& function =
Function::Handle(caller_frame->LookupDartFunction());
- if (function.IsInvokeFieldDispatcher() ||
- function.IsNoSuchMethodDispatcher()) {
+ if (function.HasSavedArgumentsDescriptor()) {
const auto& args_desc_array = Array::Handle(function.saved_args_desc());
const ArgumentsDescriptor args_desc(args_desc_array);
OS::PrintErr(" -> Function %s [%s]\n", function.ToFullyQualifiedCString(),
diff --git a/runtime/vm/scopes.cc b/runtime/vm/scopes.cc
index 3b08708..c643b46 100644
--- a/runtime/vm/scopes.cc
+++ b/runtime/vm/scopes.cc
@@ -362,7 +362,7 @@
const ContextScope& context_scope =
ContextScope::Handle(func.context_scope());
if (!context_scope.IsNull()) {
- ASSERT(func.IsLocalFunction());
+ ASSERT(func.HasParent());
for (int i = 0; i < context_scope.num_variables(); i++) {
String& name = String::Handle(context_scope.NameAt(i));
UntaggedLocalVarDescriptors::VarInfoKind kind;
diff --git a/runtime/vm/stack_trace.cc b/runtime/vm/stack_trace.cc
index f200269..74fa21a 100644
--- a/runtime/vm/stack_trace.cc
+++ b/runtime/vm/stack_trace.cc
@@ -238,7 +238,7 @@
return GetCallerInFutureImpl(future_);
}
- if (receiver_function_.IsLocalFunction()) {
+ if (receiver_function_.HasParent()) {
parent_function_ = receiver_function_.parent_function();
if (parent_function_.recognized_kind() ==
MethodRecognizer::kFutureTimeout) {