Refactor property/member identification for JsInterop.

Change-Id: I7e4d2bc0675bb08f013c69993c1fd602f12a8ca1
diff --git a/dev/core/src/com/google/gwt/dev/javac/JsInteropUtil.java b/dev/core/src/com/google/gwt/dev/javac/JsInteropUtil.java
index f04627a..87dd845 100644
--- a/dev/core/src/com/google/gwt/dev/javac/JsInteropUtil.java
+++ b/dev/core/src/com/google/gwt/dev/javac/JsInteropUtil.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.dev.javac;
 
+import com.google.gwt.dev.jjs.ast.HasJsInfo.JsMemberType;
 import com.google.gwt.dev.jjs.ast.JClassType;
 import com.google.gwt.dev.jjs.ast.JConstructor;
 import com.google.gwt.dev.jjs.ast.JDeclaredType;
@@ -22,9 +23,7 @@
 import com.google.gwt.dev.jjs.ast.JInterfaceType;
 import com.google.gwt.dev.jjs.ast.JMember;
 import com.google.gwt.dev.jjs.ast.JMethod;
-import com.google.gwt.dev.jjs.ast.JMethod.JsPropertyAccessorType;
 import com.google.gwt.dev.jjs.ast.JPrimitiveType;
-import com.google.gwt.thirdparty.guava.common.base.Strings;
 
 import org.eclipse.jdt.internal.compiler.ast.Annotation;
 import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding;
@@ -46,7 +45,7 @@
   public static void maybeSetJsInteropProperties(JDeclaredType type, Annotation... annotations) {
     AnnotationBinding jsType = JdtUtil.getAnnotation(annotations, JSTYPE_CLASS);
     String namespace = maybeGetJsNamespace(annotations);
-    String exportName = maybeGetJsExportName(annotations, "");
+    String exportName = maybeGetJsExportName(annotations);
     String jsPrototype = JdtUtil.getAnnotationParameterString(jsType, "prototype");
     boolean isJsNative = jsPrototype != null;
     if (isJsNative) {
@@ -55,7 +54,7 @@
       exportName = jsPrototype.substring(indexOf + 1);
     }
     boolean isJsType = jsType != null;
-    boolean isClassWideExport = exportName != null;
+    boolean isClassWideExport = JdtUtil.getAnnotation(annotations, JSEXPORT_CLASS) != null;
     boolean isJsFunction = JdtUtil.getAnnotation(annotations, JSFUNCTION_CLASS) != null;
     boolean canBeImplementedExternally =
         (type instanceof JInterfaceType && (isJsType || isJsFunction))
@@ -84,61 +83,60 @@
   }
 
   public static void maybeSetJsInteropProperties(JMethod method, Annotation... annotations) {
-    setJsInteropProperties(method, annotations);
-    if (JdtUtil.getAnnotation(annotations, JSPROPERTY_CLASS) != null) {
-      setJsPropertyProperties(method, null);
-    }
+    boolean isPropertyAccessor = JdtUtil.getAnnotation(annotations, JSPROPERTY_CLASS) != null;
+    setJsInteropProperties(method, annotations, isPropertyAccessor);
   }
 
   public static void maybeSetJsInteropPropertiesNew(JMethod method, Annotation... annotations) {
-    AnnotationBinding annotation = getInteropAnnotation(annotations, "JsMethod");
     if (getInteropAnnotation(annotations, "JsOverlay") != null) {
       method.setJsOverlay();
     }
 
+    AnnotationBinding annotation = getInteropAnnotation(annotations, "JsMethod");
     if (annotation == null) {
       annotation = getInteropAnnotation(annotations, "JsConstructor");
     }
-    AnnotationBinding jsPropertyAnnotation = getInteropAnnotation(annotations, "JsProperty");
     if (annotation == null) {
-      annotation = jsPropertyAnnotation;
+      annotation = getInteropAnnotation(annotations, "JsProperty");
     }
-    setJsInteropPropertiesNew(method, annotations, annotation);
-    if (jsPropertyAnnotation != null) {
-      setJsPropertyProperties(
-          method, JdtUtil.getAnnotationParameterString(jsPropertyAnnotation, "name"));
-    }
+
+    boolean isPropertyAccessor = getInteropAnnotation(annotations, "JsProperty") != null;
+    setJsInteropPropertiesNew(method, annotations, annotation, isPropertyAccessor);
   }
 
   public static void maybeSetJsInteropProperties(JField field, Annotation... annotations) {
-    setJsInteropProperties(field, annotations);
+    setJsInteropProperties(field, annotations, false);
   }
 
   public static void maybeSetJsInteropPropertiesNew(JField field, Annotation... annotations) {
     AnnotationBinding annotation = getInteropAnnotation(annotations, "JsProperty");
-    setJsInteropPropertiesNew(field, annotations, annotation);
+    setJsInteropPropertiesNew(field, annotations, annotation, false);
   }
 
