Skip to content

Commit

Permalink
Introduces native JsType.
Browse files Browse the repository at this point in the history
This CL introduces native JsType based on prototype attribute
which will later be replaced by isNative attribute.

The patch doesn't handle static JsMethods/JsProperties, a follow
up patch will fix that.

Change-Id: Ibe5bf89978bb063ad86b003fe296acd02a362ff4
Review-Link: https://gwt-review.googlesource.com/#/c/13520/
  • Loading branch information
gkdn committed Sep 16, 2015
1 parent 5e341a2 commit 04b56f4
Show file tree
Hide file tree
Showing 34 changed files with 213 additions and 506 deletions.
136 changes: 6 additions & 130 deletions dev/core/src/com/google/gwt/dev/javac/JSORestrictionsChecker.java
Expand Up @@ -18,7 +18,6 @@
import com.google.gwt.dev.jdt.SafeASTVisitor; import com.google.gwt.dev.jdt.SafeASTVisitor;
import com.google.gwt.dev.util.InstalledHelpInfo; import com.google.gwt.dev.util.InstalledHelpInfo;
import com.google.gwt.dev.util.collect.Stack; import com.google.gwt.dev.util.collect.Stack;
import com.google.gwt.thirdparty.guava.common.base.Strings;


import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.ASTNode;
Expand All @@ -29,7 +28,6 @@
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope; import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.ClassScope; import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope; import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
Expand Down Expand Up @@ -67,32 +65,28 @@ public class JSORestrictionsChecker {
"@JsExport and @JsNoExport is not allowed at the same time."; "@JsExport and @JsNoExport is not allowed at the same time.";
public static final String ERR_JSEXPORT_ON_ENUMERATION = public static final String ERR_JSEXPORT_ON_ENUMERATION =
"@JsExport is not allowed on individual enumerations"; "@JsExport is not allowed on individual enumerations";
public static final String ERR_MUST_EXTEND_MAGIC_PROTOTYPE_CLASS =
"Classes implementing @JsType with a prototype must extend that interface's Prototype class";
public static final String ERR_CLASS_EXTENDS_MAGIC_PROTOTYPE_BUT_NO_PROTOTYPE_ATTRIBUTE =
"Classes implementing a @JsType without a prototype should not extend the Prototype class";
public static final String ERR_CONSTRUCTOR_WITH_PARAMETERS = public static final String ERR_CONSTRUCTOR_WITH_PARAMETERS =
"Constructors must not have parameters in subclasses of JavaScriptObject"; "Constructors must not have parameters in subclasses of JavaScriptObject";
public static final String ERR_INSTANCE_FIELD = "Instance fields cannot be used in subclasses of JavaScriptObject"; public static final String ERR_INSTANCE_FIELD =
"Instance fields cannot be used in subclasses of JavaScriptObject";
public static final String ERR_INSTANCE_METHOD_NONFINAL = public static final String ERR_INSTANCE_METHOD_NONFINAL =
"Instance methods must be 'final' in non-final subclasses of JavaScriptObject"; "Instance methods must be 'final' in non-final subclasses of JavaScriptObject";
public static final String ERR_IS_NONSTATIC_NESTED = "Nested classes must be 'static' if they extend JavaScriptObject"; public static final String ERR_IS_NONSTATIC_NESTED =
"Nested classes must be 'static' if they extend JavaScriptObject";
public static final String ERR_NEW_JSO = public static final String ERR_NEW_JSO =
"'new' cannot be used to create instances of JavaScriptObject subclasses; instances must originate in JavaScript"; "'new' cannot be used to create instances of JavaScriptObject subclasses; "
+ "instances must originate in JavaScript";
public static final String ERR_NONEMPTY_CONSTRUCTOR = public static final String ERR_NONEMPTY_CONSTRUCTOR =
"Constructors must be totally empty in subclasses of JavaScriptObject"; "Constructors must be totally empty in subclasses of JavaScriptObject";
public static final String ERR_NONPROTECTED_CONSTRUCTOR = public static final String ERR_NONPROTECTED_CONSTRUCTOR =
"Constructors must be 'protected' in subclasses of JavaScriptObject"; "Constructors must be 'protected' in subclasses of JavaScriptObject";
public static final String ERR_OVERRIDDEN_METHOD = public static final String ERR_OVERRIDDEN_METHOD =
"Methods cannot be overridden in JavaScriptObject subclasses"; "Methods cannot be overridden in JavaScriptObject subclasses";
public static final String JSO_CLASS = "com/google/gwt/core/client/JavaScriptObject"; public static final String JSO_CLASS = "com/google/gwt/core/client/JavaScriptObject";
public static final String ERR_FORGOT_TO_MAKE_PROTOTYPE_IMPL_JSTYPE = "@JsType subtype extends magic _Prototype class, but _Prototype class doesn't implement JsType";
public static final String ERR_JS_TYPE_WITH_PROTOTYPE_SET_NOT_ALLOWED_ON_CLASS_TYPES = "@JsType with prototype set not allowed on class types";
public static final String ERR_JS_FUNCTION_ONLY_ALLOWED_ON_FUNCTIONAL_INTERFACE = public static final String ERR_JS_FUNCTION_ONLY_ALLOWED_ON_FUNCTIONAL_INTERFACE =
"@JsFunction is only allowed on functional interface"; "@JsFunction is only allowed on functional interface";
public static final String ERR_JS_FUNCTION_CANNOT_HAVE_DEFAULT_METHODS = public static final String ERR_JS_FUNCTION_CANNOT_HAVE_DEFAULT_METHODS =
"JsFunction cannot have default methods"; "JsFunction cannot have default methods";
static boolean LINT_MODE = false;


private enum ClassState { private enum ClassState {
NORMAL, JSO NORMAL, JSO
Expand Down Expand Up @@ -247,18 +241,6 @@ private void checkJsFunction(TypeDeclaration type, TypeBinding typeBinding) {
} }
} }


