Devirtualize JsFunction implementations
Change-Id: Id352e6a877f30ff8710fae32e34ec45d09583a60
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/Devirtualizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/Devirtualizer.java
index 67d5e4d..5bd2725 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/Devirtualizer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/Devirtualizer.java
@@ -301,14 +301,19 @@
/**
* Returns true if {@code method} is an overlay method. Overlay methods include the ones that
- * are marked as JsOverlay but also (synthetic) private instance methods on interfaces.
- * <p>
- * Synthetic private methods on interfaces are the result of lambdas that capture the enclosing
- * instance and are defined on default methods.
+ * are marked as JsOverlay but also implicit overlays.
*/
private boolean isOverlayMethod(JMethod method) {
return method.isJsOverlay()
- || (method.getEnclosingType() instanceof JInterfaceType && method.isPrivate());
+ // Synthetic private methods on interfaces are the result of lambdas that capture the
+ // enclosing instance and are defined on default methods; these can appear in native
+ // interfaces and thus need to be treated as overlays.
+ || (method.getEnclosingType() instanceof JInterfaceType && method.isPrivate())
+ // JsFunction implementation methods other than the other than the SAM implementation are
+ // also considered overalys to allow for lighter weight JsFuncitons.
+ // TODO(rluble): SAM implementation should also be devirtualized.
+ || (method.getEnclosingType().isJsFunctionImplementation()
+ && !method.isOrOverridesJsFunctionMethod());
}
public static void exec(JProgram program) {
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 133cd79..f21fe19 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
@@ -985,18 +985,24 @@
: ctorName.makeRef(sourceInfo);
List<JsExpression> arguments = transform(newInstance.getArgs());
- JsNew newExpr = (JsNew) JsUtils.createInvocationOrPropertyAccess(
- InvocationStyle.NEWINSTANCE, sourceInfo, ctor, null, reference, arguments);
-
if (newInstance.getClassType().isJsFunctionImplementation()) {
- return constructJsFunctionObject(sourceInfo, newInstance.getClassType(), ctorName, newExpr);
+ // Synthesize makeLambdaFunction(samMethodReference, constructorReference, ctorArguments)
+ // which will create the function instance and run the constructor on it.
+ // TODO(rluble): optimize the constructor call away if it is empty.
+ return constructJsFunctionObject(
+ sourceInfo,
+ newInstance.getClassType(),
+ ctorName,
+ reference,
+ new JsArrayLiteral(sourceInfo, arguments));
}
- return newExpr;
+ return JsUtils.createInvocationOrPropertyAccess(
+ InvocationStyle.NEWINSTANCE, sourceInfo, ctor, null, reference, arguments);
}
- private JsNode constructJsFunctionObject(SourceInfo sourceInfo, JClassType type,
- JsName ctorName, JsNew newExpr) {
+ private JsExpression constructJsFunctionObject(SourceInfo sourceInfo, JClassType type,
+ JsName ctorName, JsNameRef ctorReference, JsExpression ctorArguments) {
// Foo.prototype.functionMethodName
JMethod jsFunctionMethod = getJsFunctionMethod(type);
JsNameRef funcNameRef = JsUtils.createQualifiedNameRef(sourceInfo,
@@ -1004,7 +1010,7 @@
// makeLambdaFunction(Foo.prototype.functionMethodName, new Foo(...))
return constructInvocation(sourceInfo, RuntimeConstants.RUNTIME_MAKE_LAMBDA_FUNCTION,
- funcNameRef, newExpr);
+ funcNameRef, ctorReference, ctorArguments);
}
private JMethod getJsFunctionMethod(JClassType type) {
@@ -2513,6 +2519,11 @@
* literal in each constructor.
*/
private boolean initializeAtTopScope(JField x) {
+ if (x.getEnclosingType().isJsFunctionImplementation()) {
+ // JsFunction implementation are plain JS functions with no class prototype, fields
+ // need to be initialized and placed on the instance itself.
+ return false;
+ }
if (x.getLiteralInitializer() == null) {
return false;
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeCategory.java b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeCategory.java
index 3772b19..4f3e702 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeCategory.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeCategory.java
@@ -126,7 +126,7 @@
return TypeCategory.TYPE_JS_UNKNOWN_NATIVE;
} else if (type instanceof JClassType && type.isJsNative()) {
return TypeCategory.TYPE_JS_NATIVE;
- } else if (type.isJsFunction()) {
+ } else if (type.isJsFunction() || type.isJsFunctionImplementation()) {
return TypeCategory.TYPE_JS_FUNCTION;
}
return TypeCategory.TYPE_JAVA_OBJECT;
diff --git a/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Runtime.java b/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Runtime.java
index 0cdfd56..45bd626 100644
--- a/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Runtime.java
+++ b/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Runtime.java
@@ -156,18 +156,12 @@
* Create a function that applies the specified samMethod on itself, and whose __proto__ points to
* <code>instance</code>.
*/
- public static native JavaScriptObject makeLambdaFunction(JavaScriptObject samMethod,
- JavaScriptObject instance) /*-{
+ public static native JavaScriptObject makeLambdaFunction(
+ JavaScriptObject samMethod,
+ JavaScriptObject ctor,
+ JavaScriptObject ctorArguments) /*-{
var lambda = function() { return samMethod.apply(lambda, arguments); }
-
- if (lambda.__proto__) {
- lambda.__proto__ = instance;
- } else {
- for (var prop in instance) {
- lambda[prop] = instance[prop];
- }
- }
-
+ ctor.apply(lambda, ctorArguments);
return lambda;
}-*/;
diff --git a/user/test/com/google/gwt/core/interop/JsFunctionTest.java b/user/test/com/google/gwt/core/interop/JsFunctionTest.java
index 4f74b93..8c8ccad 100644
--- a/user/test/com/google/gwt/core/interop/JsFunctionTest.java
+++ b/user/test/com/google/gwt/core/interop/JsFunctionTest.java
@@ -146,13 +146,8 @@
assertNotNull(c2);
ElementLikeNativeInterface i = (ElementLikeNativeInterface) createFunction();
assertNotNull(i);
- try {
- MyJsFunctionInterfaceImpl c3 = (MyJsFunctionInterfaceImpl) createFunction();
- assertNotNull(c3);
- fail("ClassCastException should be caught.");
- } catch (ClassCastException cce) {
- // Expected.
- }
+ MyJsFunctionInterfaceImpl c3 = (MyJsFunctionInterfaceImpl) createFunction();
+ assertNotNull(c3);
}
public void testCast_fromJsObject() {
@@ -304,6 +299,19 @@
assertEquals(MyJsFunctionInterface.class, ((Object) createMyJsFunction()).getClass());
}
+ public void testInstanceField() {
+
+ MyJsFunctionInterface jsfunctionImplementation =
+ new MyJsFunctionInterface() {
+ String hello = new Object().getClass().getName();
+ @Override
+ public int foo(int a) {
+ return hello.length() + a;
+ }
+ };
+ assertEquals(Object.class.getName().length() + 4, jsfunctionImplementation.foo(4));
+ }
+
// uncomment when Java8 is supported.
// public void testJsFunctionLambda_JS() {
// MyJsFunctionInterface jsFunctionInterface = a -> { return a + 2; };