-  private static void setJsInteropProperties(JMember member, Annotation... annotations) {
-    String namespace = maybeGetJsNamespace(annotations);
-    String exportName = maybeGetJsExportName(annotations, computeName(member));
-    member.setJsMemberInfo(namespace, exportName, exportName != null);
+  private static void setJsInteropProperties(
+      JMember member, Annotation[] annotations, boolean isPropertyAccessor) {
+    boolean hasExport = JdtUtil.getAnnotation(annotations, JSEXPORT_CLASS) != null;
 
     /* Apply class wide JsInterop annotations */
 
     boolean ignore = JdtUtil.getAnnotation(annotations, JSNOEXPORT_CLASS) != null;
-    if (ignore || (!member.isPublic() && !isNativeConstructor(member)) || exportName != null) {
+    if (ignore || (!member.isPublic() && !isNativeConstructor(member)) || !hasExport) {
       return;
     }
 
+    String namespace = maybeGetJsNamespace(annotations);
+    String exportName = maybeGetJsExportName(annotations);
+    JsMemberType memberType = getJsMemberType(member, isPropertyAccessor);
+    member.setJsMemberInfo(memberType, namespace, exportName, hasExport);
+
     JDeclaredType enclosingType = member.getEnclosingType();
 
     if (enclosingType.isJsType() && member.needsDynamicDispatch()) {
-      member.setJsMemberInfo(namespace, computeName(member), true);
+      member.setJsMemberInfo(memberType, namespace, exportName, true);
     }
 
     if (enclosingType.isClassWideExport() && !member.needsDynamicDispatch()) {
-      member.setJsMemberInfo(namespace, computeName(member), true);
+      member.setJsMemberInfo(memberType, namespace, exportName, true);
     }
   }
 
@@ -146,8 +144,8 @@
     return member instanceof JConstructor && member.getEnclosingType().isJsNative();
   }
 
-  private static void setJsInteropPropertiesNew(
-      JMember member, Annotation[] annotations, AnnotationBinding memberAnnotation) {
+  private static void setJsInteropPropertiesNew(JMember member, Annotation[] annotations,
+      AnnotationBinding memberAnnotation, boolean isAccessor) {
     if (getInteropAnnotation(annotations, "JsIgnore") != null) {
       return;
     }
@@ -159,38 +157,43 @@
 
     String namespace = JdtUtil.getAnnotationParameterString(memberAnnotation, "namespace");
     String name = JdtUtil.getAnnotationParameterString(memberAnnotation, "name");
-    member.setJsMemberInfo(namespace, name == null ? computeName(member) : name, true);
+    JsMemberType memberType = getJsMemberType(member, isAccessor);
+    member.setJsMemberInfo(memberType, namespace, name, true);
   }
 
-  private static void setJsPropertyProperties(JMethod method, String name) {
-    if (method.getParams().size() == 1 && method.getType() == JPrimitiveType.VOID) {
-      method.setJsPropertyInfo(name, JsPropertyAccessorType.SETTER);
-    } else if (method.getParams().isEmpty() && method.getType() != JPrimitiveType.VOID) {
-      method.setJsPropertyInfo(name, JsPropertyAccessorType.GETTER);
-    } else {
-      method.setJsPropertyInfo(name, JsPropertyAccessorType.UNDEFINED);
+  private static JsMemberType getJsMemberType(JMember member, boolean isPropertyAccessor) {
+    if (member instanceof JField) {
+      return JsMemberType.PROPERTY;
     }
+    if (member instanceof JConstructor) {
+      return JsMemberType.CONSTRUCTOR;
+    }
+    if (isPropertyAccessor) {
+      return getJsPropertyAccessorType((JMethod) member);
+    }
+    return JsMemberType.METHOD;
+  }
+
+  private static JsMemberType getJsPropertyAccessorType(JMethod method) {
+    if (method.getParams().size() == 1 && method.getType() == JPrimitiveType.VOID) {
+      return JsMemberType.SETTER;
+    } else if (method.getParams().isEmpty() && method.getType() != JPrimitiveType.VOID) {
+      return JsMemberType.GETTER;
+    }
+    return JsMemberType.UNDEFINED_ACCESSOR;
   }
 
   private static AnnotationBinding getInteropAnnotation(Annotation[] annotations, String name) {
     return JdtUtil.getAnnotation(annotations, "jsinterop.annotations." + name);
   }
 
-  private static String computeName(JMember member) {
-    return member instanceof JConstructor ? "" : member.getName();
-  }
-
   private static String maybeGetJsNamespace(Annotation[] annotations) {
     AnnotationBinding jsNamespace = JdtUtil.getAnnotation(annotations, JSNAMESPACE_CLASS);
     return JdtUtil.getAnnotationParameterString(jsNamespace, "value");
   }
 
-  private static String maybeGetJsExportName(Annotation[] annotations, String calculatedName) {
-    AnnotationBinding jsExport = JdtUtil.getAnnotation(annotations, JSEXPORT_CLASS);
-    if (jsExport == null) {
-      return null;
-    }
-    String value = JdtUtil.getAnnotationParameterString(jsExport, "value");
-    return Strings.isNullOrEmpty(value) ? calculatedName : value;
+  private static String maybeGetJsExportName(Annotation[] annotations) {
+    AnnotationBinding annotation = JdtUtil.getAnnotation(annotations, JSEXPORT_CLASS);
+    return JdtUtil.getAnnotationParameterString(annotation, "value");
   }
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/HasJsInfo.java b/dev/core/src/com/google/gwt/dev/jjs/ast/HasJsInfo.java
index a799f33..75f6b6a 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/HasJsInfo.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/HasJsInfo.java
@@ -15,14 +15,116 @@
  */
 package com.google.gwt.dev.jjs.ast;
 
+import com.google.gwt.dev.javac.JsInteropUtil;
+
+import java.beans.Introspector;
+
 /**
  * Abstracts JsInterop information for the AST nodes.
  */
 public interface HasJsInfo extends HasJsName {
+  /**
+   * Indicates type of JsMember.
+   */
+  enum JsMemberType {
+    /**
+     * Not a js member.
+     */
+    NONE,
+    /**
+     * A JsConstructor.
+     */
+    CONSTRUCTOR {
+      @Override
+      public String computeName(JMember member) {
+        return "";
+      }
+    },
+    /**
+     * A JsMethod.
+     */
+    METHOD,
+    /**
+     * A JsProperty.
+     */
+    PROPERTY,
+    /**
+     * A getter JsProperty accessor. Usually in the form of getX()/isX().
+     */
+    GETTER("get") {
+      @Override
+      public String computeName(JMember member) {
+        String methodName = member.getName();
+        if (startsWithCamelCase(methodName, "get")) {
+          return Introspector.decapitalize(methodName.substring(3));
+        }
+        if (startsWithCamelCase(methodName, "is")) {
+          return Introspector.decapitalize(methodName.substring(2));
+        }
+        return JsInteropUtil.INVALID_JSNAME;
+      }
+      @Override
+      public boolean isPropertyAccessor() {
+        return true;
+      }
+    },
+    /**
+     * A setter JsProperty accessor. Usually in the form of setX(x).
+     */
+    SETTER("set") {
+      @Override
+      public String computeName(JMember member) {
+        String methodName = member.getName();
+        if (startsWithCamelCase(methodName, "set")) {
+          return Introspector.decapitalize(methodName.substring(3));
+        }
+        return JsInteropUtil.INVALID_JSNAME;
+      }
+      @Override
+      public boolean isPropertyAccessor() {
+        return true;
+      }
+    },
+    /**
+     * A property accessor but doesn't match setter/getter patterns.
+     */
+    UNDEFINED_ACCESSOR;
 
-  void setJsMemberInfo(String namespace, String name, boolean exported);
+    private String accessorKey;
+
+    private JsMemberType() { }
+
+    private JsMemberType(String accessorKey) {
+      this.accessorKey = accessorKey;
+    }
+
+    public String getPropertyAccessorKey() {
+      return accessorKey;
+    }
+
+    public boolean isPropertyAccessor() {
+      return getPropertyAccessorKey() != null;
+    }
+
+    public String computeName(JMember member) {
+      return member.getName();
+    }
+
+    private static boolean startsWithCamelCase(String string, String prefix) {
+      return string.length() > prefix.length() && string.startsWith(prefix)
+          && Character.isUpperCase(string.charAt(prefix.length()));
+    }
+  }
+
+  void setJsMemberInfo(JsMemberType type, String namespace, String name, boolean exported);
+
+  JsMemberType getJsMemberType();
 
   boolean isJsNative();
 
   boolean isJsOverlay();
+
+  boolean canBeReferencedExternally();
+
+  boolean isJsInteropEntryPoint();
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java
index cb88496..535ff13 100755
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java
@@ -360,6 +360,10 @@
     return methods;
   }
 
+  public Iterable<JMember> getMembers() {
+    return Iterables.<JMember>concat(fields, methods);
+  }
+
   @Override
   public boolean isJsType() {
     return isJsType;
@@ -394,22 +398,16 @@
     return false;
   }
 
+  @Override
   public boolean canBeReferencedExternally() {
     if (isJsType()) {
       return true;
     }
-    for (JMethod method : getMethods()) {
-      if (method.canBeCalledExternally()) {
+    for (JMember member : getMembers()) {
+      if (member.canBeReferencedExternally()) {
         return true;
       }
     }
-
-    for (JField field : getFields()) {
-      if (field.canBeReferencedExternally()) {
-        return true;
-      }
-    }
-
     return false;
   }
 
@@ -613,14 +611,17 @@
     return null;
   }
 
+  @Override
   public String getJsName() {
     return Strings.isNullOrEmpty(jsName) ? getSimpleName() : jsName;
   }
 
+  @Override
   public String getJsNamespace() {
     return jsNamespace;
   }
 
+  @Override
   public String getQualifiedJsName() {
     return jsNamespace.isEmpty() ? getJsName() : jsNamespace + "." + getJsName();
   }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JField.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JField.java
index 26d48c4..27edfb0 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JField.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JField.java
@@ -80,6 +80,7 @@
   public static final JField NULL_FIELD = new JField(SourceOrigin.UNKNOWN, "nullField", null,
       JReferenceType.NULL_TYPE, false, Disposition.FINAL);
 
+  private JsMemberType jsMembertype = JsMemberType.NONE;
   private String jsName;
   private String jsNamespace;
   private boolean exported;
@@ -131,16 +132,25 @@
   }
 
   @Override
-  public void setJsMemberInfo(String namespace, String name, boolean exported) {
-    this.jsName = name;
+  public void setJsMemberInfo(
+      JsMemberType jsMembertype, String namespace, String name, boolean exported) {
+    this.jsMembertype = jsMembertype;
+    this.jsName = name != null ? name : jsMembertype.computeName(this);
     this.jsNamespace = namespace;
     this.exported = exported;
   }
 
-  public boolean isJsInteropEntryPoint() {
-    return exported && isStatic();
+  @Override
+  public JsMemberType getJsMemberType() {
+    return jsMembertype;
   }
 
+  @Override
+  public boolean isJsInteropEntryPoint() {
+    return exported && isStatic() && !isJsNative();
+  }
+
+  @Override
   public boolean canBeReferencedExternally() {
     return exported;
   }
@@ -161,10 +171,6 @@
     return false;
   }
 
-  public boolean isJsProperty() {
-    return jsName != null;
-  }
-
   @Override
   public boolean isJsNative() {
     return enclosingType.isJsNative() && jsName != null;
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JMember.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JMember.java
index d73a303..c0ab0e3 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JMember.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JMember.java
@@ -21,7 +21,7 @@
  * Abstracts class members (i.e. fields and methods).
  */
 public interface JMember extends HasEnclosingType, HasName, HasJsInfo, HasType, HasSourceInfo,
-    CanBeSetFinal, CanBeStatic, CanBeAbstract {
+    CanBeSetFinal, CanBeStatic, CanBeAbstract, CanHaveSuppressedWarnings {
 
   boolean isPublic();
 
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 278b0fc..98a574f 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
@@ -26,7 +26,6 @@
 import com.google.gwt.dev.util.collect.Lists;
 import com.google.gwt.thirdparty.guava.common.collect.Sets;
 
-import java.beans.Introspector;
 import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
@@ -40,76 +39,7 @@
 /**
  * A Java method implementation.
  */
-public class JMethod extends JNode implements JMember, CanBeAbstract, CanHaveSuppressedWarnings {
-
-  /**
-   * Indicates whether a method is a JsProperty accessor.
-   */
-  public enum JsPropertyAccessorType {
-    /**
-     * Not a property accessor.
-     */
-    NONE,
-    /**
-     * A getter property accessor. Usually in the form of getX()/isX().
-     */
-    GETTER("get") {
-      @Override
-      public String computeName(JMethod member) {
-        String methodName = member.getName();
-        if (startsWithCamelCase(methodName, "get")) {
-          return Introspector.decapitalize(methodName.substring(3));
-        }
-        if (startsWithCamelCase(methodName, "is")) {
-          return Introspector.decapitalize(methodName.substring(2));
-        }
-        return JsInteropUtil.INVALID_JSNAME;
-      }
-    },
-    /**
-     * A setter JsProperty accessor. Usually in the form of setX(x).
-     */
-    SETTER("set") {
-      @Override
-      public String computeName(JMethod member) {
-        String methodName = member.getName();
-        if (startsWithCamelCase(methodName, "set")) {
-          return Introspector.decapitalize(methodName.substring(3));
-        }
-        return JsInteropUtil.INVALID_JSNAME;
-      }
-    },
-    /**
-     * A property accessor but doesn't match setter/getter patterns.
-     */
-    UNDEFINED;
-
-    private String key;
-
-    JsPropertyAccessorType() {
-    }
-
-    JsPropertyAccessorType(String key) {
-      this.key = key;
-    }
-
-    public String getKey() {
-      return key;
-    }
-
-    public boolean isPropertyAccessor() {
-      return key != null;
-    }
-
-    public String computeName(JMethod method) {
-      return method.getName();
-    }
-
-    private static boolean startsWithCamelCase(String string, String prefix) {
-      return string.length() > prefix.length() && string.startsWith(prefix)
-          && Character.isUpperCase(string.charAt(prefix.length()));
-    }
-  }
+public class JMethod extends JNode implements JMember, CanBeAbstract {
 
   public static final Comparator<JMethod> BY_SIGNATURE_COMPARATOR = new Comparator<JMethod>() {
     @Override
@@ -121,7 +51,7 @@
   private String jsName;
   private boolean exported;
   private String jsNamespace;
-  private JsPropertyAccessorType jsPropertyType = JsPropertyAccessorType.NONE;
+  private JsMemberType jsMemberType = JsMemberType.NONE;
   private Specialization specialization;
   private InliningMode inliningMode = InliningMode.NORMAL;
   private boolean preventDevirtualization = false;
@@ -132,17 +62,20 @@
   private boolean isJsOverlay = false;
 
   @Override
-  public void setJsMemberInfo(String namespace, String name, boolean exported) {
-    this.jsName = name;
+  public void setJsMemberInfo(JsMemberType type, String namespace, String name, boolean exported) {
+    this.jsMemberType = type;
+    this.jsName = name != null ? name : type.computeName(this);
     this.jsNamespace = namespace;
     this.exported = exported;
   }
 
+  @Override
   public boolean isJsInteropEntryPoint() {
     return exported && !needsDynamicDispatch() && !isJsNative();
   }
 
-  public boolean canBeCalledExternally() {
+  @Override
+  public boolean canBeReferencedExternally() {
     if (exported || isJsFunctionMethod()) {
       return true;
     }
@@ -209,58 +142,38 @@
   }
 
   /**
-   * Returns {@code true} if this method is the first JsMethod in the method hierarchy that exposes
-   * an existing non-JsMethod inside a class.
+   * Returns {@code true} if this method is the first JsMember in the method hierarchy that exposes
+   * an existing non-JsMember inside a class.
    */
-  public boolean exposesNonJsMethod() {
-    if (isInterfaceMethod() || !isOrOverridesJsMethod()) {
+  public boolean exposesNonJsMember() {
+    if (isInterfaceMethod() || getJsMemberType() == JsMemberType.NONE) {
       return false;
     }
 
-    boolean hasNonJsMethodParent = false;
+    boolean hasNonJsMemberParent = false;
     for (JMethod overriddenMethod : overriddenMethods) {
-      if (!overriddenMethod.isOrOverridesJsMethod()) {
-        hasNonJsMethodParent = true;
+      if (overriddenMethod.getJsMemberType() == JsMemberType.NONE) {
+        hasNonJsMemberParent = true;
       }
-      if (overriddenMethod.exposesNonJsMethod()) {
+      if (overriddenMethod.exposesNonJsMember()) {
         return false; // some other method already exposed this method.
       }
     }
 
-    return hasNonJsMethodParent;
+    return hasNonJsMemberParent;
   }
 
-  public boolean isOrOverridesJsMethod() {
-    if (jsName != null) {
-      return true;
+  @Override
+  public JsMemberType getJsMemberType() {
+    if (jsMemberType != JsMemberType.NONE) {
+      return jsMemberType;
     }
     for (JMethod overriddenMethod : getOverriddenMethods()) {
-      if (overriddenMethod.jsName != null) {
-        return true;
+      if (overriddenMethod.jsMemberType != JsMemberType.NONE) {
+        return overriddenMethod.jsMemberType;
       }
     }
-    return false;
-  }
-
-  public void setJsPropertyInfo(String jsName, JsPropertyAccessorType jsPropertyType) {
-    this.jsName = jsName != null ? jsName : jsPropertyType.computeName(this);
-    this.jsPropertyType = jsPropertyType;
-  }
-
-  public JsPropertyAccessorType getJsPropertyAccessorType() {
-    if (isJsPropertyAccessor()) {
-      return jsPropertyType;
-    }
-    for (JMethod overriddenMethod : getOverriddenMethods()) {
-      if (overriddenMethod.isJsPropertyAccessor()) {
-        return overriddenMethod.jsPropertyType;
-      }
-    }
-    return JsPropertyAccessorType.NONE;
-  }
-
-  private boolean isJsPropertyAccessor() {
-    return jsPropertyType != JsPropertyAccessorType.NONE;
+    return JsMemberType.NONE;
   }
 
   private boolean isJsFunctionMethod() {
@@ -284,6 +197,7 @@
     return (isAbstract || body == null) && jsName != null;
   }
 
+  @Override
   public boolean isJsOverlay() {
     return isJsOverlay || (getEnclosingType() != null && getEnclosingType().isJsoType());
   }
@@ -690,6 +604,7 @@
     }
   }
 
+  @Override
   public boolean isPrivate() {
     return access == AccessModifier.PRIVATE.ordinal();
   }
@@ -704,6 +619,7 @@
     return isStatic;
   }
 
+  @Override
   public boolean isSynthetic() {
     return isSynthetic;
   }
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 8b2710a..9d9faa4 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
@@ -616,7 +616,7 @@
           // Returning from this method passes a value from JavaScript into Java.
           maybeRescueJavaScriptObjectPassingIntoJava(method.getType());
         }
-        if (method.canBeCalledExternally() || method.canBeImplementedExternally()) {
+        if (method.canBeReferencedExternally() || method.canBeImplementedExternally()) {
           for (JParameter param : method.getParams()) {
             // Parameters in JsExport, JsType, JsFunction methods should not be pruned in order to
             // keep the API intact.
@@ -683,7 +683,7 @@
       JDeclaredType declaredType = (JDeclaredType) type;
 
       for (JMethod method : declaredType.getMethods()) {
-        if (method.canBeCalledExternally()) {
+        if (method.canBeReferencedExternally()) {
           rescue(method);
         }
       }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowRecorder.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowRecorder.java
index ee30b35..d13ed82 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowRecorder.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowRecorder.java
@@ -137,7 +137,7 @@
           overriddenMethodName);
     }
 
-    if (x.canBeCalledExternally()) {
+    if (x.canBeReferencedExternally()) {
       stringAnalyzableTypeEnvironment.recordExportedMethodInType(currentMethodName, typeName);
     }
 
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 07552c3..9cbda78 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
@@ -30,6 +30,7 @@
 import com.google.gwt.dev.jjs.SourceOrigin;
 import com.google.gwt.dev.jjs.ast.Context;
 import com.google.gwt.dev.jjs.ast.HasEnclosingType;
+import com.google.gwt.dev.jjs.ast.HasJsInfo.JsMemberType;
 import com.google.gwt.dev.jjs.ast.HasName;
 import com.google.gwt.dev.jjs.ast.JAbstractMethodBody;
 import com.google.gwt.dev.jjs.ast.JArrayLength;
@@ -64,7 +65,6 @@
 import com.google.gwt.dev.jjs.ast.JLocalRef;
 import com.google.gwt.dev.jjs.ast.JMember;
 import com.google.gwt.dev.jjs.ast.JMethod;
-import com.google.gwt.dev.jjs.ast.JMethod.JsPropertyAccessorType;
 import com.google.gwt.dev.jjs.ast.JMethodBody;
 import com.google.gwt.dev.jjs.ast.JMethodCall;
 import com.google.gwt.dev.jjs.ast.JNameOf;
@@ -241,7 +241,7 @@
         jsName = topScope.declareName(mangleName(x), x.getName());
       } else {
         jsName =
-            x.isJsProperty()
+            x.getJsMemberType() != JsMemberType.NONE
                 ? scopeStack.peek().declareUnobfuscatableName(x.getJsName())
                 : scopeStack.peek().declareName(mangleName(x), x.getName());
       }
@@ -366,9 +366,8 @@
           } else if (x.isPackagePrivate()) {
             polyName = interfaceScope.declareName(mangleNameForPackagePrivatePoly(x), name);
           } else {
-            boolean isJsMethod = x.isOrOverridesJsMethod();
             polyName =
-                isJsMethod
+                x.getJsMemberType() != JsMemberType.NONE
                     ? interfaceScope.declareUnobfuscatableName(x.getJsName())
                     : interfaceScope.declareName(mangleNameForPoly(x), name);
           }
@@ -886,7 +885,7 @@
         List<JsExpression> args, SourceInfo sourceInfo) {
       JsNameRef methodName = createStaticReference(method, sourceInfo);
       JsExpression result = JsUtils.createInvocationOrPropertyAccess(
-          sourceInfo, method.getJsPropertyAccessorType(), methodName, args);
+          sourceInfo, method.getJsMemberType(), methodName, args);
       return JsUtils.createCommaExpression(unnecessaryQualifier, result);
     }
 
@@ -945,10 +944,8 @@
     private JsExpression dispatchToInstanceMethod(
         JsExpression instance, JMethod method, List<JsExpression> args, SourceInfo sourceInfo) {
       JsNameRef reference = polymorphicNames.get(method).makeQualifiedRef(sourceInfo, instance);
-
-      JsPropertyAccessorType propertyAccessorType = method.getJsPropertyAccessorType();
-      return JsUtils
-          .createInvocationOrPropertyAccess(sourceInfo, propertyAccessorType, reference, args);
+      return JsUtils.createInvocationOrPropertyAccess(
+          sourceInfo, method.getJsMemberType(), reference, args);
     }
 
     @Override
@@ -1456,24 +1453,18 @@
           exportedMembersByExportName.put(type.getQualifiedJsName(), type);
         }
 
-        for (JMethod method : type.getMethods()) {
-          if (method.isJsInteropEntryPoint()) {
-            exportedMembersByExportName.put(method.getQualifiedJsName(), method);
-          }
-        }
-
-        for (JField field : type.getFields()) {
-          if (field.isJsInteropEntryPoint()) {
-            if (!field.isFinal()) {
-              // TODO(rluble): move waring to JsInteropRestrictionChecker.
+        for (JMember member : type.getMembers()) {
+          if (member.isJsInteropEntryPoint()) {
+            if (member.getJsMemberType() == JsMemberType.PROPERTY && !member.isFinal()) {
+              // TODO(goktug): Remove the warning when we export via Object.defineProperty
               logger.log(
                   TreeLogger.Type.WARN,
                   "Exporting effectively non-final field "
-                      + field.getQualifiedName()
+                      + member.getQualifiedName()
                       + ". Due to the way exporting works, the value of the"
                       + " exported field will not be reflected across Java/JavaScript border.");
             }
-            exportedMembersByExportName.put(field.getQualifiedJsName(), field);
+            exportedMembersByExportName.put(member.getQualifiedJsName(), member);
           }
         }
       }
@@ -2221,7 +2212,7 @@
     }
 
     private void generatePrototypeAssignment(JMethod method, JsName name, JsExpression rhs) {
-      generatePrototypeAssignment(method, name, rhs, method.getJsPropertyAccessorType());
+      generatePrototypeAssignment(method, name, rhs, method.getJsMemberType());
     }
 
      /**
@@ -2229,11 +2220,11 @@
       * created for {@code method}.
       */
     private void generatePrototypeAssignment(JMethod method, JsName name, JsExpression rhs,
-        JsPropertyAccessorType accessorType) {
+        JsMemberType memberType) {
       SourceInfo sourceInfo = method.getSourceInfo();
       JsNameRef prototypeQualifierOf = getPrototypeQualifierOf(method);
       JsNameRef lhs = name.makeQualifiedRef(sourceInfo, prototypeQualifierOf);
-      switch (accessorType) {
+      switch (memberType) {
         case GETTER:
         case SETTER:
           emitPropertyImplementation(method, prototypeQualifierOf, name.makeRef(sourceInfo), rhs);
@@ -2260,7 +2251,7 @@
                // {name: {get: function() { ..... }} or {set : function (v) {....}}}
               .add(name, JsObjectLiteral.builder(sourceInfo)
                       // {get: function() { ..... }} or {set : function (v) {....}}
-                  .add(method.getJsPropertyAccessorType().getKey(), methodDefinitionStatement)
+                  .add(method.getJsMemberType().getPropertyAccessorKey(), methodDefinitionStatement)
                   .build())
               .build();
 
@@ -2282,7 +2273,7 @@
       JsName polyName = polymorphicNames.get(method);
       JsExpression bridge = JsUtils.createBridge(method, polyName, topScope);
       // Aliases are never property accessors.
-      generatePrototypeAssignment(method, alias, bridge, JsPropertyAccessorType.NONE);
+      generatePrototypeAssignment(method, alias, bridge, JsMemberType.NONE);
     }
 
     private JsExprStmt outputDisplayName(JsNameRef function, JMethod method) {
@@ -2332,7 +2323,7 @@
         generatePrototypeAssignment(method, polymorphicNames.get(method), functionDefinition);
       }
 
-      if (method.exposesNonJsMethod()) {
+      if (method.exposesNonJsMember()) {
         JsName internalMangledName = interfaceScope.declareName(mangleNameForPoly(method),
             method.getName());
         generatePrototypeDefinitionAlias(method, internalMangledName);
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/JjsUtils.java b/dev/core/src/com/google/gwt/dev/jjs/impl/JjsUtils.java
index 024cf91..73fbfea 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/JjsUtils.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/JjsUtils.java
@@ -386,7 +386,7 @@
         });
     // A synthetic accidental  override is unnecessary iff it retains the same property
     // name (polyname) as the the concrete method it overrides.
-    return overridesConcreteMethod && !method.exposesNonJsMethod();
+    return overridesConcreteMethod && !method.exposesNonJsMember();
   }
 
   /**
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/JsInteropRestrictionChecker.java b/dev/core/src/com/google/gwt/dev/jjs/impl/JsInteropRestrictionChecker.java
index c0f8ab6..f1dbe2b 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/JsInteropRestrictionChecker.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/JsInteropRestrictionChecker.java
@@ -21,7 +21,9 @@
 import com.google.gwt.dev.jjs.HasSourceInfo;
 import com.google.gwt.dev.jjs.ast.CanHaveSuppressedWarnings;
 import com.google.gwt.dev.jjs.ast.Context;
+import com.google.gwt.dev.jjs.ast.HasJsInfo.JsMemberType;
 import com.google.gwt.dev.jjs.ast.HasJsName;
+import com.google.gwt.dev.jjs.ast.HasType;
 import com.google.gwt.dev.jjs.ast.JClassType;
 import com.google.gwt.dev.jjs.ast.JConstructor;
 import com.google.gwt.dev.jjs.ast.JDeclarationStatement;
@@ -34,7 +36,6 @@
 import com.google.gwt.dev.jjs.ast.JInterfaceType;
 import com.google.gwt.dev.jjs.ast.JMember;
 import com.google.gwt.dev.jjs.ast.JMethod;
-import com.google.gwt.dev.jjs.ast.JMethod.JsPropertyAccessorType;
 import com.google.gwt.dev.jjs.ast.JMethodBody;
 import com.google.gwt.dev.jjs.ast.JMethodCall;
 import com.google.gwt.dev.jjs.ast.JParameter;
@@ -226,50 +227,32 @@
     return call.getTarget().equals(targetCtor);
   }
 
-  private void checkField(Map<String, JsMember> localNames, JField field) {
-    if (field.getEnclosingType().isJsNative()) {
-      checkMemberOfNativeJsType(field);
+  private void checkMember(Map<String, JsMember> localNames, JMember member) {
+    if (member.getEnclosingType().isJsNative()) {
+      checkMemberOfNativeJsType(member);
     }
 
-    checkUnusableByJs(field);
-
-    if (!field.isJsProperty()) {
+    if (member.isJsOverlay()) {
+      checkJsOverlay((JMethod) member);
       return;
     }
 
-    checkMemberQualifiedJsName(field);
-
-    if (field.needsDynamicDispatch()) {
-      checkLocalName(localNames, field);
-    } else if (!field.isJsNative()) {
-      checkGlobalName(field);
-    }
-  }
-
-  private void checkMethod(Map<String, JsMember> localNames, JMethod method) {
-    if (method.getEnclosingType().isJsNative()) {
-      checkMemberOfNativeJsType(method);
+    if (member.canBeReferencedExternally()) {
+      checkUnusableByJs(member);
     }
 
-    if (method.isJsOverlay()) {
-      checkJsOverlay(method);
+    if (member.getJsMemberType() == JsMemberType.NONE) {
       return;
     }
 
-    checkUnusableByJs(method);
+    checkMemberQualifiedJsName(member);
 
-    if (!method.isOrOverridesJsMethod()) {
-      return;
+    if (isCheckedLocalName(member)) {
+      checkLocalName(localNames, member);
     }
 
-    checkMemberQualifiedJsName(method);
-
-    if (method.needsDynamicDispatch()) {
-      if (!isSyntheticBridgeMethod(method)) {
-        checkLocalName(localNames, method);
-      }
-    } else if (!method.isJsNative()) {
-      checkGlobalName(method);
+    if (isCheckedGlobalName(member)) {
+      checkGlobalName(member);
     }
   }
 
@@ -286,8 +269,7 @@
 
   private void checkLocalName(Map<String, JsMember> localNames, JMember member) {
     if (member.getJsName().equals(JsInteropUtil.INVALID_JSNAME)) {
-      if (member instanceof JMethod
-          && ((JMethod) member).getJsPropertyAccessorType().isPropertyAccessor()) {
+      if (member.getJsMemberType().isPropertyAccessor()) {
         logError(
             member,
             "JsProperty %s should either follow Java Bean naming conventions or "
@@ -319,27 +301,23 @@
         getMemberDescription(member), getMemberDescription(oldMember.member), member.getJsName());
   }
 
-  private void checkJsPropertyAccessor(JMember x, JsMember newMember) {
-    if (!(x instanceof JMethod)) {
-      return;
-    }
-    JMethod method = (JMethod) x;
-
-    if (method.getJsPropertyAccessorType() == JsPropertyAccessorType.UNDEFINED) {
-      logError(method, "JsProperty %s should have a correct setter or getter signature.",
-          getMemberDescription(method));
+  private void checkJsPropertyAccessor(JMember member, JsMember newMember) {
+    if (member.getJsMemberType() == JsMemberType.UNDEFINED_ACCESSOR) {
+      logError(member, "JsProperty %s should have a correct setter or getter signature.",
+          getMemberDescription(member));
     }
 
-    if (method.getJsPropertyAccessorType() == JsPropertyAccessorType.GETTER) {
-      if (method.getType() != JPrimitiveType.BOOLEAN && method.getName().startsWith("is")) {
-        logError(method, "JsProperty %s cannot have a non-boolean return.",
-            getMemberDescription(method));
+    if (member.getJsMemberType() == JsMemberType.GETTER) {
+      if (member.getType() != JPrimitiveType.BOOLEAN && member.getName().startsWith("is")) {
+        logError(member, "JsProperty %s cannot have a non-boolean return.",
+            getMemberDescription(member));
       }
     }
 
     if (newMember.setter != null && newMember.getter != null) {
-      if (newMember.getter.getType() != newMember.setter.getParams().get(0).getType()) {
-        logError(method, "JsProperty setter %s and getter %s cannot have inconsistent types.",
+      List<JParameter> setterParams = ((JMethod) newMember.setter).getParams();
+      if (newMember.getter.getType() != setterParams.get(0).getType()) {
+        logError(member, "JsProperty setter %s and getter %s cannot have inconsistent types.",
             getMemberDescription(newMember.setter), getMemberDescription(newMember.getter));
       }
     }
@@ -447,7 +425,7 @@
       @Override
       public void endVisit(JMethodCall x, Context ctx) {
         JMethod target = x.getTarget();
-        if (x.isStaticDispatchOnly() && target.getJsPropertyAccessorType().isPropertyAccessor()) {
+        if (x.isStaticDispatchOnly() && target.getJsMemberType().isPropertyAccessor()) {
           logError(x, "Cannot call property accessor %s via super.",
               getMemberDescription(target));
         }
@@ -603,62 +581,45 @@
     }
 
     Map<String, JsMember> localNames = collectNames(type.getSuperClass());
-
-    for (JField field : type.getFields()) {
-      checkField(localNames, field);
-    }
-    for (JMethod method : type.getMethods()) {
-      checkMethod(localNames, method);
+    for (JMember member : type.getMembers()) {
+      checkMember(localNames, member);
     }
   }
 
-  private void checkUnusableByJs(JMethod method) {
-    if (!method.canBeCalledExternally() || isUnusableByJsSuppressed(method.getEnclosingType())
-        || isUnusableByJsSuppressed(method)) {
-      return;
-    }
-    // check parameters.
-    for (JParameter parameter : method.getParams()) {
-      if (!parameter.getType().canBeReferencedExternally()
-          && !isUnusableByJsSuppressed(parameter)) {
-        logWarning(
-            parameter,
-            "[unusable-by-js] Type of parameter '%s' in method %s is not usable by but exposed to"
-                + " JavaScript.",
-            parameter.getName(), getMemberDescription(method));
+  private void checkUnusableByJs(JMember member) {
+    logIfUnusableByJs(member, member instanceof JField ? "Type of" : "Return type of", member);
+
+    if (member instanceof JMethod) {
+      for (JParameter parameter : ((JMethod) member).getParams()) {
+        String prefix = String.format("Type of parameter '%s' in", parameter.getName());
+        logIfUnusableByJs(parameter, prefix, member);
       }
     }
-    // check return type.
-    if (!method.getType().canBeReferencedExternally()) {
-      logWarning(
-          method, "[unusable-by-js] Return type of %s is not usable by but exposed to JavaScript.",
-          getMemberDescription(method));
-    }
   }
 
-  private void checkUnusableByJs(JField field) {
-    if (!field.canBeReferencedExternally() || isUnusableByJsSuppressed(field.getEnclosingType())
-        || isUnusableByJsSuppressed(field)) {
+  private <T extends HasType & CanHaveSuppressedWarnings> void logIfUnusableByJs(
+      T hasType, String prefix, JMember x) {
+    if (hasType.getType().canBeReferencedExternally()) {
       return;
     }
-    if (!field.getType().canBeReferencedExternally()) {
-      logWarning(
-          field, "[unusable-by-js] Type of field '%s' in type '%s' is not usable by but exposed to "
-              + "JavaScript.",
-          field.getName(), JjsUtils.getReadableDescription(field.getEnclosingType()));
+    if (isUnusableByJsSuppressed(x.getEnclosingType()) || isUnusableByJsSuppressed(x)
+        || isUnusableByJsSuppressed(hasType)) {
+      return;
     }
+    logWarning(x, "[unusable-by-js] %s %s is not usable by but exposed to JavaScript.", prefix,
+        getMemberDescription(x));
   }
 
   private static class JsMember {
     private JMember member;
-    private JMethod setter;
-    private JMethod getter;
+    private JMember setter;
+    private JMember getter;
 
     public JsMember(JMember member) {
       this.member = member;
     }
 
-    public JsMember(JMethod member, JMethod setter, JMethod getter) {
+    public JsMember(JMember member, JMember setter, JMember getter) {
       this.member = member;
       this.setter = setter;
       this.getter = getter;
@@ -679,24 +640,23 @@
     }
 
     LinkedHashMap<String, JsMember> memberByLocalMemberNames = collectNames(type.getSuperClass());
-
-    for (JField field : type.getFields()) {
-      if (!field.isJsProperty() || !field.needsDynamicDispatch()) {
-        continue;
+    for (JMember member : type.getMembers()) {
+      if (isCheckedLocalName(member)) {
+        updateJsMembers(memberByLocalMemberNames, member);
       }
-      updateJsMembers(memberByLocalMemberNames, field);
-    }
-    for (JMethod method : type.getMethods()) {
-      if (!method.isOrOverridesJsMethod() || !method.needsDynamicDispatch()
-          || isSyntheticBridgeMethod(method)) {
-        continue;
-      }
-      updateJsMembers(memberByLocalMemberNames, method);
     }
     return memberByLocalMemberNames;
   }
 
-  private boolean isSyntheticBridgeMethod(JMethod method) {
+  private boolean isCheckedLocalName(JMember method) {
+    return method.needsDynamicDispatch() && method.getJsMemberType() != JsMemberType.NONE
+        && !isSyntheticBridgeMethod(method);
+  }
+
+  private boolean isSyntheticBridgeMethod(JMember member) {
+    if (!(member instanceof JMethod)) {
+      return false;
+    }
     // A name slot taken up by a synthetic method, such as a bridge method for a generic method,
     // is not the fault of the user and so should not be reported as an error. JS generation
     // should take responsibility for ensuring that only the correct method version (in this
@@ -704,7 +664,11 @@
     // (such as an accidental override forwarding method that occurs when a JsType interface
     // starts exposing a method in class B that is only ever implemented in its parent class A)
     // though should be checked since they are exported and do take up an name slot.
-    return method.isSynthetic() && !method.isForwarding();
+    return member.isSynthetic() && !((JMethod) member).isForwarding();
+  }
+
+  private boolean isCheckedGlobalName(JMember member) {
+    return !member.needsDynamicDispatch() && !member.isJsNative();
   }
 
   private void updateJsMembers(Map<String, JsMember> memberByLocalMemberNames, JMember member) {
@@ -714,45 +678,41 @@
   }
 
   private JsMember createOrUpdateJsMember(JsMember jsMember, JMember member) {
-    if (member instanceof JField) {
-      return new JsMember(member);
-    }
-
-    JMethod method = (JMethod) member;
-    switch (method.getJsPropertyAccessorType()) {
+    switch (member.getJsMemberType()) {
       case GETTER:
         if (jsMember != null && jsMember.isPropertyAccessor()) {
-          if (jsMember.getter == null || overrides(method, jsMember.getter)) {
-            jsMember.getter = method;
-            jsMember.member = method;
+          if (jsMember.getter == null || overrides(member, jsMember.getter)) {
+            jsMember.getter = member;
+            jsMember.member = member;
             return jsMember;
           }
         }
-        return new JsMember(method, jsMember == null ? null : jsMember.setter, method);
+        return new JsMember(member, jsMember == null ? null : jsMember.setter, member);
       case SETTER:
         if (jsMember != null && jsMember.isPropertyAccessor()) {
-          if (jsMember.setter == null || overrides(method, jsMember.setter)) {
-            jsMember.setter = method;
-            jsMember.member = method;
+          if (jsMember.setter == null || overrides(member, jsMember.setter)) {
+            jsMember.setter = member;
+            jsMember.member = member;
             return jsMember;
           }
         }
-        return new JsMember(method, method, jsMember == null ? null : jsMember.getter);
+        return new JsMember(member, member, jsMember == null ? null : jsMember.getter);
       default:
         if (jsMember != null) {
-          if (overrides(method, jsMember.member)) {
-            jsMember.member = method;
+          if (overrides(member, jsMember.member)) {
+            jsMember.member = member;
             return jsMember;
           }
         }
-        return new JsMember(method);
+        return new JsMember(member);
     }
   }
 
-  private boolean overrides(JMethod method, JMember potentiallyOverriddenMember) {
-    if (potentiallyOverriddenMember instanceof JField) {
+  private boolean overrides(JMember member, JMember potentiallyOverriddenMember) {
+    if (member instanceof JField || potentiallyOverriddenMember instanceof JField) {
       return false;
     }
+    JMethod method = (JMethod) member;
     if (method.getOverriddenMethods().contains(potentiallyOverriddenMember)) {
       return true;
     }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/SameParameterValueOptimizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/SameParameterValueOptimizer.java
index 275dbce..7c8dba6 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/SameParameterValueOptimizer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/SameParameterValueOptimizer.java
@@ -127,7 +127,7 @@
     }
 
     private boolean isNotOptimizable(JMethod x) {
-      return x.needsDynamicDispatch() || x.canBeCalledExternally();
+      return x.needsDynamicDispatch() || x.canBeReferencedExternally();
     }
 
     private boolean equalLiterals(JValueLiteral l1, JValueLiteral l2) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java
index 76751e1..f42ba3b 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java
@@ -564,7 +564,7 @@
     public void endVisit(JParameter x, Context ctx) {
       JMethod currentMethod = getCurrentMethod();
       if (program.codeGenTypes.contains(currentMethod.getEnclosingType())
-          || currentMethod.canBeCalledExternally()) {
+          || currentMethod.canBeReferencedExternally()) {
         // We cannot tighten this parameter as we don't know all callers.
         return;
       }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/UnifyAst.java b/dev/core/src/com/google/gwt/dev/jjs/impl/UnifyAst.java
index 3433c78..19f5151 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/UnifyAst.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/UnifyAst.java
@@ -1329,7 +1329,7 @@
 
     // Flow into any reachable virtual methods.
     for (JMethod method : type.getMethods()) {
-      if (method.canBeCalledExternally()) {
+      if (method.canBeReferencedExternally()) {
         flowInto(method);
         continue;
       }
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 729c74e..bc296c7 100644
--- a/dev/core/src/com/google/gwt/dev/js/JsUtils.java
+++ b/dev/core/src/com/google/gwt/dev/js/JsUtils.java
@@ -16,8 +16,8 @@
 package com.google.gwt.dev.js;
 
 import com.google.gwt.dev.jjs.SourceInfo;
+import com.google.gwt.dev.jjs.ast.HasJsInfo.JsMemberType;
 import com.google.gwt.dev.jjs.ast.JMethod;
-import com.google.gwt.dev.jjs.ast.JMethod.JsPropertyAccessorType;
 import com.google.gwt.dev.jjs.ast.JParameter;
 import com.google.gwt.dev.jjs.ast.JPrimitiveType;
 import com.google.gwt.dev.jjs.ast.JProgram;
@@ -108,7 +108,7 @@
     }
 
     JsExpression invocation =
-        createInvocationOrPropertyAccess(sourceInfo, method.getJsPropertyAccessorType(), ref, args);
+        createInvocationOrPropertyAccess(sourceInfo, method.getJsMemberType(), ref, args);
 
     JsBlock block = new JsBlock(sourceInfo);
     if (method.getType() == JPrimitiveType.VOID) {
@@ -162,8 +162,8 @@
   }
 
   public static JsExpression createInvocationOrPropertyAccess(SourceInfo sourceInfo,
-      JsPropertyAccessorType propertyAccessorType, JsNameRef reference, List<JsExpression> args) {
-    switch (propertyAccessorType) {
+      JsMemberType memberType, JsNameRef reference, List<JsExpression> args) {
+    switch (memberType) {
       case SETTER:
         assert args.size() == 1;
         return createAssignment(reference, args.get(0));
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/JsInteropRestrictionCheckerTest.java b/dev/core/test/com/google/gwt/dev/jjs/impl/JsInteropRestrictionCheckerTest.java
index 9aaf79f..656fbd7 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/impl/JsInteropRestrictionCheckerTest.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/impl/JsInteropRestrictionCheckerTest.java
@@ -1850,7 +1850,7 @@
         "}",
         "@JsFunction public static interface FI  { void f(A a); }", // JsFunction method is checked.
         "@JsType public static class Buggy {",
-        "  public A field;", // exported field
+        "  public A f;", // exported field
         "  public A f1(A a) { return null; }", // regular class fails.
         "  public A[] f2(A[] a) { return null; }", // array of regular class fails.
         "  public long f3(long a) { return 1l; }", // long fails.
@@ -1861,39 +1861,38 @@
         "}");
 
     assertBuggySucceeds(
+        "Line 12: [unusable-by-js] Type of parameter 'a' in "
+            + "'void EntryPoint.C.fc1(EntryPoint.A)' is not usable by but exposed to JavaScript.",
+        "Line 16: [unusable-by-js] Type of 'EntryPoint.A EntryPoint.D.a' is not usable by but "
+            + "exposed to JavaScript.",
+        "Line 18: [unusable-by-js] Type of parameter 'a' in "
+            + "'void EntryPoint.FI.f(EntryPoint.A)' is not usable by but exposed to JavaScript.",
+        "Line 20: [unusable-by-js] Type of 'EntryPoint.A EntryPoint.Buggy.f' is not usable by but "
+            + "exposed to JavaScript.",
         "Line 21: [unusable-by-js] Return type of 'EntryPoint.A EntryPoint.Buggy.f1(EntryPoint.A)' "
             + "is not usable by but exposed to JavaScript.",
+        "Line 21: [unusable-by-js] Type of parameter 'a' in "
+            + "'EntryPoint.A EntryPoint.Buggy.f1(EntryPoint.A)' is not usable by but "
+            + "exposed to JavaScript.",
         "Line 22: [unusable-by-js] Return type of "
             + "'EntryPoint.A[] EntryPoint.Buggy.f2(EntryPoint.A[])' is not usable by but "
             + "exposed to JavaScript.",
+        "Line 22: [unusable-by-js] Type of parameter 'a' in "
+            + "'EntryPoint.A[] EntryPoint.Buggy.f2(EntryPoint.A[])' is not usable by but "
+            + "exposed to JavaScript.",
         "Line 23: [unusable-by-js] Return type of 'long EntryPoint.Buggy.f3(long)' is not "
             + "usable by but exposed to JavaScript.",
+        "Line 23: [unusable-by-js] Type of parameter 'a' in "
+            + "'long EntryPoint.Buggy.f3(long)' is not usable by but exposed to JavaScript.",
         "Line 24: [unusable-by-js] Return type of 'EntryPoint.B EntryPoint.Buggy.f4(EntryPoint.B)' "
             + "is not usable by but exposed to JavaScript.",
-        "Line 20: [unusable-by-js] Type of field 'field' in type 'EntryPoint.Buggy' "
-            + "is not usable by but exposed to JavaScript.",
-        "Line 21: [unusable-by-js] Type of parameter 'a' in method "
-            + "'EntryPoint.A EntryPoint.Buggy.f1(EntryPoint.A)' is not usable by but "
-            + "exposed to JavaScript.",
-        "Line 22: [unusable-by-js] Type of parameter 'a' in method "
-            + "'EntryPoint.A[] EntryPoint.Buggy.f2(EntryPoint.A[])' is not usable by but "
-            + "exposed to JavaScript.",
-        "Line 23: [unusable-by-js] Type of parameter 'a' in method "
-            + "'long EntryPoint.Buggy.f3(long)' is not usable by but exposed to JavaScript.",
-        "Line 24: [unusable-by-js] Type of parameter 'a' in method "
+        "Line 24: [unusable-by-js] Type of parameter 'a' in "
             + "'EntryPoint.B EntryPoint.Buggy.f4(EntryPoint.B)' is not usable by but "
             + "exposed to JavaScript.",
-        "Line 25: [unusable-by-js] Type of parameter 'a' in method "
+        "Line 25: [unusable-by-js] Type of parameter 'a' in "
             + "'void EntryPoint.Buggy.f5(Object[][])' is not usable by but exposed to JavaScript.",
-        "Line 26: [unusable-by-js] Type of parameter 'a' in method "
-            + "'void EntryPoint.Buggy.f6(Object[])' is not usable by but exposed to JavaScript.",
-        "Line 18: [unusable-by-js] Type of parameter 'a' in method "
-            + "'void EntryPoint.FI.f(EntryPoint.A)' is not usable by but exposed to JavaScript.",
-        "Line 12: [unusable-by-js] Type of parameter 'a' in method "
-            // JsMethod in non-jstype class.
-            + "'void EntryPoint.C.fc1(EntryPoint.A)' is not usable by but exposed to JavaScript.",
-        "Line 16: [unusable-by-js] Type of field 'a' in type " // JsProperty in non-jstype class.
-            + "'EntryPoint.D' is not usable by but exposed to JavaScript.");
+        "Line 26: [unusable-by-js] Type of parameter 'a' in "
+            + "'void EntryPoint.Buggy.f6(Object[])' is not usable by but exposed to JavaScript.");
   }
 
   public void testUnusableByJsAccidentalOverrideSuppressionWarns()
@@ -1911,7 +1910,7 @@
         "public static class Buggy extends Parent implements Foo {}");
 
     assertBuggySucceeds(
-        "Line 10: [unusable-by-js] Type of parameter 'x' in method "
+        "Line 10: [unusable-by-js] Type of parameter 'x' in "
             + "'void EntryPoint.Parent.doIt(Class)' (exposed by 'EntryPoint.Buggy') is not usable "
             + "by but exposed to JavaScript.");
   }