ControlFlowAnalyzer JsInterop overhaul.
- Major simplifications the special casing of JsInterop types
in ControlFlowAnalyzer.
- Readability improvements.
- Removes the extra pass that collects the casting data.
- Adds more jsinterop tests.
Change-Id: I5f7ad99dd5e879683b2c5bb262f3b2e5cc4eab20
Review-Link: https://gwt-review.googlesource.com/#/c/12740/
diff --git a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
index 03a1c26..5bef255 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
@@ -68,7 +68,6 @@
import com.google.gwt.dev.jjs.impl.CompileTimeConstantsReplacer;
import com.google.gwt.dev.jjs.impl.ComputeCastabilityInformation;
import com.google.gwt.dev.jjs.impl.ComputeExhaustiveCastabilityInformation;
-import com.google.gwt.dev.jjs.impl.ComputeInstantiatedJsoInterfaces;
import com.google.gwt.dev.jjs.impl.ControlFlowAnalyzer;
import com.google.gwt.dev.jjs.impl.ControlFlowRecorder;
import com.google.gwt.dev.jjs.impl.DeadCodeElimination;
@@ -478,7 +477,6 @@
!shouldOptimize() /* recordTrivialCasts */);
}
- ComputeInstantiatedJsoInterfaces.exec(jprogram);
ImplementCastsAndTypeChecks.exec(jprogram, options.isCastCheckingDisabled(),
shouldOptimize() /* pruneTrivialCasts */);
ArrayNormalizer.exec(jprogram, options.isCastCheckingDisabled());
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java
index 54974e4..3ff2567 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java
@@ -132,16 +132,6 @@
private String javaLangObject;
}
- private Set<JReferenceType> instantiatedJsoTypesViaCast = Sets.newHashSet();
-
- public void setInstantiatedJsoTypesViaCast(Set<JReferenceType> instantiatedJsoTypesViaCast) {
- this.instantiatedJsoTypesViaCast = instantiatedJsoTypesViaCast;
- }
-
- public Set<JReferenceType> getInstantiatedJsoTypesViaCast() {
- return instantiatedJsoTypesViaCast;
- }
-
/**
* A method needs a JsInterop bridge if any of the following are true:
* 1) the method name conflicts with a method name of a non-JsType/JsExport method in a superclass
@@ -548,13 +538,6 @@
return false;
}
- /**
- * True if the type is a JSO or interface implemented by a JSO, or a JsType, or a JsFunction.
- */
- public boolean canBeInstantiatedInJavascript(JType type) {
- return canBeJavaScriptObject(type) || isJsType(type) || isJsFunction(type);
- }
-
public boolean castFailsTrivially(JReferenceType fromType, JReferenceType toType) {
if (!fromType.canBeNull() && toType.isNullType()) {
// Cannot cast non-nullable to null
@@ -999,14 +982,8 @@
return true;
}
}
- // TODO(rluble): ControlFlowAnalyzer should be responsible for making sure that these types
- // are considered live. THIS IS A HACK. In particular this method and the specialized
- // version for JDeclaredType should have the same semantics.
- return isJsType(type) || hasAnyExports(type) || isJsFunction(type);
- }
- private boolean hasAnyExports(JReferenceType type) {
- return type instanceof JDeclaredType ? ((JDeclaredType) type).hasAnyExports() : false;
+ return false;
}
private boolean isArrayInterface(JType type) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ComputeInstantiatedJsoInterfaces.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ComputeInstantiatedJsoInterfaces.java
deleted file mode 100644
index 268f266..0000000
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ComputeInstantiatedJsoInterfaces.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright 2008 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.dev.jjs.impl;
-
-import com.google.gwt.dev.jjs.ast.Context;
-import com.google.gwt.dev.jjs.ast.JCastOperation;
-import com.google.gwt.dev.jjs.ast.JClassType;
-import com.google.gwt.dev.jjs.ast.JDeclaredType;
-import com.google.gwt.dev.jjs.ast.JInterfaceType;
-import com.google.gwt.dev.jjs.ast.JProgram;
-import com.google.gwt.dev.jjs.ast.JReferenceType;
-import com.google.gwt.dev.jjs.ast.JType;
-import com.google.gwt.dev.jjs.ast.JVisitor;
-
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Record all Cast operations on JSOs as instantiations. This must run before
- * {@link com.google.gwt.dev.jjs.impl.ImplementCastsAndTypeChecks}.
- */
-public class ComputeInstantiatedJsoInterfaces {
- class InstantiatedJsoInterfacesCollector extends JVisitor {
- @Override
- public void endVisit(JCastOperation x, Context ctx) {
- JType toType = x.getCastType();
-
- if (toType instanceof JReferenceType && !toType.isNullType()) {
- toType = toType.getUnderlyingType();
- if (program.typeOracle.willCrossCastLikeJso(toType) ||
- program.typeOracle.isOrExtendsJsType(toType, true) ||
- program.typeOracle.isJsFunction(toType)) {
- instantiateJsoInterface((JReferenceType) toType);
- }
- }
- }
- }
-
- private void instantiateJsoInterface(JReferenceType toType) {
- if (instantiatedJsoTypes.add(toType)) {
- if (program.typeOracle.getSingleJsoImpl(toType) != null) {
- // rescuing an Interface via Cast, we record the JSO implementing it
- instantiateJsoInterface(program.typeOracle.getSingleJsoImpl(toType));
- }
- // if it's a class, and the superType is JSO, rescue it too
- if (toType instanceof JClassType) {
- JClassType superType = ((JClassType) toType).getSuperClass();
- if (superType != null && superType.isJsoType()) {
- instantiateJsoInterface(superType);
- }
- }
- // if we extend another JsType, or Interface with JSO implementation, rescue it
- for (JInterfaceType intf : ((JDeclaredType) toType).getImplements()) {
- if (intf.isJsType() || program.typeOracle.getSingleJsoImpl(intf) != null) {
- instantiateJsoInterface(intf);
- }
- }
- }
- }
-
- private final JProgram program;
- private final Set<JReferenceType> instantiatedJsoTypes = new HashSet<JReferenceType>();
-
- public static void exec(JProgram program) {
- new ComputeInstantiatedJsoInterfaces(program).execImpl();
- }
-
- private ComputeInstantiatedJsoInterfaces(JProgram program) {
- this.program = program;
- }
-
- private void execImpl() {
- InstantiatedJsoInterfacesCollector replacer = new InstantiatedJsoInterfacesCollector();
- replacer.accept(program);
- program.typeOracle.setInstantiatedJsoTypesViaCast(instantiatedJsoTypes);
- }
-}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java
index 4582834..05cc741 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java
@@ -17,6 +17,8 @@
import com.google.gwt.dev.jjs.ast.Context;
import com.google.gwt.dev.jjs.ast.JAbsentArrayDimension;
+import com.google.gwt.dev.jjs.ast.JArrayLength;
+import com.google.gwt.dev.jjs.ast.JArrayRef;
import com.google.gwt.dev.jjs.ast.JArrayType;
import com.google.gwt.dev.jjs.ast.JBinaryOperation;
import com.google.gwt.dev.jjs.ast.JBinaryOperator;
@@ -29,6 +31,7 @@
import com.google.gwt.dev.jjs.ast.JExpression;
import com.google.gwt.dev.jjs.ast.JField;
import com.google.gwt.dev.jjs.ast.JFieldRef;
+import com.google.gwt.dev.jjs.ast.JInstanceOf;
import com.google.gwt.dev.jjs.ast.JInterfaceType;
import com.google.gwt.dev.jjs.ast.JLocal;
import com.google.gwt.dev.jjs.ast.JLocalRef;
@@ -92,6 +95,18 @@
private final List<JMethod> curMethodStack = Lists.newArrayList();
@Override
+ public boolean visit(JArrayRef arrayRef, Context ctx) {
+ maybeRescueJsTypeArray(arrayRef.getInstance().getType());
+ return true;
+ }
+
+ @Override
+ public boolean visit(JArrayLength arrayLength, Context ctx) {
+ maybeRescueJsTypeArray(arrayLength.getInstance().getType());
+ return true;
+ }
+
+ @Override
public boolean visit(JArrayType type, Context ctx) {
assert (referencedTypes.contains(type));
boolean isInstantiated = instantiatedTypes.contains(type);
@@ -105,23 +120,23 @@
JClassType superClass = ((JClassType) leafType).getSuperClass();
if (superClass != null) {
// FooSub[] -> Foo[]
- rescue(program.getOrCreateArrayType(superClass, dims), true, isInstantiated);
+ rescue(program.getOrCreateArrayType(superClass, dims), isInstantiated);
didSuperType = true;
}
} else if (leafType instanceof JInterfaceType) {
// Intf[] -> Object[]
rescue(program.getOrCreateArrayType(program.getTypeJavaLangObject(), dims),
- true, isInstantiated);
+ isInstantiated);
didSuperType = true;
}
if (!didSuperType) {
if (dims > 1) {
// anything[][] -> Object[]
- rescue(program.getOrCreateArrayType(program.getTypeJavaLangObject(), dims - 1), true,
+ rescue(program.getOrCreateArrayType(program.getTypeJavaLangObject(), dims - 1),
isInstantiated);
} else {
// anything[] -> Object
- rescue(program.getTypeJavaLangObject(), true, isInstantiated);
+ rescue(program.getTypeJavaLangObject(), isInstantiated);
}
}
@@ -130,7 +145,7 @@
JDeclaredType dLeafType = (JDeclaredType) leafType;
for (JInterfaceType intfType : dLeafType.getImplements()) {
JArrayType intfArray = program.getOrCreateArrayType(intfType, dims);
- rescue(intfArray, true, isInstantiated);
+ rescue(intfArray, isInstantiated);
}
}
@@ -186,10 +201,10 @@
public boolean visit(JCastOperation x, Context ctx) {
// Rescue any JavaScriptObject type that is the target of a cast.
JType targetType = x.getCastType();
- if (!program.typeOracle.canBeInstantiatedInJavascript(targetType)) {
+ if (!canBeInstantiatedInJavaScript(targetType)) {
return true;
}
- rescue((JReferenceType) targetType, true, true);
+ rescue((JReferenceType) targetType, true);
JType exprType = x.getExpr().getType();
if (program.typeOracle.isSingleJsoImpl(targetType)) {
/*
@@ -203,14 +218,8 @@
// source is JSO or SingleJso interface whose implementor is live
JClassType jsoImplementor =
program.typeOracle.getSingleJsoImpl((JReferenceType) targetType);
- if (jsoImplementor != null) {
- rescue(jsoImplementor, true, true);
- }
+ rescue(jsoImplementor, true);
}
- } else if (program.typeOracle.isJsType(targetType)
- && ((JDeclaredType) targetType).getJsPrototype() != null) {
- // keep alive JsType with prototype used in cast so it can used in cast checks against JS objects later
- rescue((JReferenceType) targetType, true, true);
}
return true;
@@ -230,20 +239,18 @@
boolean isInstantiated = instantiatedTypes.contains(type);
// Rescue my super type
- rescue(type.getSuperClass(), true, isInstantiated);
+ rescue(type.getSuperClass(), isInstantiated);
// Rescue my clinit (it won't ever be explicitly referenced)
if (type.hasClinit()) {
rescue(type.getClinitMethod());
}
- // JLS 12.4.1: don't rescue my super interfaces just because I'm rescued.
- // However, if I'm instantiated, let's mark them as instantiated.
- for (JInterfaceType intfType : type.getImplements()) {
- rescue(intfType, false, isInstantiated);
+ // If I'm instantiated, let's mark super interfaces as instantiated.
+ if (isInstantiated) {
+ rescueMembersAndInstantiateSuperInterfaces(type);
}
- rescueMembersIfInstantiable(type);
return false;
}
@@ -289,7 +296,7 @@
* the enclosing types for any static fields that make it here.
*/
if (target.isStatic()) {
- rescue(target.getEnclosingType(), true, false);
+ rescue(target.getEnclosingType(), false);
}
if (target.isStatic() || instantiatedTypes.contains(target.getEnclosingType())) {
rescue(target);
@@ -303,6 +310,16 @@
}
@Override
+ public boolean visit(JInstanceOf x, Context ctx) {
+ JReferenceType targetType = x.getTestType();
+ if (program.typeOracle.isJsType(targetType) || program.typeOracle.isJsFunction(targetType)) {
+ // keep alive JsType used in instanceof so it is not pruned and re-written as null check.
+ rescue(targetType, true);
+ }
+ return true;
+ }
+
+ @Override
public boolean visit(JInterfaceType type, Context ctx) {
boolean isReferenced = referencedTypes.contains(type);
boolean isInstantiated = instantiatedTypes.contains(type);
@@ -313,15 +330,11 @@
rescue(type.getClinitMethod());
}
- // JLS 12.4.1: don't rescue my super interfaces just because I'm rescued.
- // However, if I'm instantiated, let's mark them as instantiated.
+ // If I'm instantiated, let's mark super interfaces as instantiated.
if (isInstantiated) {
- for (JInterfaceType intfType : type.getImplements()) {
- rescue(intfType, false, true);
- }
+ rescueMembersAndInstantiateSuperInterfaces(type);
}
- rescueMembersIfInstantiable(type);
return false;
}
@@ -338,10 +351,10 @@
if (enclosingType.isJsoType()) {
// Calls to JavaScriptObject types rescue those types.
boolean instance = !x.isStatic() || program.isStaticImpl(x);
- rescue(enclosingType, true, instance);
+ rescue(enclosingType, instance);
} else if (x.isStatic()) {
// JLS 12.4.1: references to static methods rescue the enclosing class
- rescue(enclosingType, true, false);
+ rescue(enclosingType, false);
}
if (x.isNative()) {
@@ -405,7 +418,14 @@
return true;
}
- return rescueArgumentsIfParametersCanBeRead(call, method);
+ if (call.getInstance() != null) {
+ // Explicitly visit instance since we're returning false below.
+ this.accept(call.getInstance());
+ }
+
+ rescueArgumentsIfParametersCanBeRead(call);
+
+ return false;
}
@Override
@@ -421,11 +441,11 @@
if (newArray.dims.get(i) instanceof JAbsentArrayDimension) {
break;
}
- rescue(program.getOrCreateArrayType(leafType, nDims - i), true, true);
+ rescue(program.getOrCreateArrayType(leafType, nDims - i), true);
}
} else {
// just rescue my own specific type
- rescue(arrayType, true, true);
+ rescue(arrayType, true);
}
return true;
}
@@ -433,7 +453,7 @@
@Override
public boolean visit(JNewInstance x, Context ctx) {
// rescue and instantiate the target class!
- rescueAndInstantiate(x.getClassType());
+ rescue(x.getClassType(), true);
return super.visit(x, ctx);
}
@@ -488,7 +508,7 @@
if (x.getTarget() instanceof JConstructor) {
// But if a constructor is targeted, there is an implicit 'new' op.
JConstructor ctor = (JConstructor) x.getTarget();
- rescueAndInstantiate(ctor.getEnclosingType());
+ rescue(ctor.getEnclosingType(), true);
}
return visit((JMethodCall) x, ctx);
}
@@ -498,13 +518,14 @@
liveStrings.add(literal.getValue());
// rescue and instantiate java.lang.String
- rescue(program.getTypeJavaLangString(), true, true);
+ rescue(program.getTypeJavaLangString(), true);
return true;
}
private boolean canBeInstantiatedInJavaScript(JType type) {
- if (program.typeOracle.canBeInstantiatedInJavascript(type) ||
- program.isJavaLangString(type)) {
+ // Technically, JsType/JsFunction are also instantiatable in JavaScript but we don't track
+ // them using similar to JSO as if we do that then after cast normalization, they got pruned.
+ if (program.typeOracle.canBeJavaScriptObject(type) || program.isJavaLangString(type)) {
return true;
}
@@ -581,13 +602,11 @@
if (!canBeInstantiatedInJavaScript(type)) {
return;
}
- rescue((JReferenceType) type, true, true);
+ rescue((JReferenceType) type, true);
if (program.typeOracle.isSingleJsoImpl(type)) {
// Cast of JSO into SingleJso interface, rescue the implementor if exists
JClassType singleJsoImpl = program.typeOracle.getSingleJsoImpl((JReferenceType) type);
- if (singleJsoImpl != null) {
- rescue(singleJsoImpl, true, true);
- }
+ rescue(singleJsoImpl, true);
}
}
@@ -596,8 +615,19 @@
return;
}
- if (!liveFieldsAndMethods.contains(method)) {
- liveFieldsAndMethods.add(method);
+ if (liveFieldsAndMethods.add(method)) {
+ JDeclaredType enclosingType = method.getEnclosingType();
+ // If any method reachable for JsType/Function interface, we rescue it so it is not pruned.
+ // This is simpler than tracking where the objects may enter the system and cheap as
+ // interfaces doesn't cost much in the output.
+ // More appropriate solution is to track casts and JSNI methods (see
+ // #canBeInstantiatedInJavaScript) but unfortunately casts are replaced at a later stage
+ // that causes type and all calls to be pruned.
+ if (enclosingType instanceof JInterfaceType
+ && (method.isJsTypeMember() || method.isJsFunctionMethod())) {
+ rescue(enclosingType, true);
+ }
+
membersToRescueIfTypeIsInstantiated.remove(method);
if (dependencyRecorder != null) {
curMethodStack.add(method);
@@ -607,43 +637,22 @@
if (dependencyRecorder != null) {
curMethodStack.remove(curMethodStack.size() - 1);
}
- if (method.isNative() || method.isOrOverridesJsTypeMethod()
- || method.isOrOverridesJsFunctionMethod()) {
- /*
- * SPECIAL: returning from this method passes a value from
- * JavaScript into Java.
- */
+ if (method.isNative()) {
+ // Returning from this method passes a value from JavaScript into Java.
maybeRescueJavaScriptObjectPassingIntoJava(method.getType());
}
- if (method.isExported()
- || method.isOrOverridesJsTypeMethod()
- || method.isOrOverridesJsFunctionMethod()) {
+ if (method.isExported() || method.isJsTypeMember() || method.isJsFunctionMethod()) {
for (JParameter param : method.getParams()) {
- /**
- * TODO (cromwellian): JS visible methods (virtual or static) may be supplied
- * instantiated types that are implemented in JS. This code prevents parameters
- * that are JS interface types from being considered un-instantiated. This logic
- * needs to be tightened up here and elsewhere, we want to be conservative as
- * possible to avoid code size bloat. Parameters in JsExport methods or
- * JsType methods should not be pruned in order to keep the calling API
- * consistent between optimized Java and JS.
- */
+ // Parameters in JsExport, JsType, JsFunction methods should not be pruned in order to
+ // keep the API intact.
rescue(param);
- // Strings, Arrays, JSOs, and JsTypes can all be instantiatd in Javascript
- if (canBeInstantiatedInJavaScript(param.getType())) {
- // Param can be read from external JS if this method is implemented in JS
- // this should really be done in rescueArgumentsIfParametersCanBeRead, but
- // there is no JMethodCall to process since it might be from external JS
- // arg supplied from external caller can be from JS, so treat type as instantiated
- rescue((JReferenceType) param.getType(), true, true);
- }
}
}
rescueOverridingMethods(method);
if (method == getClassMethod) {
rescueClassLiteralsIfGetClassIsLive();
}
- if (program.isJsTypePrototype(method.getEnclosingType())) {
+ if (program.isJsTypePrototype(enclosingType)) {
// for JsInterface Prototype methods, rescue all parameters
// because these are stub methods and the parameters would get pruned ordinarily
for (JParameter param : method.getParams()) {
@@ -656,7 +665,19 @@
}
}
- private void rescue(JReferenceType type, boolean isReferenced, boolean isInstantiated) {
+ private void maybeRescueJsTypeArray(JType type) {
+ if (!(type instanceof JArrayType)) {
+ return;
+ }
+ JArrayType arrayType = (JArrayType) type;
+ if (program.typeOracle.isJsType(arrayType.getLeafType()) ||
+ program.typeOracle.isJsFunction(arrayType.getLeafType())) {
+ rescue(arrayType, true);
+ maybeRescueJsTypeArray(arrayType.getElementType());
+ }
+ }
+
+ private void rescue(JReferenceType type, boolean isInstantiated) {
if (type == null) {
return;
}
@@ -673,7 +694,7 @@
doVisit = true;
}
- if (isReferenced && referencedTypes.add(type)) {
+ if (referencedTypes.add(type)) {
doVisit = true;
}
@@ -682,42 +703,25 @@
}
accept(type);
- if (type instanceof JDeclaredType) {
- /*
- * For @JsType, we rescue all JsType exposed fields and methods
- * because we don't know if they'll be called from JS or not.
- * That is, the Java implementor may be called because the interface
- * was passed into JS, or it may be called via exported functions.
- *
- * For @JsFunction, we rescue its sam function because we don't know
- * if they'll be called from JS or not. That is, when a instance of
- * a @JsFunction is passed to JS, its sam function may be implicitly
- * called by the instance.
- *
- * We may be able to tighten this to check for @JsExport as well,
- * since if there is no @JsExport, the only way for JS code to get a
- * reference to the interface is by it being constructed in Java
- * and passed via JSNI into JS, and in that mechanism, the
- * rescue would happen automatically.
- */
- JDeclaredType dtype = (JDeclaredType) type;
- // On the surface this would appear to fail to correctly rescue any exported method that is
- // on a subclass of a @JsType annotated type. But actually since such an exported method is
- // an override and since it overrides something that has been rescued, such methods are
- // already safely rescued by other ControlFlow logic.
- if (dtype.isJsType() || dtype.isJsFunction()) {
- for (JMethod method : dtype.getMethods()) {
- if (method.isOrOverridesJsTypeMethod()
- || method.isOrOverridesJsFunctionMethod()) {
- rescue(method);
- }
- }
- for (JField field : dtype.getFields()) {
- if (field.isJsTypeMember()) {
- rescue(field);
- }
- }
+ if (!program.typeOracle.isJsType(type) && !program.typeOracle.isJsFunction(type)) {
+ return;
+ }
+
+ /*
+ * We rescue all JsType member and JsFunction methods because we don't know if they'll be
+ * called from JS or not.
+ */
+ JDeclaredType declaredType = (JDeclaredType) type;
+
+ for (JMethod method : declaredType.getMethods()) {
+ if (method.isJsTypeMember() || method.isJsFunctionMethod()) {
+ rescue(method);
+ }
+ }
+ for (JField field : declaredType.getFields()) {
+ if (field.isJsTypeMember()) {
+ rescue(field);
}
}
}
@@ -775,10 +779,6 @@
}
}
- private void rescueAndInstantiate(JClassType type) {
- rescue(type, true, true);
- }
-
/**
* The code is very tightly tied to the behavior of
* Pruner.CleanupRefsVisitor. CleanUpRefsVisitor will prune unread
@@ -787,11 +787,10 @@
* need to iterate over Pruner until reaching a stable point, so we avoid
* actually rescuing such arguments until/unless the parameter is read.
*/
- private boolean rescueArgumentsIfParametersCanBeRead(JMethodCall call, JMethod method) {
- if (call.getInstance() != null) {
- // Explicitly visit instance since we're returning false below.
- this.accept(call.getInstance());
- }
+ private void rescueArgumentsIfParametersCanBeRead(JMethodCall call) {
+ JMethod method = call.getTarget();
+ assert !method.canBePolymorphic();
+
List<JExpression> args = call.getArgs();
List<JParameter> params = method.getParams();
int i = 0;
@@ -799,9 +798,7 @@
JExpression arg = args.get(i);
JParameter param = params.get(i);
if (arg.hasSideEffects() || liveFieldsAndMethods.contains(param)
- // rescue any args of JsInterface Prototype methods or JsInterface
- || method.isOrOverridesJsTypeMethod()
- || method.isOrOverridesJsFunctionMethod()
+ // rescue any args of JsInterface Prototype methods
|| program.isJsTypePrototype(method.getEnclosingType())) {
this.accept(arg);
continue;
@@ -812,7 +809,6 @@
for (int c = args.size(); i < c; ++i) {
this.accept(args.get(i));
}
- return false;
}
/**
@@ -853,19 +849,21 @@
}
}
- /**
- * If the type is instantiable, rescue any of its virtual methods that a
- * previously seen method call could call.
- */
- private void rescueMembersIfInstantiable(JDeclaredType type) {
- if (!instantiatedTypes.contains(type)) {
- return;
+ private void rescueMembersAndInstantiateSuperInterfaces(JDeclaredType type) {
+ for (JInterfaceType intfType : type.getImplements()) {
+ rescue(intfType, true);
}
+ rescueMembers(type);
+ }
+
+ /**
+ * Rescues any of type's virtual methods that a previously seen method call could call.
+ */
+ private void rescueMembers(JDeclaredType type) {
+ assert instantiatedTypes.contains(type);
+
for (JMethod method : type.getMethods()) {
- if (!method.isStatic() && (membersToRescueIfTypeIsInstantiated.contains(method)
- // method may be called from JS as well
- || method.isOrOverridesJsTypeMethod()
- || method.isOrOverridesJsFunctionMethod())) {
+ if (!method.isStatic() && membersToRescueIfTypeIsInstantiated.contains(method)) {
rescue(method);
}
}
@@ -900,12 +898,13 @@
}
}
- private boolean isTypeInstantiatedOrJso(JType type) {
+ private boolean isTypeInstantiatedOrJso(JDeclaredType type) {
if (type == null) {
return false;
}
- return type.isJsoType() || instantiatedTypes.contains(type);
+ return type.isJsoType() || type.isJsFunction() || type.isJsType()
+ || instantiatedTypes.contains(type);
}
/**
@@ -1010,20 +1009,6 @@
}
/**
- * Forcibly rescue {@code typesToRescue}.
- * <p>
- * NOTE: this is used to rescue types that are made live by operations (e.g. casts) that
- * have been eliminated by a normalization pass.
- */
- public void rescue(Iterable<JReferenceType> typesToRescue) {
- // TODO(rluble): this functionality should go away, the AST should contain all the information
- // needed to determine whether a type is live or not.
- for (JReferenceType type : typesToRescue) {
- rescuer.rescue(type, true, true);
- }
- }
-
- /**
* Specify the {@link DependencyRecorder} to be used for future traversals.
* Specifying <code>null</code> means to stop recording dependencies.
*/
@@ -1058,13 +1043,13 @@
for (JMethod method : type.getMethods()) {
if (method.isExported()) {
// treat class as instantiated, since a ctor may be called from JS export
- rescuer.rescue(method.getEnclosingType(), true, true);
+ rescuer.rescue(method.getEnclosingType(), true);
traverseFrom(method);
}
}
for (JField field : type.getFields()) {
if (field.isExported()) {
- rescuer.rescue(field.getEnclosingType(), true, true);
+ rescuer.rescue(field.getEnclosingType(), true);
rescuer.rescue(field);
}
}
@@ -1102,11 +1087,11 @@
* execute as a result.
*/
public void traverseFromInstantiationOf(JDeclaredType type) {
- rescuer.rescue(type, true, true);
+ rescuer.rescue(type, true);
}
public void traverseFromReferenceTo(JDeclaredType type) {
- rescuer.rescue(type, true, false);
+ rescuer.rescue(type, false);
}
/**
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java b/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java
index d7823c2..2aa55fd 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java
@@ -31,7 +31,6 @@
import com.google.gwt.dev.jjs.ast.JExpression;
import com.google.gwt.dev.jjs.ast.JField;
import com.google.gwt.dev.jjs.ast.JFieldRef;
-import com.google.gwt.dev.jjs.ast.JInterfaceType;
import com.google.gwt.dev.jjs.ast.JLocal;
import com.google.gwt.dev.jjs.ast.JLocalRef;
import com.google.gwt.dev.jjs.ast.JMethod;
@@ -408,7 +407,7 @@
@Override
public boolean visit(JDeclaredType type, Context ctx) {
- assert (referencedTypes.contains(type) || type instanceof JInterfaceType);
+ assert referencedTypes.contains(type);
Predicate<JNode> notReferenced = Predicates.not(Predicates.in(referencedNonTypes));
removeFields(notReferenced, type);
removeMethods(notReferenced, type);
@@ -481,12 +480,9 @@
@Override
public boolean visit(JProgram program, Context ctx) {
- for (JMethod method : program.getEntryMethods()) {
- accept(method);
- }
for (Iterator<JDeclaredType> it = program.getDeclaredTypes().iterator(); it.hasNext();) {
JDeclaredType type = it.next();
- if (referencedTypes.contains(type) || program.typeOracle.isInstantiatedType(type)) {
+ if (referencedTypes.contains(type)) {
accept(type);
} else {
prunedMethods.addAll(type.getMethods());
@@ -657,11 +653,6 @@
OptimizerStats stats = new OptimizerStats(NAME);
ControlFlowAnalyzer livenessAnalyzer = new ControlFlowAnalyzer(program);
- // Don't prune JSOs, JsTypes that were considered instantiated before removing
- // casts at {@link ImplementCastsAndTypeChecks}.
- // TODO(rluble): the AST should have been left in a state that whatever method, attribute, etc
- // from a JSO, JsType needs to be live, should have been already reachable from the AST.
- livenessAnalyzer.rescue(program.typeOracle.getInstantiatedJsoTypesViaCast());
livenessAnalyzer.setForPruning();
// SPECIAL: Immortal codegen types are never pruned
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/EnumOrdinalizerTest.java b/dev/core/test/com/google/gwt/dev/jjs/impl/EnumOrdinalizerTest.java
index 5e76689..6191146 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/impl/EnumOrdinalizerTest.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/impl/EnumOrdinalizerTest.java
@@ -1138,7 +1138,6 @@
*/
if (performCastReplacement) {
ComputeCastabilityInformation.exec(program, false);
- ComputeInstantiatedJsoInterfaces.exec(program);
ImplementCastsAndTypeChecks.exec(program, false);
}
if (runEqualityNormalizer) {
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/FullCompileTestBase.java b/dev/core/test/com/google/gwt/dev/jjs/impl/FullCompileTestBase.java
index b2baba4..3f62f68 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/impl/FullCompileTestBase.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/impl/FullCompileTestBase.java
@@ -95,7 +95,6 @@
optimizeJava();
ComputeCastabilityInformation.exec(jProgram, false);
- ComputeInstantiatedJsoInterfaces.exec(jProgram);
ImplementCastsAndTypeChecks.exec(jProgram, false);
ArrayNormalizer.exec(jProgram, false);
TypeTightener.exec(jProgram);
diff --git a/dev/core/test/com/google/gwt/dev/js/JsStackEmulatorTest.java b/dev/core/test/com/google/gwt/dev/js/JsStackEmulatorTest.java
index ae0da86..7798933 100644
--- a/dev/core/test/com/google/gwt/dev/js/JsStackEmulatorTest.java
+++ b/dev/core/test/com/google/gwt/dev/js/JsStackEmulatorTest.java
@@ -39,7 +39,6 @@
import com.google.gwt.dev.jjs.impl.ArrayNormalizer;
import com.google.gwt.dev.jjs.impl.CatchBlockNormalizer;
import com.google.gwt.dev.jjs.impl.ComputeCastabilityInformation;
-import com.google.gwt.dev.jjs.impl.ComputeInstantiatedJsoInterfaces;
import com.google.gwt.dev.jjs.impl.FullCompileTestBase;
import com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST;
import com.google.gwt.dev.jjs.impl.ImplementCastsAndTypeChecks;
@@ -281,7 +280,6 @@
// These passes are needed by GenerateJavaScriptAST.
ComputeCastabilityInformation.exec(jProgram, false);
- ComputeInstantiatedJsoInterfaces.exec(jProgram);
ImplementCastsAndTypeChecks.exec(jProgram, false);
ArrayNormalizer.exec(jProgram, false);
diff --git a/user/test/com/google/gwt/core/client/interop/JsFunctionTest.java b/user/test/com/google/gwt/core/client/interop/JsFunctionTest.java
index 509f32e..8ea042a 100644
--- a/user/test/com/google/gwt/core/client/interop/JsFunctionTest.java
+++ b/user/test/com/google/gwt/core/client/interop/JsFunctionTest.java
@@ -198,6 +198,7 @@
Object object = createFunction();
assertTrue(object instanceof MyJsFunctionInterface);
assertTrue(object instanceof MyJsFunctionIdentityInterface);
+ assertTrue(object instanceof MyJsFunctionWithOnlyInstanceofReference);
assertFalse(object instanceof MyJsFunctionInterfaceImpl);
assertTrue(object instanceof ElementLikeJsInterface);
}
@@ -206,6 +207,7 @@
Object object = createObject();
assertFalse(object instanceof MyJsFunctionInterface);
assertFalse(object instanceof MyJsFunctionIdentityInterface);
+ assertFalse(object instanceof MyJsFunctionWithOnlyInstanceofReference);
assertFalse(object instanceof MyJsFunctionInterfaceImpl);
assertTrue(object instanceof ElementLikeJsInterface);
}
@@ -215,6 +217,7 @@
assertTrue(object instanceof MyJsFunctionInterface);
assertTrue(object instanceof MyJsFunctionInterfaceImpl);
assertTrue(object instanceof MyJsFunctionIdentityInterface);
+ assertTrue(object instanceof MyJsFunctionWithOnlyInstanceofReference);
assertTrue(object instanceof ElementLikeJsInterface);
}
diff --git a/user/test/com/google/gwt/core/client/interop/JsTypeArrayTest.java b/user/test/com/google/gwt/core/client/interop/JsTypeArrayTest.java
index 113c2eb..fecc5c5 100644
--- a/user/test/com/google/gwt/core/client/interop/JsTypeArrayTest.java
+++ b/user/test/com/google/gwt/core/client/interop/JsTypeArrayTest.java
@@ -49,8 +49,7 @@
@JsProperty int getId();
}
- // TODO: Fix CFA
- public void _disabled_testJsTypeArray_returnFromNativeWithACall() {
+ public void testJsTypeArray_returnFromNativeWithACall() {
SimpleJsTypeReturnFromNativeWithACall[] array = returnJsTypeWithIdsFromNative();
assertEquals(2, array[1].getId());
}
@@ -67,8 +66,8 @@
public SimpleJsTypeAsAField[] arrayField;
}
- // TODO: Fix CFA
- public void _disabled_testJsTypeArray_asAField() {
+ // TODO(rluble): Needs fixes in ImlementCastsAndTypeChecks, ArrayNormalizer and maybe type oracle.
+ public void __disabled__testJsTypeArray_asAField() {
SimpleJsTypeAsAFieldHolder holder = new SimpleJsTypeAsAFieldHolder();
fillArrayField(holder);
SimpleJsTypeAsAField[] array = holder.arrayField;
@@ -109,8 +108,8 @@
@JsProperty int getId();
}
- // TODO: Fix CFA
- public void _disabled_testJsType3DimArray_castedFromNativeWithACall() {
+ // TODO(rluble): Needs fixes in ImlementCastsAndTypeChecks, ArrayNormalizer and maybe type oracle.
+ public void __disabled__testJsType3DimArray_castedFromNativeWithACall() {
SimpleJsTypeReturnForMultiDimArray[][][] array =
(SimpleJsTypeReturnForMultiDimArray[][][]) returnJsType3DimFromNative();
assertEquals(1, array.length);
@@ -123,16 +122,14 @@
return [ [ [{id:1}, {id:2}, {}], [] ] ];
}-*/;
- // TODO: Fix CFA
- public void _disabled_testString3DimArray_castedFromNative() {
- String[][][] array = (String[][][]) returnString3DimFromNative();
- assertEquals(1, array.length);
- assertEquals(2, array[0].length);
- assertEquals(3, array[0][0].length);
- assertEquals("1", array[0][0][0]);
+ // TODO(rluble): Needs fixes in ImlementCastsAndTypeChecks, ArrayNormalizer and maybe type oracle.
+ public void __disabled__testObjectArray_castedFromNative() {
+ Object[] array = (Object[]) returnObjectArrayFromNative();
+ assertEquals(3, array.length);
+ assertEquals("1", array[0]);
}
- private native Object returnString3DimFromNative() /*-{
- return [ [ ["1", "2", "3"], [] ] ];
+ private native Object returnObjectArrayFromNative() /*-{
+ return ["1", "2", "3"];
}-*/;
}
diff --git a/user/test/com/google/gwt/core/client/interop/MyJsFunctionWithOnlyInstanceofReference.java b/user/test/com/google/gwt/core/client/interop/MyJsFunctionWithOnlyInstanceofReference.java
new file mode 100644
index 0000000..4c5b951
--- /dev/null
+++ b/user/test/com/google/gwt/core/client/interop/MyJsFunctionWithOnlyInstanceofReference.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2015 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.JsFunction;
+
+/**
+ * A functional interface annotated by JsFunction that is only referenced by instanceof.
+ */
+@JsFunction
+public interface MyJsFunctionWithOnlyInstanceofReference {
+ int foo(int a);
+}
diff --git a/user/test/com/google/gwt/dev/jjs/test/JsoTest.java b/user/test/com/google/gwt/dev/jjs/test/JsoTest.java
index 64ef561..d6a5f4a 100644
--- a/user/test/com/google/gwt/dev/jjs/test/JsoTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/JsoTest.java
@@ -533,6 +533,15 @@
}
}
+ static class MyJSO extends JavaScriptObject {
+ protected MyJSO() {
+ }
+
+ public final boolean equalMethod(Object o) {
+ return this == o;
+ }
+ }
+
public void testEquality() {
JavaScriptObject jso = makeJSO();
assertEquals(jso, jso);
@@ -543,6 +552,11 @@
jso2 = returnMe(jso);
assertEquals(jso, jso2);
+
+ MyJSO jso3 = (MyJSO) makeJSO();
+ MyJSO jso4 = (MyJSO) makeJSO();
+ assertTrue(jso3.equalMethod(jso3));
+ assertFalse(jso3.equalMethod(jso4));
}
public void testGenericsJsos() {