Skip to content

Commit

Permalink
Refactor property/member identification for JsInterop.
Browse files Browse the repository at this point in the history
Change-Id: I7e4d2bc0675bb08f013c69993c1fd602f12a8ca1
  • Loading branch information
gkdn authored and Gerrit Code Review committed Nov 6, 2015
1 parent 4312ec8 commit 64e3270
Show file tree
Hide file tree
Showing 16 changed files with 332 additions and 354 deletions.
91 changes: 47 additions & 44 deletions dev/core/src/com/google/gwt/dev/javac/JsInteropUtil.java
Expand Up @@ -15,16 +15,15 @@
*/
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;
import com.google.gwt.dev.jjs.ast.JField;
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;
Expand All @@ -46,7 +45,7 @@ public final class JsInteropUtil {
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) {
Expand All @@ -55,7 +54,7 @@ public static void maybeSetJsInteropProperties(JDeclaredType type, Annotation...
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))
Expand Down Expand Up @@ -84,70 +83,69 @@ public static void maybeSetJsInteropPropertiesNew(JDeclaredType type, Annotation
}

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;
}
setJsInteropPropertiesNew(method, annotations, annotation);
if (jsPropertyAnnotation != null) {
setJsPropertyProperties(
method, JdtUtil.getAnnotationParameterString(jsPropertyAnnotation, "name"));
annotation = getInteropAnnotation(annotations, "JsProperty");
}

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);
}
}

private static boolean isNativeConstructor(JMember member) {
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;
}
Expand All @@ -159,38 +157,43 @@ private static void setJsInteropPropertiesNew(

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) {
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) {
method.setJsPropertyInfo(name, JsPropertyAccessorType.SETTER);
return JsMemberType.SETTER;
} else if (method.getParams().isEmpty() && method.getType() != JPrimitiveType.VOID) {
method.setJsPropertyInfo(name, JsPropertyAccessorType.GETTER);
} else {
method.setJsPropertyInfo(name, JsPropertyAccessorType.UNDEFINED);
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");
}
}
104 changes: 103 additions & 1 deletion dev/core/src/com/google/gwt/dev/jjs/ast/HasJsInfo.java
Expand Up @@ -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;

private String accessorKey;

private JsMemberType() { }

private JsMemberType(String accessorKey) {
this.accessorKey = accessorKey;
}

public String getPropertyAccessorKey() {
return accessorKey;
}

void setJsMemberInfo(String namespace, String name, boolean exported);
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();
}
19 changes: 10 additions & 9 deletions dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java
Expand Up @@ -360,6 +360,10 @@ public final List<JMethod> getMethods() {
return methods;
}

public Iterable<JMember> getMembers() {
return Iterables.<JMember>concat(fields, methods);
}

@Override
public boolean isJsType() {
return isJsType;
Expand Down Expand Up @@ -394,22 +398,16 @@ public boolean hasJsInteropEntryPoints() {
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;
}

Expand Down Expand Up @@ -613,14 +611,17 @@ private JMethod getDefaultConstructor() {
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();
}
Expand Down

0 comments on commit 64e3270

Please sign in to comment.