Skip to content

Commit

Permalink
Add support to for native JsType arrays.
Browse files Browse the repository at this point in the history
Java arrays stash some properties to be have to perform casts and array
store checks. Storing properties can be avoided for native JsType arrays,
which in this patch are represented as plan JavaScript arrays.

Change-Id: I63e7dcfa606dd639819f3bc17fd31d16f759cf12
  • Loading branch information
rluble authored and Gerrit Code Review committed Nov 3, 2015
1 parent f5bdc00 commit 7db1ae7
Show file tree
Hide file tree
Showing 17 changed files with 340 additions and 63 deletions.
14 changes: 13 additions & 1 deletion dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
Expand Up @@ -485,7 +485,10 @@ public EnumSet<DispatchType> getDispatchType(JReferenceType type) {
} }


if (type.isArrayType()) { if (type.isArrayType()) {
return EnumSet.of(DispatchType.JAVA_ARRAY); // A variable of type Object[] could contain an instance of native JsType[], the latter
// is treated as a JSO for devirtualization purposes.
// TODO(rluble): maybe it should not be treated as a JSO, think .toString().
return EnumSet.of(DispatchType.JSO, DispatchType.JAVA_ARRAY);
} }
EnumSet<DispatchType> dispatchSet = EnumSet.noneOf(DispatchType.class); EnumSet<DispatchType> dispatchSet = EnumSet.noneOf(DispatchType.class);
DispatchType dispatchType = getRepresentedAsNativeTypesDispatchMap().get(type); DispatchType dispatchType = getRepresentedAsNativeTypesDispatchMap().get(type);
Expand Down Expand Up @@ -1134,6 +1137,15 @@ public void initTypeInfo(Map<JReferenceType, JCastMap> castMapForType) {
} }
} }


public boolean isUntypedArrayType(JType type) {
if (!type.isArrayType()) {
return false;
}

JArrayType arrayType = (JArrayType) type;
return arrayType.getLeafType().isJsNative();
}

