[kernel] Implement correct semantics of forwarding stubs in VM
This revision has been factored out of Revision 29300.
Change-Id: Idf579f0e5ffeea5764e2a9654c6812d69f6977c7
Reviewed-on: https://dart-review.googlesource.com/31921
Commit-Queue: Samir Jindel <sjindel@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Reviewed-by: Paul Berry <paulberry@google.com>
diff --git a/pkg/front_end/lib/src/fasta/type_inference/interface_resolver.dart b/pkg/front_end/lib/src/fasta/type_inference/interface_resolver.dart
index 25f7ae3..5cdc260 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/interface_resolver.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/interface_resolver.dart
@@ -411,6 +411,7 @@
}
function.body = new ReturnStatement(superCall)..parent = function;
procedure.transformerFlags |= TransformerFlag.superCalls;
+ procedure.forwardingStubSuperTarget = superTarget.reference;
}
/// Creates a forwarding stub based on the given [target].
diff --git a/pkg/kernel/binary.md b/pkg/kernel/binary.md
index 848fa2f..f307f05 100644
--- a/pkg/kernel/binary.md
+++ b/pkg/kernel/binary.md
@@ -354,6 +354,8 @@
// An absolute path URI to the .dart file from which the class was created.
UriReference fileUri;
List<Expression> annotations;
+ // Only present if the 'isForwardingStub' flag is set.
+ Option<MemberReference> forwardingStubSuperTarget;
// Can only be absent if abstract, but tag is there anyway.
Option<FunctionNode> function;
}
@@ -367,8 +369,8 @@
Name name;
UriReference fileUri;
List<Expression> annotations;
- List<DartType> typeArguments;
MemberReference targetReference;
+ List<DartType> typeArguments;
List<TypeParameter> typeParameters;
UInt parameterCount; // positionalParameters.length + namedParameters.length.
UInt requiredParameterCount;
diff --git a/pkg/kernel/lib/ast.dart b/pkg/kernel/lib/ast.dart
index e655f93..89a1643 100644
--- a/pkg/kernel/lib/ast.dart
+++ b/pkg/kernel/lib/ast.dart
@@ -1490,6 +1490,8 @@
/// The uri of the source file this procedure was loaded from.
Uri fileUri;
+ Reference forwardingStubSuperTarget;
+
Procedure(Name name, this.kind, this.function,
{bool isAbstract: false,
bool isStatic: false,
@@ -1499,7 +1501,8 @@
bool isForwardingSemiStub: false,
int transformerFlags: 0,
this.fileUri,
- Reference reference})
+ Reference reference,
+ this.forwardingStubSuperTarget})
: super(name, reference) {
function?.parent = this;
this.isAbstract = isAbstract;
diff --git a/pkg/kernel/lib/binary/ast_from_binary.dart b/pkg/kernel/lib/binary/ast_from_binary.dart
index d88793b..83c5af1 100644
--- a/pkg/kernel/lib/binary/ast_from_binary.dart
+++ b/pkg/kernel/lib/binary/ast_from_binary.dart
@@ -930,6 +930,8 @@
bool readFunctionNodeNow =
(kind == ProcedureKind.Factory && functionNodeSize <= 50) ||
_disableLazyReading;
+ var forwardingStubSuperTarget =
+ readAndCheckOptionTag() ? readMemberReference() : null;
var function = readFunctionNodeOption(!readFunctionNodeNow, endOffset);
var transformerFlags = getAndResetTransformerFlags();
assert(((_) => true)(debugPath.removeLast()));
@@ -944,6 +946,10 @@
node.function = function;
function?.parent = node;
node.setTransformerFlagsWithoutLazyLoading(transformerFlags);
+ node.forwardingStubSuperTarget = forwardingStubSuperTarget;
+
+ assert((node.forwardingStubSuperTarget != null) ||
+ !(node.isForwardingStub && node.function.body != null));
}
_byteOffset = endOffset;
return node;
diff --git a/pkg/kernel/lib/binary/ast_to_binary.dart b/pkg/kernel/lib/binary/ast_to_binary.dart
index 38fac13..3d12707 100644
--- a/pkg/kernel/lib/binary/ast_to_binary.dart
+++ b/pkg/kernel/lib/binary/ast_to_binary.dart
@@ -229,6 +229,15 @@
}
}
+ void writeOptionalReference(Reference ref) {
+ if (ref == null) {
+ writeByte(Tag.Nothing);
+ } else {
+ writeByte(Tag.Something);
+ writeReference(ref);
+ }
+ }
+
void writeLinkTable(Program program) {
_binaryOffsetForLinkTable = getBufferOffset();
writeList(_canonicalNameList, writeCanonicalNameEntry);
@@ -756,8 +765,12 @@
writeName(node.name ?? '');
writeUriReference(node.fileUri);
writeAnnotationList(node.annotations);
+ writeOptionalReference(node.forwardingStubSuperTarget);
writeOptionalNode(node.function);
_variableIndexer = null;
+
+ assert((node.forwardingStubSuperTarget != null) ||
+ !(node.isForwardingStub && node.function.body != null));
}
visitField(Field node) {
diff --git a/pkg/kernel/lib/clone.dart b/pkg/kernel/lib/clone.dart
index 621c4f1..e461650 100644
--- a/pkg/kernel/lib/clone.dart
+++ b/pkg/kernel/lib/clone.dart
@@ -403,7 +403,8 @@
isForwardingStub: node.isForwardingStub,
isForwardingSemiStub: node.isForwardingSemiStub,
transformerFlags: node.transformerFlags,
- fileUri: node.fileUri)
+ fileUri: node.fileUri,
+ forwardingStubSuperTarget: node.forwardingStubSuperTarget)
..fileEndOffset = node.fileEndOffset
..isGenericContravariant = node.isGenericContravariant;
}
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
index f194023..13adddf 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
@@ -226,6 +226,11 @@
}
if (++next_read_ == field) return;
}
+ case kForwardingStubSuperTarget:
+ if (builder_->ReadTag() == kSomething) {
+ forwarding_stub_super_target_ = builder_->ReadCanonicalNameReference();
+ }
+ if (++next_read_ == field) return;
case kFunction:
if (builder_->ReadTag() == kSomething)
builder_->SkipFunctionNode(); // read function node.
@@ -3437,7 +3442,7 @@
}
}
-void StreamingFlowGraphBuilder::ReadUntilFunctionNode() {
+void StreamingFlowGraphBuilder::ReadUntilFunctionNode(ParsedFunction* set_forwarding_stub) {
const Tag tag = PeekTag();
if (tag == kProcedure) {
ProcedureHelper procedure_helper(this);
@@ -3446,7 +3451,12 @@
// Running a procedure without a function node doesn't make sense.
UNREACHABLE();
}
- return;
+ if (set_forwarding_stub != NULL && flow_graph_builder_ && procedure_helper.IsForwardingStub() &&
+ !procedure_helper.IsAbstract()) {
+ ASSERT(procedure_helper.forwarding_stub_super_target_ != -1);
+ set_forwarding_stub->MarkForwardingStub(
+ procedure_helper.forwarding_stub_super_target_);
+ }
// Now at start of FunctionNode.
} else if (tag == kConstructor) {
ConstructorHelper constructor_helper(this);
@@ -3460,7 +3470,6 @@
H.ReportError("Unsupported tag at this point: %d.", tag);
UNREACHABLE();
}
- return;
}
StringIndex StreamingFlowGraphBuilder::GetNameFromVariableDeclaration(
@@ -4095,13 +4104,35 @@
AlternativeReadingScope _(reader_);
SetOffset(type_parameters_offset);
+ const Function* forwarding_target = NULL;
+ if (parsed_function()->is_forwarding_stub()) {
+ NameIndex target_name = parsed_function()->forwarding_stub_super_target();
+ const String& name = dart_function.IsSetterFunction()
+ ? H.DartSetterName(target_name)
+ : H.DartProcedureName(target_name);
+ forwarding_target =
+ &Function::ZoneHandle(Z, LookupMethodByMember(target_name, name));
+ ASSERT(!forwarding_target->IsNull());
+ }
+
// Type parameters
intptr_t list_length = ReadListLength();
+ TypeArguments& forwarding_params = TypeArguments::Handle(Z);
+ if (forwarding_target != NULL) {
+ forwarding_params = forwarding_target->type_parameters();
+ ASSERT(forwarding_params.Length() == list_length);
+ }
+ TypeParameter& forwarding_param = TypeParameter::Handle(Z);
for (intptr_t i = 0; i < list_length; ++i) {
ReadFlags(); // skip flags
SkipListOfExpressions(); // skip annotations
String& name = H.DartSymbol(ReadStringReference()); // read name
- const AbstractType& bound = T.BuildType(); // read bound
+ AbstractType& bound = T.BuildType(); // read bound
+
+ if (forwarding_target != NULL) {
+ forwarding_param ^= forwarding_params.TypeAt(i);
+ bound = forwarding_param.bound();
+ }
if (I->strong() && !bound.IsObjectType() &&
(I->reify_generic_functions() || dart_function.IsFactory())) {
@@ -4126,10 +4157,20 @@
// Positional.
list_length = ReadListLength();
+ const intptr_t positional_length = list_length;
+ const intptr_t kFirstParameterOffset = 1;
for (intptr_t i = 0; i < list_length; ++i) {
// ith variable offset.
- body += LoadLocal(LookupVariable(ReaderOffset() + data_program_offset_));
- body += CheckArgumentType(ReaderOffset() + data_program_offset_);
+ const intptr_t offset = ReaderOffset();
+ LocalVariable* param = LookupVariable(offset + data_program_offset_);
+ const AbstractType* target_type = ¶m->type();
+ if (forwarding_target != NULL) {
+ // We add 1 to the parameter index to account for the receiver.
+ target_type = &AbstractType::ZoneHandle(Z,
+ forwarding_target->ParameterTypeAt(kFirstParameterOffset + i));
+ }
+ body += LoadLocal(param);
+ body += CheckArgumentType(param, *target_type);
body += Drop();
SkipVariableDeclaration(); // read ith variable.
}
@@ -4138,8 +4179,16 @@
list_length = ReadListLength();
for (intptr_t i = 0; i < list_length; ++i) {
// ith variable offset.
- body += LoadLocal(LookupVariable(ReaderOffset() + data_program_offset_));
- body += CheckArgumentType(ReaderOffset() + data_program_offset_);
+ LocalVariable* param =
+ LookupVariable(ReaderOffset() + data_program_offset_);
+ body += LoadLocal(param);
+ const AbstractType* target_type = ¶m->type();
+ if (forwarding_target != NULL) {
+ // We add 1 to the parameter index to account for the receiver.
+ target_type = &AbstractType::ZoneHandle(Z,
+ forwarding_target->ParameterTypeAt(positional_length + i + 1));
+ }
+ body += CheckArgumentType(param, *target_type);
body += Drop();
SkipVariableDeclaration(); // read ith variable.
}
@@ -4343,14 +4392,14 @@
case RawFunction::kRegularFunction:
case RawFunction::kGetterFunction:
case RawFunction::kSetterFunction: {
- ReadUntilFunctionNode(); // read until function node.
+ ReadUntilFunctionNode(parsed_function()); // read until function node.
if (function.IsImplicitClosureFunction()) {
return BuildGraphOfImplicitClosureFunction(function);
}
return BuildGraphOfFunction(false);
}
case RawFunction::kConstructor: {
- ReadUntilFunctionNode(); // read until function node.
+ ReadUntilFunctionNode(parsed_function()); // read until function node.
return BuildGraphOfFunction(!function.IsFactory());
}
case RawFunction::kImplicitGetter:
@@ -5752,10 +5801,9 @@
}
Fragment StreamingFlowGraphBuilder::CheckArgumentType(
- intptr_t variable_kernel_position) {
- LocalVariable* variable = LookupVariable(variable_kernel_position);
- return flow_graph_builder_->CheckAssignable(variable->type(),
- variable->name());
+ LocalVariable* variable,
+ const AbstractType& type) {
+ return flow_graph_builder_->CheckAssignable(type, variable->name());
}
Fragment StreamingFlowGraphBuilder::CheckTypeArgumentBound(
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h
index c3da1c1..96d73be 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h
@@ -245,6 +245,7 @@
kName,
kSourceUriIndex,
kAnnotations,
+ kForwardingStubSuperTarget,
kFunction,
kEnd,
};
@@ -262,6 +263,7 @@
kAbstract = 1 << 1,
kExternal = 1 << 2,
kConst = 1 << 3, // Only for external const factories.
+ kForwardingStub = 1 << 4,
};
explicit ProcedureHelper(StreamingFlowGraphBuilder* builder) {
@@ -282,6 +284,7 @@
bool IsAbstract() { return (flags_ & kAbstract) != 0; }
bool IsExternal() { return (flags_ & kExternal) != 0; }
bool IsConst() { return (flags_ & kConst) != 0; }
+ bool IsForwardingStub() { return (flags_ & kForwardingStub) != 0; }
NameIndex canonical_name_;
TokenPosition position_;
@@ -291,6 +294,9 @@
intptr_t source_uri_index_;
intptr_t annotation_count_;
+ // Only valid if the 'isForwardingStub' flag is set.
+ NameIndex forwarding_stub_super_target_;
+
private:
StreamingFlowGraphBuilder* builder_;
intptr_t next_read_;
@@ -927,7 +933,11 @@
String& GetSourceFor(intptr_t index);
RawTypedData* GetLineStartsFor(intptr_t index);
void SetOffset(intptr_t offset);
- void ReadUntilFunctionNode();
+
+ // If a 'ParsedFunction' is provided for 'set_forwarding_stub', this method
+ // will attach the forwarding stub target reference to the parsed function if
+ // it crosses a procedure node for a concrete forwarding stub.
+ void ReadUntilFunctionNode(ParsedFunction* set_forwarding_stub = NULL);
intptr_t ReadListLength();
enum DispatchCategory { Interface, ViaThis, Closure, DynamicDispatch };
@@ -954,6 +964,7 @@
Fragment BuildInitializers(const Class& parent_class);
FlowGraph* BuildGraphOfImplicitClosureFunction(const Function& function);
FlowGraph* BuildGraphOfFunction(bool constructor);
+ FlowGraph* BuildGraphOfForwardingStub(const Function& function);
intptr_t GetOffsetForSourceInfo(intptr_t index);
@@ -1142,7 +1153,7 @@
Fragment CheckBooleanInCheckedMode();
Fragment CheckAssignableInCheckedMode(const AbstractType& dst_type,
const String& dst_name);
- Fragment CheckArgumentType(intptr_t variable_kernel_position);
+ Fragment CheckArgumentType(LocalVariable* variable, const AbstractType& type);
Fragment CheckTypeArgumentBound(const AbstractType& parameter,
const AbstractType& bound,
const String& dst_name);
diff --git a/runtime/vm/kernel.h b/runtime/vm/kernel.h
index 1452e5c..30fe483 100644
--- a/runtime/vm/kernel.h
+++ b/runtime/vm/kernel.h
@@ -5,7 +5,6 @@
#ifndef RUNTIME_VM_KERNEL_H_
#define RUNTIME_VM_KERNEL_H_
-#if !defined(DART_PRECOMPILED_RUNTIME)
#include "platform/assert.h"
#include "vm/allocation.h"
#include "vm/globals.h"
@@ -14,6 +13,22 @@
#include "vm/token_position.h"
namespace dart {
+namespace kernel {
+class NameIndex {
+ public:
+ NameIndex() : value_(-1) {}
+ explicit NameIndex(int value) : value_(value) {}
+
+ operator int() const { return value_; }
+
+ private:
+ int value_;
+};
+} // kernel
+} // dart
+
+#if !defined(DART_PRECOMPILED_RUNTIME)
+namespace dart {
class Field;
class ParsedFunction;
@@ -34,17 +49,6 @@
int value_;
};
-class NameIndex {
- public:
- NameIndex() : value_(-1) {}
- explicit NameIndex(int value) : value_(value) {}
-
- operator int() const { return value_; }
-
- private:
- int value_;
-};
-
const uint8_t kNativeYieldFlags = 0x2;
enum LogicalOperator { kAnd, kOr };
diff --git a/runtime/vm/parser.h b/runtime/vm/parser.h
index ad9283e..12d0557 100644
--- a/runtime/vm/parser.h
+++ b/runtime/vm/parser.h
@@ -190,6 +190,15 @@
void record_await() { have_seen_await_expr_ = true; }
bool have_seen_await() const { return have_seen_await_expr_; }
+ bool is_forwarding_stub() const {
+ return forwarding_stub_super_target_ != -1;
+ }
+ kernel::NameIndex forwarding_stub_super_target() const {
+ return forwarding_stub_super_target_;
+ }
+ void MarkForwardingStub(kernel::NameIndex target) {
+ forwarding_stub_super_target_ = target;
+ }
Thread* thread() const { return thread_; }
Isolate* isolate() const { return thread_->isolate(); }
@@ -235,6 +244,7 @@
int num_stack_locals_;
bool have_seen_await_expr_;
+ kernel::NameIndex forwarding_stub_super_target_;
kernel::ScopeBuildingResult* kernel_scopes_;
friend class Parser;