private void checkJsType(TypeDeclaration type, TypeBinding typeBinding) {
ReferenceBinding binding = (ReferenceBinding) typeBinding;
if (binding.isClass()) {
AnnotationBinding jsinterfaceAnn = JdtUtil.getAnnotation(typeBinding,
JsInteropUtil.JSTYPE_CLASS);
String jsPrototype = JdtUtil.getAnnotationParameterString(jsinterfaceAnn, "prototype");
if (jsPrototype != null && !"".equals(jsPrototype)) {
errorOn(type, ERR_JS_TYPE_WITH_PROTOTYPE_SET_NOT_ALLOWED_ON_CLASS_TYPES);
}
}
}

private void checkJsExport(MethodBinding mb) { private void checkJsExport(MethodBinding mb) {
if (JdtUtil.getAnnotation(mb, JsInteropUtil.JSEXPORT_CLASS) != null) { if (JdtUtil.getAnnotation(mb, JsInteropUtil.JSEXPORT_CLASS) != null) {
boolean isStatic = mb.isConstructor() || mb.isStatic(); boolean isStatic = mb.isConstructor() || mb.isStatic();
Expand Down Expand Up @@ -294,14 +276,6 @@ private boolean isEnumConstant(FieldDeclaration fd) {
private ClassState checkType(TypeDeclaration type) { private ClassState checkType(TypeDeclaration type) {
SourceTypeBinding binding = type.binding; SourceTypeBinding binding = type.binding;
checkJsFunction(type, binding); checkJsFunction(type, binding);
if (isJsType(type.binding)) {
checkJsType(type, type.binding);
return ClassState.NORMAL;
}

if (checkClassImplementingJsType(type)) {
return ClassState.NORMAL;
}


if (!isJsoSubclass(binding)) { if (!isJsoSubclass(binding)) {
return ClassState.NORMAL; return ClassState.NORMAL;
Expand Down Expand Up @@ -332,91 +306,6 @@ private ClassState checkType(TypeDeclaration type) {
return ClassState.JSO; return ClassState.JSO;
} }


private boolean checkClassImplementingJsType(TypeDeclaration type) {
ReferenceBinding jsInterface = findNearestJsTypeRecursive(type.binding);
if (jsInterface == null) {
return false;
}

AnnotationBinding jsinterfaceAnn = JdtUtil.getAnnotation(jsInterface,
JsInteropUtil.JSTYPE_CLASS);
String jsPrototype = JdtUtil.getAnnotationParameterString(jsinterfaceAnn, "prototype");
boolean shouldExtend = !Strings.isNullOrEmpty(jsPrototype);
checkClassExtendsMagicPrototype(type, jsInterface, shouldExtend);

// TODO(cromwellian) add multiple-inheritance checks when ambiguity in spec is resolved
return true;
}

private void checkClassExtendsMagicPrototype(TypeDeclaration type, ReferenceBinding jsInterface,
boolean shouldExtend) {
ReferenceBinding superClass = type.binding.superclass();
// if type is the _Prototype stub (implements JsType) exit
if (isMagicPrototype(type.binding, jsInterface)) {
return;
} else if (isMagicPrototypeStub(type)) {
errorOn(type, ERR_FORGOT_TO_MAKE_PROTOTYPE_IMPL_JSTYPE);
}

if (shouldExtend) {
// super class should be SomeInterface.Prototype, so enclosing type should match the jsInterface
if (LINT_MODE && (superClass == null || !isMagicPrototype(superClass, jsInterface))) {
errorOn(type, ERR_MUST_EXTEND_MAGIC_PROTOTYPE_CLASS);
}
} else {
if (superClass != null && isMagicPrototype(superClass, jsInterface)) {
errorOn(type, ERR_CLASS_EXTENDS_MAGIC_PROTOTYPE_BUT_NO_PROTOTYPE_ATTRIBUTE);
}
}
}

// Roughly parallels JProgram.isJsTypePrototype()
private boolean isMagicPrototype(ReferenceBinding type, ReferenceBinding jsInterface) {
if (isMagicPrototypeStub(type)) {
for (ReferenceBinding intf : type.superInterfaces()) {
if (intf == jsInterface) {
return true;
}
}
}
return false;
}

private boolean isMagicPrototypeStub(TypeDeclaration type) {
return isMagicPrototypeStub(type.binding);
}

private boolean isMagicPrototypeStub(ReferenceBinding binding) {
return JdtUtil.getAnnotation(binding, JsInteropUtil.JSTYPEPROTOTYPE_CLASS) != null;
}

/**
* Walks up chain of interfaces and superinterfaces to find the first one marked with @JsType.
*/
private ReferenceBinding findNearestJsType(ReferenceBinding binding) {
if (isJsType(binding)) {
return binding;
}

for (ReferenceBinding intb : binding.superInterfaces()) {
ReferenceBinding checkSuperInt = findNearestJsType(intb);
if (checkSuperInt != null) {
return checkSuperInt;
}
}
return null;
}

private ReferenceBinding findNearestJsTypeRecursive(ReferenceBinding binding) {
ReferenceBinding nearest = findNearestJsType(binding);
if (nearest != null) {
return nearest;
} else if (binding.superclass() != null) {
return findNearestJsTypeRecursive(binding.superclass());
}
return null;
}

private boolean areAllEnclosingClassesPublic() { private boolean areAllEnclosingClassesPublic() {
for (SourceTypeBinding typeBinding : typeBindingStack) { for (SourceTypeBinding typeBinding : typeBindingStack) {
if (!typeBinding.isPublic()) { if (!typeBinding.isPublic()) {
Expand Down Expand Up @@ -469,19 +358,6 @@ public static boolean isJso(TypeBinding typeBinding) {
return false; return false;
} }


/**
* Returns the first JsType annotation encountered traversing the type hierarchy upwards from the type.
*/
private boolean isJsType(TypeBinding typeBinding) {

if (!(typeBinding instanceof ReferenceBinding) || !(typeBinding instanceof SourceTypeBinding)) {
return false;
}

AnnotationBinding jsInterface = JdtUtil.getAnnotation(typeBinding, JsInteropUtil.JSTYPE_CLASS);
return jsInterface != null;
}

/** /**
* Returns {@code true} if {@code typeBinding} is a subtype of * Returns {@code true} if {@code typeBinding} is a subtype of
* {@code JavaScriptObject}, but not {@code JavaScriptObject} itself. * {@code JavaScriptObject}, but not {@code JavaScriptObject} itself.
Expand Down
7 changes: 0 additions & 7 deletions dev/core/src/com/google/gwt/dev/javac/JsInteropUtil.java
Expand Up @@ -24,7 +24,6 @@
import com.google.gwt.thirdparty.guava.common.base.Strings; import com.google.gwt.thirdparty.guava.common.base.Strings;


import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding; import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding;


import java.beans.Introspector; import java.beans.Introspector;
Expand All @@ -40,8 +39,6 @@ public final class JsInteropUtil {
public static final String JSNOEXPORT_CLASS = "com.google.gwt.core.client.js.JsNoExport"; public static final String JSNOEXPORT_CLASS = "com.google.gwt.core.client.js.JsNoExport";
public static final String JSPROPERTY_CLASS = "com.google.gwt.core.client.js.JsProperty"; public static final String JSPROPERTY_CLASS = "com.google.gwt.core.client.js.JsProperty";
public static final String JSTYPE_CLASS = "com.google.gwt.core.client.js.JsType"; public static final String JSTYPE_CLASS = "com.google.gwt.core.client.js.JsType";
public static final String JSTYPEPROTOTYPE_CLASS =
"com.google.gwt.core.client.js.impl.PrototypeOfJsType";


public static void maybeSetJsInteropProperties(JDeclaredType type, Annotation... annotations) { public static void maybeSetJsInteropProperties(JDeclaredType type, Annotation... annotations) {
AnnotationBinding jsType = JdtUtil.getAnnotation(annotations, JSTYPE_CLASS); AnnotationBinding jsType = JdtUtil.getAnnotation(annotations, JSTYPE_CLASS);
Expand Down Expand Up @@ -106,10 +103,6 @@ private static String computeName(JMember member) {
return member instanceof JConstructor ? "" : member.getName(); return member instanceof JConstructor ? "" : member.getName();
} }


public static boolean isJsPrototypeFlag(TypeDeclaration x) {
return JdtUtil.getAnnotation(x.annotations, JSTYPEPROTOTYPE_CLASS) != null;
}

private static String maybeGetJsNamespace(Annotation[] annotations) { private static String maybeGetJsNamespace(Annotation[] annotations) {
AnnotationBinding jsNamespace = JdtUtil.getAnnotation(annotations, JSNAMESPACE_CLASS); AnnotationBinding jsNamespace = JdtUtil.getAnnotation(annotations, JSNAMESPACE_CLASS);
return JdtUtil.getAnnotationParameterString(jsNamespace, "value"); return JdtUtil.getAnnotationParameterString(jsNamespace, "value");
Expand Down
Expand Up @@ -187,10 +187,6 @@ public static JsFunction parseJsniFunction(AbstractMethodDeclaration method,
int startPos = jsniCode.indexOf("/*-{"); int startPos = jsniCode.indexOf("/*-{");
int endPos = jsniCode.lastIndexOf("}-*/"); int endPos = jsniCode.lastIndexOf("}-*/");
if (startPos < 0 && endPos < 0) { if (startPos < 0 && endPos < 0) {
reportJsniError(
info,
method,
"Native methods require a JavaScript implementation enclosed with /*-{ and }-*/");
return null; return null;
} }
if (startPos < 0) { if (startPos < 0) {
Expand Down
14 changes: 5 additions & 9 deletions dev/core/src/com/google/gwt/dev/jjs/ast/JClassType.java
Expand Up @@ -42,7 +42,6 @@ private Object readResolve() {
private final boolean isFinal; private final boolean isFinal;
private boolean isJso; private boolean isJso;
private JClassType superClass; private JClassType superClass;
private boolean isJsPrototypeStub;


public JClassType(SourceInfo info, String name, boolean isAbstract, boolean isFinal) { public JClassType(SourceInfo info, String name, boolean isAbstract, boolean isFinal) {
super(info, name); super(info, name);
Expand Down Expand Up @@ -113,6 +112,11 @@ public boolean isJsFunctionImplementation() {
return false; return false;
} }


@Override
public boolean canBeImplementedExternally() {
return isJsNative();
}

/** /**
* Sets this type's super class. * Sets this type's super class.
*/ */
Expand All @@ -125,14 +129,6 @@ public final void setSuperClass(JClassType superClass) {
} }
} }


public boolean isJsPrototypeStub() {
return isJsPrototypeStub;
}

public void setJsPrototypeStub(boolean isJsPrototype) {
this.isJsPrototypeStub = isJsPrototype;
}

@Override @Override
public void traverse(JVisitor visitor, Context ctx) { public void traverse(JVisitor visitor, Context ctx) {
if (visitor.visit(this, ctx)) { if (visitor.visit(this, ctx)) {
Expand Down
7 changes: 6 additions & 1 deletion dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java
Expand Up @@ -50,12 +50,12 @@
*/ */
public abstract class JDeclaredType extends JReferenceType { public abstract class JDeclaredType extends JReferenceType {


private String jsPrototype;
private boolean isJsFunction; private boolean isJsFunction;
private boolean isJsType; private boolean isJsType;
private boolean isClassWideExport; private boolean isClassWideExport;
private String jsNamespace = null; private String jsNamespace = null;
private String jsName = null; private String jsName = null;
private String jsPrototype;


/** /**
* The types of nested classes, https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html * The types of nested classes, https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html
Expand Down Expand Up @@ -380,6 +380,11 @@ public String getJsPrototype() {
return jsPrototype; return jsPrototype;
} }


@Override
public boolean isJsNative() {
return jsPrototype != null;
}

/** /**
* Returns this type's super class, or <code>null</code> if this type is * Returns this type's super class, or <code>null</code> if this type is
* {@link Object} or an interface. * {@link Object} or an interface.
Expand Down
6 changes: 5 additions & 1 deletion dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
Expand Up @@ -91,7 +91,7 @@ public boolean canBeCalledExternally() {
} }


public boolean canBeImplementedExternally() { public boolean canBeImplementedExternally() {
return isJsFunctionMethod() || isJsInterfaceMethod(); return isJsNative() || isJsFunctionMethod() || isJsInterfaceMethod();
} }


private boolean isJsInterfaceMethod() { private boolean isJsInterfaceMethod() {
Expand Down Expand Up @@ -226,6 +226,10 @@ public boolean isOrOverridesJsFunctionMethod() {
return false; return false;
} }


public boolean isJsNative() {
return enclosingType != null && enclosingType.isJsNative();
}

public void setSpecialization(List<JType> paramTypes, JType returnsType, String targetMethod) { public void setSpecialization(List<JType> paramTypes, JType returnsType, String targetMethod) {
this.specialization = new Specialization(paramTypes, this.specialization = new Specialization(paramTypes,
returnsType == null ? this.getOriginalReturnType() : returnsType, targetMethod); returnsType == null ? this.getOriginalReturnType() : returnsType, targetMethod);
Expand Down
21 changes: 0 additions & 21 deletions dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
Expand Up @@ -130,13 +130,6 @@ public String getclassName() {
} }
} }


/**
* Returns whether a class is a synthetic Prototype class generated by APT or user.
*/
public boolean isJsTypePrototype(JDeclaredType classType) {
return classType instanceof JClassType && ((JClassType) classType).isJsPrototypeStub();
}

private static final class TreeStatistics extends JVisitor { private static final class TreeStatistics extends JVisitor {
private int nodeCount = 0; private int nodeCount = 0;


Expand Down Expand Up @@ -1177,20 +1170,6 @@ public boolean isStaticImpl(JMethod method) {
return staticToInstanceMap.containsKey(method); return staticToInstanceMap.containsKey(method);
} }


public static JInterfaceType maybeGetJsTypeFromPrototype(JDeclaredType classType) {
if (classType == null) {
return null;
}
if (classType instanceof JClassType && ((JClassType) classType).isJsPrototypeStub()) {
for (JInterfaceType intf : classType.getImplements()) {
if (intf.isJsType() && intf.getJsPrototype() != null) {
return intf;
}
}
}
return null;
}

/** /**
* If the type is a JSO or an array of JSOs it returns cggcc.JavaScriptObject or an array of * If the type is a JSO or an array of JSOs it returns cggcc.JavaScriptObject or an array of
* cggcc.JavaScriptObject respectively; otherwise returns {@code type}. * cggcc.JavaScriptObject respectively; otherwise returns {@code type}.
Expand Down
4 changes: 4 additions & 0 deletions dev/core/src/com/google/gwt/dev/jjs/ast/JType.java
Expand Up @@ -77,6 +77,10 @@ public boolean isJsFunction() {
return false; return false;
} }


public boolean isJsNative() {
return false;
}

public boolean canBeImplementedExternally() { public boolean canBeImplementedExternally() {
return false; return false;
} }
Expand Down

0 comments on commit 04b56f4

Please sign in to comment.