[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 = &param->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 = &param->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;