public boolean isJavaLangString(JType type) { public boolean isJavaLangString(JType type) {
assert type != null; assert type != null;
return type.getUnderlyingType() == typeString; return type.getUnderlyingType() == typeString;
Expand Down
5 changes: 3 additions & 2 deletions dev/core/src/com/google/gwt/dev/jjs/ast/RuntimeConstants.java
Expand Up @@ -27,13 +27,14 @@ public class RuntimeConstants {
public static final String ARRAY_GET_CLASS_LITERAL_FOR_ARRAY = "Array.getClassLiteralForArray"; public static final String ARRAY_GET_CLASS_LITERAL_FOR_ARRAY = "Array.getClassLiteralForArray";
public static final String ARRAY_INITIALIZE_UNIDIMENSIONAL_ARRAY = "Array.initUnidimensionalArray"; public static final String ARRAY_INITIALIZE_UNIDIMENSIONAL_ARRAY = "Array.initUnidimensionalArray";
public static final String ARRAY_INITIALIZE_MULTIDIMENSIONAL_ARRAY = "Array.initMultidimensionalArray"; public static final String ARRAY_INITIALIZE_MULTIDIMENSIONAL_ARRAY = "Array.initMultidimensionalArray";
public static final String ARRAY_STAMP_JAVA_TYPE_INFO = "Array.stampJavaTypeInfo"; public static final String ARRAY_NEW_ARRAY = "Array.newArray";
public static final String ARRAY_SET_CHECK = "Array.setCheck"; public static final String ARRAY_SET_CHECK = "Array.setCheck";
public static final String ARRAY_STAMP_JAVA_TYPE_INFO = "Array.stampJavaTypeInfo";
public static final String ARRAY_IS_JAVA_ARRAY = "Array.isJavaArray";


public static final String CAST_CHAR_TO_STRING = "Cast.charToString"; public static final String CAST_CHAR_TO_STRING = "Cast.charToString";
public static final String CAST_HAS_JAVA_OBJECT_VIRTUAL_DISPATCH = public static final String CAST_HAS_JAVA_OBJECT_VIRTUAL_DISPATCH =
"Cast.hasJavaObjectVirtualDispatch"; "Cast.hasJavaObjectVirtualDispatch";
public static final String CAST_IS_JAVA_ARRAY = "Cast.isJavaArray";
public static final String CAST_THROW_CLASS_CAST_EXCEPTION_UNLESS_NULL public static final String CAST_THROW_CLASS_CAST_EXCEPTION_UNLESS_NULL
= "Cast.throwClassCastExceptionUnlessNull"; = "Cast.throwClassCastExceptionUnlessNull";


Expand Down
22 changes: 12 additions & 10 deletions dev/core/src/com/google/gwt/dev/jjs/ast/js/JsonArray.java
Expand Up @@ -17,8 +17,8 @@


import com.google.gwt.dev.jjs.SourceInfo; import com.google.gwt.dev.jjs.SourceInfo;
import com.google.gwt.dev.jjs.ast.Context; import com.google.gwt.dev.jjs.ast.Context;
import com.google.gwt.dev.jjs.ast.JClassType;
import com.google.gwt.dev.jjs.ast.JExpression; import com.google.gwt.dev.jjs.ast.JExpression;
import com.google.gwt.dev.jjs.ast.JType;
import com.google.gwt.dev.jjs.ast.JVisitor; import com.google.gwt.dev.jjs.ast.JVisitor;
import com.google.gwt.thirdparty.guava.common.collect.Lists; import com.google.gwt.thirdparty.guava.common.collect.Lists;


Expand All @@ -30,17 +30,19 @@
*/ */
public class JsonArray extends JExpression { public class JsonArray extends JExpression {


private final List<JExpression> expressions; private final List<JExpression> expressions = Lists.newArrayList();


private JClassType arrayType; // JsonArray objects are typed as either JavaScriptObject, Object[], or native JsType[] depending
// on the use.
private JType arrayType;


public JsonArray(SourceInfo sourceInfo, JClassType arrayType, Iterable<JExpression> expressions) { public JsonArray(SourceInfo sourceInfo, JType arrayType, List<JExpression> expressions) {
super(sourceInfo); super(sourceInfo);
this.arrayType = arrayType; this.arrayType = arrayType;
this.expressions = Lists.newArrayList(expressions); this.expressions.addAll(expressions);
} }


public JsonArray(SourceInfo sourceInfo, JClassType arrayType, JExpression... expressions) { public JsonArray(SourceInfo sourceInfo, JType arrayType, JExpression... expressions) {
this(sourceInfo, arrayType, Arrays.asList(expressions)); this(sourceInfo, arrayType, Arrays.asList(expressions));
} }


Expand All @@ -49,7 +51,7 @@ public List<JExpression> getExpressions() {
} }


@Override @Override
public JClassType getType() { public JType getType() {
return arrayType; return arrayType;
} }


Expand All @@ -66,9 +68,9 @@ public boolean hasSideEffects() {
/** /**
* Resolve an external references during AST stitching. * Resolve an external references during AST stitching.
*/ */
public void resolve(JClassType jsoType) { public void resolve(JType arrayType) {
assert jsoType.replaces(this.arrayType); assert arrayType.replaces(this.arrayType);
this.arrayType = jsoType; this.arrayType = arrayType;
} }


@Override @Override
Expand Down
17 changes: 16 additions & 1 deletion dev/core/src/com/google/gwt/dev/jjs/impl/ArrayNormalizer.java
Expand Up @@ -37,6 +37,7 @@
import com.google.gwt.dev.jjs.ast.js.JsonArray; import com.google.gwt.dev.jjs.ast.js.JsonArray;


import java.util.Collections; import java.util.Collections;
import java.util.List;


/** /**
* Replace array accesses and instantiations with calls to the Array class. * Replace array accesses and instantiations with calls to the Array class.
Expand Down Expand Up @@ -78,11 +79,25 @@ public void endVisit(JBinaryOperation x, Context ctx) {
public void endVisit(JNewArray x, Context ctx) { public void endVisit(JNewArray x, Context ctx) {
JArrayType type = x.getArrayType(); JArrayType type = x.getArrayType();


if (x.getInitializers() != null) { List<JExpression> initializers = x.getInitializers();
if (initializers != null) {
JsonArray initializerArray = new JsonArray(x.getSourceInfo(), type, initializers);
if (program.isUntypedArrayType(type)) {
ctx.replaceMe(initializerArray);
return;
}
ctx.replaceMe(createArrayFromInitializers(x, type)); ctx.replaceMe(createArrayFromInitializers(x, type));
return; return;
} }


if (program.isUntypedArrayType(type) && type.getDims() == 1) {
// Create a plain array.
ctx.replaceMe(new JMethodCall(x.getSourceInfo(), null,
program.getIndexedMethod(RuntimeConstants.ARRAY_NEW_ARRAY),
x.getDimensionExpressions().get(0)));
return;
}

int suppliedDimensions = x.getDimensionExpressions().size(); int suppliedDimensions = x.getDimensionExpressions().size();
assert (suppliedDimensions >= 1); assert (suppliedDimensions >= 1);


Expand Down
6 changes: 3 additions & 3 deletions dev/core/src/com/google/gwt/dev/jjs/impl/Devirtualizer.java
Expand Up @@ -225,7 +225,7 @@ public static void exec(JProgram program) {
private final JMethod hasJavaObjectVirtualDispatch; private final JMethod hasJavaObjectVirtualDispatch;


/** /**
* Contains the Cast.instanceofArray method. * Contains the Cast.isJavaArray method.
*/ */
private final JMethod isJavaArray; private final JMethod isJavaArray;


Expand Down Expand Up @@ -284,7 +284,7 @@ private Devirtualizer(JProgram program) {


this.hasJavaObjectVirtualDispatch = this.hasJavaObjectVirtualDispatch =
program.getIndexedMethod(RuntimeConstants.CAST_HAS_JAVA_OBJECT_VIRTUAL_DISPATCH); program.getIndexedMethod(RuntimeConstants.CAST_HAS_JAVA_OBJECT_VIRTUAL_DISPATCH);
this.isJavaArray = program.getIndexedMethod(RuntimeConstants.CAST_IS_JAVA_ARRAY); this.isJavaArray = program.getIndexedMethod(RuntimeConstants.ARRAY_IS_JAVA_ARRAY);
// TODO: consider turning on null checks for "this"? // TODO: consider turning on null checks for "this"?
// However, for JSO's there is existing code that relies on nulls being okay. // However, for JSO's there is existing code that relies on nulls being okay.
this.converter = new StaticCallConverter(program, false); this.converter = new StaticCallConverter(program, false);
Expand Down Expand Up @@ -479,7 +479,7 @@ private JMethod getOrCreateDevirtualMethod(JMethod method) {
// Synthesize the dispatch at a single conditional doing the checks in this order. // Synthesize the dispatch at a single conditional doing the checks in this order.
// isString(obj) ? dispatchToString : ( // isString(obj) ? dispatchToString : (
// isRegularJavaObject(obj) ? obj.method : ( // isRegularJavaObject(obj) ? obj.method : (
// isArray(obj) ? // isJavaArray(obj) ?
// dispatchToArray : // dispatchToArray :
// dispatchToJSO // dispatchToJSO
// ) // )
Expand Down
20 changes: 15 additions & 5 deletions dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java
Expand Up @@ -2912,14 +2912,20 @@ private JStringLiteral getStringLiteral(SourceInfo info, String string) {
return new JStringLiteral(info, intern(string), javaLangString); return new JStringLiteral(info, intern(string), javaLangString);
} }


/**
* TODO(scottb): move to UnifyAst and only for non-abstract classes.
*/
private void implementGetClass(JDeclaredType type) { private void implementGetClass(JDeclaredType type) {
// TODO(rluble): Object.getClass() should be final our JRE, when that is done, GwtAstBuilder
// creates overrides for convenience and should unmark Object.getClass as final for
// consistency.
JMethod method = type.getMethods().get(GET_CLASS_METHOD_INDEX); JMethod method = type.getMethods().get(GET_CLASS_METHOD_INDEX);
assert (GwtAstBuilder.GET_CLASS_METHOD_NAME.equals(method.getName())); assert (GET_CLASS_METHOD_NAME.equals(method.getName()));
SourceInfo info = method.getSourceInfo(); SourceInfo info = method.getSourceInfo();
JjsUtils.replaceMethodBody(method, new JClassLiteral(info, type)); if (type.isJsoType()) {
// return Cast.getClass(this)
JjsUtils.replaceMethodBody(method,
new JMethodCall(info, null, CAST_GET_CLASS_METHOD, new JThisRef(info, type)));
} else {
JjsUtils.replaceMethodBody(method, new JClassLiteral(info, type));
}
} }


private JDeclarationStatement makeDeclaration(SourceInfo info, JLocal local, private JDeclarationStatement makeDeclaration(SourceInfo info, JLocal local,
Expand Down Expand Up @@ -3795,6 +3801,10 @@ private boolean isBinaryBinding(ReferenceBinding binding) {
JMethod.getExternalizedMethod("com.google.gwt.lang.Exceptions", JMethod.getExternalizedMethod("com.google.gwt.lang.Exceptions",
"safeClose(Ljava/lang/AutoCloseable;Ljava/lang/Throwable;)Ljava/lang/Throwable;", true); "safeClose(Ljava/lang/AutoCloseable;Ljava/lang/Throwable;)Ljava/lang/Throwable;", true);


private static JMethod CAST_GET_CLASS_METHOD =
JMethod.getExternalizedMethod("com.google.gwt.lang.Cast",
"getClass(Ljava/lang/Object;)Ljava/lang/Class;", true);

private List<JDeclaredType> processImpl() { private List<JDeclaredType> processImpl() {
CompilationUnitDeclaration cud = curCud.cud; CompilationUnitDeclaration cud = curCud.cud;
if (cud.types == null) { if (cud.types == null) {
Expand Down
Expand Up @@ -224,6 +224,7 @@ private TypeCategory determineTypeCategoryForType(JReferenceType type) {


assert EnumSet.of(TypeCategory.TYPE_JSO, assert EnumSet.of(TypeCategory.TYPE_JSO,
TypeCategory.TYPE_JAVA_OBJECT_OR_JSO, TypeCategory.TYPE_JAVA_OBJECT_OR_JSO,
TypeCategory.TYPE_NATIVE_ARRAY,
TypeCategory.TYPE_JAVA_LANG_OBJECT, TypeCategory.TYPE_JAVA_LANG_OBJECT,
TypeCategory.TYPE_JAVA_LANG_STRING, TypeCategory.TYPE_JAVA_LANG_STRING,
TypeCategory.TYPE_JAVA_LANG_DOUBLE, TypeCategory.TYPE_JAVA_LANG_DOUBLE,
Expand Down
Expand Up @@ -80,7 +80,8 @@ private boolean isGetClassMethod(JMethod method) {


private boolean isGetClassDevirtualized(JType type) { private boolean isGetClassDevirtualized(JType type) {
return type == program.getJavaScriptObject() return type == program.getJavaScriptObject()
|| program.getRepresentedAsNativeTypes().contains(type); || program.getRepresentedAsNativeTypes().contains(type)
|| type.isJsNative();
} }
} }
} }
9 changes: 6 additions & 3 deletions dev/core/src/com/google/gwt/dev/jjs/impl/TypeCategory.java
Expand Up @@ -41,6 +41,7 @@ public enum TypeCategory {
TYPE_JAVA_OBJECT, TYPE_JAVA_OBJECT,
TYPE_JAVA_OBJECT_OR_JSO("AllowJso"), TYPE_JAVA_OBJECT_OR_JSO("AllowJso"),
TYPE_JSO("Jso"), TYPE_JSO("Jso"),
TYPE_NATIVE_ARRAY("NativeArray"),
TYPE_JAVA_LANG_OBJECT("AllowJso"), TYPE_JAVA_LANG_OBJECT("AllowJso"),
TYPE_JAVA_LANG_STRING("String"), TYPE_JAVA_LANG_STRING("String"),
TYPE_JAVA_LANG_DOUBLE("Double"), TYPE_JAVA_LANG_DOUBLE("Double"),
Expand All @@ -54,11 +55,11 @@ public enum TypeCategory {


private final String castInstanceOfQualifier; private final String castInstanceOfQualifier;


private TypeCategory() { TypeCategory() {
this(""); this("");
} }


private TypeCategory(String castInstanceOfQualifier) { TypeCategory(String castInstanceOfQualifier) {
this.castInstanceOfQualifier = castInstanceOfQualifier; this.castInstanceOfQualifier = castInstanceOfQualifier;
} }


Expand All @@ -82,7 +83,9 @@ public static TypeCategory typeCategoryForType(JType type, JProgram program) {


assert type instanceof JReferenceType; assert type instanceof JReferenceType;
type = type.getUnderlyingType(); type = type.getUnderlyingType();
if (type == program.getTypeJavaLangObject()) { if (program.isUntypedArrayType(type)) {
return TypeCategory.TYPE_NATIVE_ARRAY;
} else if (type == program.getTypeJavaLangObject()) {
return TypeCategory.TYPE_JAVA_LANG_OBJECT; return TypeCategory.TYPE_JAVA_LANG_OBJECT;
} else if (program.getRepresentedAsNativeTypesDispatchMap().containsKey(type)) { } else if (program.getRepresentedAsNativeTypesDispatchMap().containsKey(type)) {
return program.getRepresentedAsNativeTypesDispatchMap().get(type).getTypeCategory(); return program.getRepresentedAsNativeTypesDispatchMap().get(type).getTypeCategory();
Expand Down
Expand Up @@ -32,23 +32,34 @@ public final class Array {
private static final int TYPE_JAVA_OBJECT = 0; private static final int TYPE_JAVA_OBJECT = 0;
private static final int TYPE_JAVA_OBJECT_OR_JSO = 1; private static final int TYPE_JAVA_OBJECT_OR_JSO = 1;
private static final int TYPE_JSO = 2; private static final int TYPE_JSO = 2;
private static final int TYPE_JAVA_LANG_OBJECT = 3; private static final int TYPE_NATIVE_ARRAYs = 3;
private static final int TYPE_JAVA_LANG_STRING = 4; private static final int TYPE_JAVA_LANG_OBJECT = 4;
private static final int TYPE_JAVA_LANG_DOUBLE = 5; private static final int TYPE_JAVA_LANG_STRING = 5;
private static final int TYPE_JAVA_LANG_BOOLEAN = 6; private static final int TYPE_JAVA_LANG_DOUBLE = 6;
private static final int TYPE_JS_NATIVE = 7; private static final int TYPE_JAVA_LANG_BOOLEAN = 7;
private static final int TYPE_JS_NATIVE_PROTOTYPE = 8; private static final int TYPE_JS_NATIVE = 8;
private static final int TYPE_JS_FUNCTION = 9; private static final int TYPE_JS_UNKNOWN_NATIVE = 9;
private static final int TYPE_PRIMITIVE_LONG = 10; private static final int TYPE_JS_FUNCTION = 10;
private static final int TYPE_PRIMITIVE_NUMBER = 11; private static final int TYPE_PRIMITIVE_LONG = 11;
private static final int TYPE_PRIMITIVE_BOOLEAN = 12; private static final int TYPE_PRIMITIVE_NUMBER = 12;
private static final int TYPE_PRIMITIVE_BOOLEAN = 13;


public static <T> T[] stampJavaTypeInfo(Object array, T[] referenceType) { public static <T> T[] stampJavaTypeInfo(Object array, T[] referenceType) {
stampJavaTypeInfo(referenceType.getClass(), Util.getCastableTypeMap(referenceType), if (Array.getElementTypeCategory(referenceType) != TYPE_JS_UNKNOWN_NATIVE) {
Array.getElementTypeId(referenceType), Array.getElementTypeCategory(referenceType), array); stampJavaTypeInfo(referenceType.getClass(), Util.getCastableTypeMap(referenceType),
Array.getElementTypeId(referenceType),
Array.getElementTypeCategory(referenceType), array);
}
return Array.asArray(array); return Array.asArray(array);
} }


/**
* Returns an untyped uninitialized array.
*/
static native Object[] newArray(int size) /*-{
return new Array(size);
}-*/;

/** /**
* Creates an array like "new T[a][b][c][][]" by passing in a native JSON * Creates an array like "new T[a][b][c][][]" by passing in a native JSON
* array, [a, b, c]. * array, [a, b, c].
Expand All @@ -71,8 +82,10 @@ public static Object initUnidimensionalArray(Class<?> leafClassLiteral,
JavaScriptObject castableTypeMap, JavaScriptObject elementTypeId, int length, JavaScriptObject castableTypeMap, JavaScriptObject elementTypeId, int length,
int elementTypeCategory, int dimensions) { int elementTypeCategory, int dimensions) {
Object result = initializeArrayElementsWithDefaults(elementTypeCategory, length); Object result = initializeArrayElementsWithDefaults(elementTypeCategory, length);
stampJavaTypeInfo(getClassLiteralForArray(leafClassLiteral, dimensions), castableTypeMap, if (elementTypeCategory != TYPE_JS_UNKNOWN_NATIVE) {
elementTypeId, elementTypeCategory, result); stampJavaTypeInfo(getClassLiteralForArray(leafClassLiteral, dimensions), castableTypeMap,
elementTypeId, elementTypeCategory, result);
}
return result; return result;
} }


Expand Down Expand Up @@ -222,8 +235,10 @@ private static Object initMultidimensionalArray(Class<?> leafClassLiteral,
int elementTypeCategory = isLastDimension ? leafElementTypeCategory : TYPE_JAVA_OBJECT; int elementTypeCategory = isLastDimension ? leafElementTypeCategory : TYPE_JAVA_OBJECT;


Object result = initializeArrayElementsWithDefaults(elementTypeCategory, length); Object result = initializeArrayElementsWithDefaults(elementTypeCategory, length);
stampJavaTypeInfo(getClassLiteralForArray(leafClassLiteral, count - index), if (leafElementTypeCategory != TYPE_JS_UNKNOWN_NATIVE) {
castableTypeMapExprs[index], elementTypeIds[index], elementTypeCategory, result); stampJavaTypeInfo(getClassLiteralForArray(leafClassLiteral, count - index),
castableTypeMapExprs[index], elementTypeIds[index], elementTypeCategory, result);
}


if (!isLastDimension) { if (!isLastDimension) {
// Recurse to next dimension. // Recurse to next dimension.
Expand All @@ -247,9 +262,12 @@ static <T> Class<T> getClassLiteralForArray(Class<?> clazz , int dimensions) {
} }


private static native int getElementTypeCategory(Object array) /*-{ private static native int getElementTypeCategory(Object array) /*-{
return array.__elementTypeCategory$; return array.__elementTypeCategory$ == null
? @Array::TYPE_JS_UNKNOWN_NATIVE
: array.__elementTypeCategory$;
}-*/; }-*/;


@HasNoSideEffects
private static native JavaScriptObject getElementTypeId(Object array) /*-{ private static native JavaScriptObject getElementTypeId(Object array) /*-{
return array.__elementTypeId$; return array.__elementTypeId$;
}-*/; }-*/;
Expand Down Expand Up @@ -281,6 +299,14 @@ private static native void setElementTypeCategory(Object array, int elementTypeC
array.__elementTypeCategory$ = elementTypeCategory; array.__elementTypeCategory$ = elementTypeCategory;
}-*/; }-*/;


/**
* Returns true if {@code src} is a Java array.
*/
@HasNoSideEffects
static boolean isJavaArray(Object src) {
return Cast.isArray(src) && Util.hasTypeMarker(src);
}

private Array() { private Array() {
} }
} }
Expand Down

0 comments on commit 7db1ae7

Please sign in to comment.