Switch from alias to forwarding methods.

This patch changes JsMethod and visibility upgraded methods as
forwarding methods instead of aliases at each level.

Earlier aliases was created at each override:

 parent.prototype.m_alias = parent.prototype.m;
 child.prototype.m_alias = parent.prototype.m;

Now it is forwarding method at the root overridde:

 parent.prototype.m_alias = function() { return this.m() };
 // note no alias in the child

This both helps to workaround closure compiler bug with method aliases
and will potentially help with code size where there are a lot
overriddes.

Change-Id: I039b0887fcb67cb661970479617354355ee2c7c9
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
index 1071a3c..c260486 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
@@ -95,7 +95,11 @@
   }
 
   private boolean isJsInterfaceMethod() {
-    return enclosingType instanceof JInterfaceType && enclosingType.isJsType();
+    return isInterFaceMethod() && enclosingType.isJsType();
+  }
+
+  private boolean isInterFaceMethod() {
+    return enclosingType instanceof JInterfaceType;
   }
 
   @Override
@@ -138,6 +142,29 @@
     return isConstructor() && jsName != null;
   }
 
+  /**
+   * Returns {@code true} if this method is the first method in the method hierarchy that exposes a
+   * JsMethod inside a class.
+   */
+  public boolean exposesJsMethod() {
+    if (isInterFaceMethod()) {
+      return false;
+    }
+
+    boolean isJsMethod = jsName != null;
+    for (JMethod override : getOverriddenMethods()) {
+      if (override.jsName == null) {
+        continue;
+      }
+      isJsMethod = true;
+      if (!override.isInterFaceMethod()) {
+        return false; // some other method already exposed this method.
+      }
+    }
+
+    return isJsMethod;
+  }
+
   public boolean isOrOverridesJsMethod() {
     if (jsName != null) {
       return true;
@@ -441,24 +468,26 @@
   }
 
   /**
-   * Returns true if this method overrides a package private method and increases its
-   * visibility.
+   * Returns {@code true} if this method is the first method in the method hierarchy that increases
+   * the visibility of a package private method.
    */
-  public boolean exposesOverriddenPackagePrivateMethod() {
+  public boolean exposesPackagePrivateMethod() {
     if (isPrivate() || isPackagePrivate()) {
       return false;
     }
 
+    boolean hasPackageVisibleParent = false;
     for (JMethod overriddenMethod : overriddenMethods) {
-      if (overriddenMethod.getEnclosingType() instanceof JInterfaceType) {
+      if (overriddenMethod.isInterFaceMethod()) {
         continue;
       }
-      if (overriddenMethod.isPackagePrivate()) {
-        return true;
+      if (!overriddenMethod.isPackagePrivate()) {
+        return false; // some other method already exposed this method.
       }
+      hasPackageVisibleParent = true;
     }
 
-    return false;
+    return hasPackageVisibleParent;
   }
 
   public AccessModifier getAccess() {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
index aa4b900..c609e35 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
@@ -2657,9 +2657,9 @@
     }
 
     private void generateVTableAlias(List<JsStatement> globalStmts, JMethod method, JsName alias) {
-      JsNameRef polyname = polymorphicNames.get(method).makeRef(method.getSourceInfo());
-      polyname.setQualifier(getPrototypeQualifierOf(method));
-      generateVTableAssignment(globalStmts, method, alias, polyname);
+      JsName polyName = polymorphicNames.get(method);
+      JsExpression bridge = JsUtils.createBridge(method, polyName, topScope);
+      generateVTableAssignment(globalStmts, method, alias, bridge);
     }
 
     private JsExprStmt outputDisplayName(JsNameRef function, JMethod method) {
@@ -2695,51 +2695,42 @@
     private void generateVTables(JClassType x, List<JsStatement> globalStmts) {
       assert x != program.getTypeJavaLangString();
       for (JMethod method : x.getMethods()) {
-        if (method.needsVtable() && !method.isAbstract()) {
-          /*
-           * Inline JsFunction rather than reference, e.g. _.vtableName =
-           * function functionName() { ... }
-           */
+        if (!method.needsVtable()) {
+          continue;
+        }
+
+        if (!method.isAbstract()) {
           JsExpression rhs = getJsFunctionFor(method);
           generateVTableAssignment(globalStmts, method, polymorphicNames.get(method), rhs);
+        }
 
-          if (method.isOrOverridesJsMethod()) {
-            String jsName = method.getJsName();
-            JsName exportedName = interfaceScope.declareName(jsName, jsName);
-            exportedName.setObfuscatable(false);
-            generateVTableAlias(globalStmts, method, exportedName);
-          }
+        if (method.exposesJsMethod()) {
+          String jsName = method.getJsName();
+          JsName exportedName = interfaceScope.declareName(jsName, jsName);
+          exportedName.setObfuscatable(false);
+          generateVTableAlias(globalStmts, method, exportedName);
+        }
 
-          if (method.exposesOverriddenPackagePrivateMethod()) {
-            // This method exposes a package private method that is actually live, hence it needs
-            // to make the package private name and the public name to be the same implementation at
-            // runtime. This is done by an assignment of the form.
-            // _.package_private_name = _.exposed_name
-
-            // Here is the situation where this is needed:
-            //
-            // class a.A { m() {} }
-            // class b.B extends a.A { m() {} }
-            // interface I { m(); }
-            // class a.C {
-            //  { A a = new b.B();  a.m() // calls A::m()} }
-            //  { I i = new b.B();  a.m() // calls B::m()} }
-            // }
-            //
-            // Up to this point it is clear that package private names need to be different than
-            // public names.
-            //
-            // Add class a.D extends a.A implements I { public m() }
-            //
-            // a.D collapses A::m and I::m into the same function and it was clear that two
-            // two different names were already needed, hence when creating the vtable for a.D
-            // both names have to point to the same function.
-            //
-            // It should be noted that all subclasses of a.D will have the two methods collapsed,
-            // and hence this assignment will be present in the vtable setup for all subclasses.
-
-            generateVTableAlias(globalStmts, method, getPackagePrivateName(method));
-          }
+        if (method.exposesPackagePrivateMethod()) {
+          // Here is the situation where this is needed:
+          //
+          // class a.A { m() {} }
+          // class b.B extends a.A { m() {} }
+          // interface I { m(); }
+          // class a.C {
+          //  { A a = new b.B();  a.m() // calls A::m()} }
+          //  { I i = new b.B();  a.m() // calls B::m()} }
+          // }
+          //
+          // Up to this point it is clear that package private names need to be different than
+          // public names.
+          //
+          // Add class a.D extends a.A implements I { public m() }
+          //
+          // a.D collapses A::m and I::m into the same function and it was clear that two
+          // two different names were already needed, hence when creating the vtable for a.D
+          // both names have to point to the same function.
+          generateVTableAlias(globalStmts, method, getPackagePrivateName(method));
         }
       }
     }
diff --git a/dev/core/src/com/google/gwt/dev/js/JsUtils.java b/dev/core/src/com/google/gwt/dev/js/JsUtils.java
index 9f8732f..73e9873 100644
--- a/dev/core/src/com/google/gwt/dev/js/JsUtils.java
+++ b/dev/core/src/com/google/gwt/dev/js/JsUtils.java
@@ -16,6 +16,9 @@
 package com.google.gwt.dev.js;
 
 import com.google.gwt.dev.jjs.SourceInfo;
+import com.google.gwt.dev.jjs.ast.JMethod;
+import com.google.gwt.dev.jjs.ast.JParameter;
+import com.google.gwt.dev.jjs.ast.JPrimitiveType;
 import com.google.gwt.dev.js.ast.JsBinaryOperation;
 import com.google.gwt.dev.js.ast.JsBinaryOperator;
 import com.google.gwt.dev.js.ast.JsBlock;
@@ -25,7 +28,10 @@
 import com.google.gwt.dev.js.ast.JsName;
 import com.google.gwt.dev.js.ast.JsNameRef;
 import com.google.gwt.dev.js.ast.JsNode;
+import com.google.gwt.dev.js.ast.JsParameter;
+import com.google.gwt.dev.js.ast.JsReturn;
 import com.google.gwt.dev.js.ast.JsScope;
+import com.google.gwt.dev.js.ast.JsThisRef;
 import com.google.gwt.dev.util.StringInterner;
 
 /**
@@ -88,6 +94,30 @@
     return new JsBinaryOperation(lhs.getSourceInfo(), JsBinaryOperator.ASG, lhs, rhs);
   }
 
+  public static JsFunction createBridge(JMethod method, JsName polyName, JsScope scope) {
+    SourceInfo sourceInfo = method.getSourceInfo();
+    JsFunction bridge = new JsFunction(sourceInfo, scope);
+    for (JParameter p : method.getParams()) {
+      JsName name = bridge.getScope().declareName(p.getName());
+      bridge.getParameters().add(new JsParameter(sourceInfo, name));
+    }
+    JsNameRef ref = polyName.makeRef(sourceInfo);
+    ref.setQualifier(new JsThisRef(sourceInfo));
+    JsInvocation invocation = new JsInvocation(sourceInfo, ref);
+    for (JsParameter p : bridge.getParameters()) {
+      invocation.getArguments().add(p.getName().makeRef(sourceInfo));
+    }
+
+    JsBlock block = new JsBlock(sourceInfo);
+    if (method.getType() == JPrimitiveType.VOID) {
+      block.getStatements().add(invocation.makeStmt());
+    } else {
+      block.getStatements().add(new JsReturn(sourceInfo, invocation));
+    }
+    bridge.setBody(block);
+    return bridge;
+  }
+
   public static JsFunction createEmptyFunctionLiteral(SourceInfo info, JsScope scope, JsName name) {
     JsFunction func = new JsFunction(info, scope, name);
     func.setBody(new JsBlock(info));
diff --git a/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/JavaClassHierarchySetupUtil.java b/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/JavaClassHierarchySetupUtil.java
index 2180870..ce2ac4b 100644
--- a/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/JavaClassHierarchySetupUtil.java
+++ b/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/JavaClassHierarchySetupUtil.java
@@ -166,20 +166,6 @@
   }-*/;
 
   /**
-   * If the parameter o is a Javascript object, return the bridge method reference,
-   * otherwise return the nonbridge reference.
-   * @param o an instance object we want to invoke a method on
-   * @param bridgeRef a reference to an exported, bridgereference or jstype method
-   * @param nonbridgeRef the internal reference to Java obfuscated method
-   * @return
-   */
-  public static native boolean trampolineBridgeMethod(Object o, Object bridgeRef,
-      Object nonbridgeRef) /*-{
-    return @com.google.gwt.lang.Cast::isJavaScriptObject(Ljava/lang/Object;)(o)
-        ? bridgeRef : nonbridgeRef;
-  }-*/;
-
-  /**
    * Do polyfills for all methods expected in a modern browser.
    */
   public static native void modernizeBrowser() /*-{
diff --git a/user/test/com/google/gwt/core/client/interop/AbstractJsType.java b/user/test/com/google/gwt/core/client/interop/AbstractJsType.java
new file mode 100644
index 0000000..5f059e5
--- /dev/null
+++ b/user/test/com/google/gwt/core/client/interop/AbstractJsType.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.client.interop;
+
+import com.google.gwt.core.client.js.JsType;
+
+/**
+ * This concrete test class is *directly* annotated as a @JsType.
+ */
+@JsType
+abstract class AbstractJsType {
+  public abstract int publicMethod();
+}
diff --git a/user/test/com/google/gwt/core/client/interop/ConcreteJsTypeSubclass.java b/user/test/com/google/gwt/core/client/interop/ConcreteJsTypeSubclass.java
index 555a85a..789fb02 100644
--- a/user/test/com/google/gwt/core/client/interop/ConcreteJsTypeSubclass.java
+++ b/user/test/com/google/gwt/core/client/interop/ConcreteJsTypeSubclass.java
@@ -32,7 +32,8 @@
    * do not override already exported members.
    */
 
-  public void publicSubclassMethod() {
+  public int publicSubclassMethod() {
+    return super.publicMethod();
   }
 
   public static void publicStaticSubclassMethod() {
diff --git a/user/test/com/google/gwt/core/client/interop/JsTypeTest.java b/user/test/com/google/gwt/core/client/interop/JsTypeTest.java
index 496ad5a..4914698 100644
--- a/user/test/com/google/gwt/core/client/interop/JsTypeTest.java
+++ b/user/test/com/google/gwt/core/client/interop/JsTypeTest.java
@@ -81,10 +81,24 @@
     assertJsTypeDoesntHaveFields(concreteJsType, "publicStaticMethod", "privateMethod",
         "protectedMethod", "packageMethod", "publicStaticField", "privateField", "protectedField",
         "packageField");
+
+    assertEquals(10, callIntFunction(concreteJsType, "publicMethod"));
+  }
+
+  public void testAbstractJsTypeAccess() {
+    AbstractJsType jsType = new AbstractJsType() {
+      @Override
+      public int publicMethod() {
+        return 32;
+      }
+    };
+
+    assertJsTypeHasFields(jsType, "publicMethod");
+    assertEquals(32, callIntFunction(jsType, "publicMethod"));
+    assertEquals(32, jsType.publicMethod());
   }
 
   public void testConcreteJsTypeSubclassAccess() {
-    ConcreteJsType concreteJsType = new ConcreteJsType();
     ConcreteJsTypeSubclass concreteJsTypeSubclass = new ConcreteJsTypeSubclass();
 
     // A subclass of a JsType is not itself a JsType.
@@ -94,12 +108,10 @@
         "privateSubclassField", "protectedSubclassField", "packageSubclassField");
 
     // But if it overrides an exported method then the overriding method will be exported.
-    assertJsTypeHasFields(concreteJsType, "publicMethod");
     assertJsTypeHasFields(concreteJsTypeSubclass, "publicMethod");
-    assertFalse(
-        areSameFunction(concreteJsType, "publicMethod", concreteJsTypeSubclass, "publicMethod"));
-    assertFalse(callIntFunction(concreteJsType, "publicMethod")
-        == callIntFunction(concreteJsTypeSubclass, "publicMethod"));
+
+    assertEquals(20, callIntFunction(concreteJsTypeSubclass, "publicMethod"));
+    assertEquals(10, concreteJsTypeSubclass.publicSubclassMethod());
   }
 
   public void testConcreteJsTypeNoTypeTightenField() {
@@ -352,11 +364,6 @@
     return !!$wnd;
   }-*/;
 
-  private static native boolean areSameFunction(
-      Object thisObject, String thisFunctionName, Object thatObject, String thatFunctionName) /*-{
-    return thisObject[thisFunctionName] === thatObject[thatFunctionName];
-  }-*/;
-
   private static native int callIntFunction(Object object, String functionName) /*-{
     return object[functionName]();
   }-*/;