diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/jribble/JribbleAstBuilder.java b/dev/core/src/com/google/gwt/dev/jjs/impl/jribble/JribbleAstBuilder.java index b0ee6dd4c..5b9610caf 100644 --- a/dev/core/src/com/google/gwt/dev/jjs/impl/jribble/JribbleAstBuilder.java +++ b/dev/core/src/com/google/gwt/dev/jjs/impl/jribble/JribbleAstBuilder.java @@ -584,18 +584,14 @@ private JStatement methodStatement(Statement s, LocalStack local) { } private JNewArray newArray(NewArray expr, LocalStack local) { + // semantics of NewArray are tricky, check jribble.proto file + // for examples JType type = mapper.getType(expr.getElementType()); // this is the element // type assert (expr.getInitExprCount() > 0 && expr.getDimensions() == 1) || expr.getInitExprCount() == 0; - if (expr.getInitExprCount() > 0) { - JArrayType arrayType = new JArrayType(type); - List initializers = new LinkedList(); - for (Expr e : expr.getInitExprList()) { - initializers.add(expression(e, local)); - } - return JNewArray.createInitializers(UNKNOWN, arrayType, initializers); - } else { + + if (expr.getDimensionExprCount() > 0) { for (int i = 0; i < expr.getDimensions(); i++) { type = new JArrayType(type); } @@ -607,6 +603,13 @@ private JNewArray newArray(NewArray expr, LocalStack local) { dims.add(JAbsentArrayDimension.INSTANCE); } return JNewArray.createDims(UNKNOWN, (JArrayType) type, dims); + } else { + JArrayType arrayType = new JArrayType(type); + List initializers = new LinkedList(); + for (Expr e : expr.getInitExprList()) { + initializers.add(expression(e, local)); + } + return JNewArray.createInitializers(UNKNOWN, arrayType, initializers); } } diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/jribble/jribble.proto b/dev/core/src/com/google/gwt/dev/jjs/impl/jribble/jribble.proto index a94f4a5db..dddd5b7ed 100644 --- a/dev/core/src/com/google/gwt/dev/jjs/impl/jribble/jribble.proto +++ b/dev/core/src/com/google/gwt/dev/jjs/impl/jribble/jribble.proto @@ -275,6 +275,31 @@ message MethodSignature { required Type returnType = 4; } +/* Encodes both new array expresions and array literals. + * Below there are Java expressions and corresponding + * protobufs that encode them. + * + * new Object[7] + * newArray { + * elementType ... //encoding for java.lang.Object + * dimensions: 1 + * dimensionExpr { ... //expr encoding 7 } + * } + * + * new Object[] {} + * newArray { + * elementType ... //encoding for java.lang.Object + * dimensions: 1 + * } + * + * new Object[] { ex1, ex2 } + * newArray { + * elementType ... //encoding for java.lang.Object + * dimensions: 1 + * initExpr { ... } //encoding for ex1 + * initExpr { ... } //encoding for ex2 + * } + */ message NewArray { required Type elementType = 1; required int32 dimensions = 2; diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/jribble/AstUtils.java b/dev/core/test/com/google/gwt/dev/jjs/impl/jribble/AstUtils.java new file mode 100644 index 000000000..58d21f1f8 --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/jjs/impl/jribble/AstUtils.java @@ -0,0 +1,227 @@ +package com.google.gwt.dev.jjs.impl.jribble; + +import com.google.gwt.dev.jjs.impl.jribble.JribbleProtos.*; + +import com.google.gwt.dev.jjs.impl.jribble.JribbleProtos.Declaration.DeclarationType; +import com.google.gwt.dev.jjs.impl.jribble.JribbleProtos.Expr.ExprType; +import com.google.gwt.dev.jjs.impl.jribble.JribbleProtos.Literal.LiteralType; +import com.google.gwt.dev.jjs.impl.jribble.JribbleProtos.Statement.StatementType; +import com.google.gwt.dev.jjs.impl.jribble.JribbleProtos.Type.TypeType; +import com.google.gwt.dev.protobuf.Descriptors.FieldDescriptor; + +import static java.util.Arrays.asList; + +import java.util.List; + +public class AstUtils { + + static GlobalName toGlobalName(String name) { + GlobalName.Builder b = GlobalName.newBuilder(); + int i = name.lastIndexOf("."); + if (i == -1) { + b.setName(name); + } else { + b.setPkg(name.substring(0, i)); + b.setName(name.substring(i + 1)); + } + return b.build(); + } + + static Type toGlobalNameType(String name) { + return Type.newBuilder().setType(TypeType.Named). + setNamedType(toGlobalName(name)).build(); + } + + static Type voidType() { + return Type.newBuilder().setType(TypeType.Void).build(); + } + + static Type stringType() { + return toGlobalNameType("java.lang.String"); + } + + static Type intType() { + return Type.newBuilder().setType(TypeType.Primitive). + setPrimitiveType(PrimitiveType.Int).build(); + } + + static MethodSignature signature(String owner, String name, List paramType, + Type returnType) { + return MethodSignature.newBuilder().setOwner(toGlobalName(owner)).setName(name). + addAllParamType(paramType).setReturnType(returnType).build(); + } + + static Expr expr(MethodCall methodCall) { + return Expr.newBuilder().setType(ExprType.MethodCall). + setMethodCall(methodCall).build(); + } + + static Expr expr(VarRef varRef) { + return Expr.newBuilder().setType(ExprType.VarRef).setVarRef(varRef). + build(); + } + + static Statement statement(Expr expr) { + return Statement.newBuilder().setType(StatementType.Expr). + setExpr(expr).build(); + } + + static Declaration declaration(Method method) { + return Declaration.newBuilder().setType(DeclarationType.Method). + setModifiers(Modifiers.getDefaultInstance()).setMethod(method).build(); + } + + static Declaration declaration(Modifiers mods, Method method) { + return Declaration.newBuilder().setType(DeclarationType.Method). + setModifiers(mods).setMethod(method).build(); + } + + static Declaration declaration(Modifiers mods, FieldDef field) { + return Declaration.newBuilder().setType(DeclarationType.Field). + setModifiers(mods).setFieldDef(field).build(); + } + + static ParamDef paramDef(String name, Type tpe) { + return ParamDef.newBuilder().setName(name).setTpe(tpe).build(); + } + + static Type primitive(PrimitiveType tpe) { + return Type.newBuilder().setType(TypeType.Primitive). + setPrimitiveType(tpe).build(); + } + + static Expr methodCall(Expr receiver, MethodSignature signature, Expr... args) { + MethodCall.Builder b = MethodCall.newBuilder(); + if (receiver != null) { + b.setReceiver(receiver); + } + b.setSignature(signature); + b.addAllArgument(asList(args)); + return expr(b.build()); + } + + static Expr varRef(String name) { + return expr(VarRef.newBuilder().setName(name).build()); + } + + static Type arrayType(Type elementType) { + return Type.newBuilder().setType(TypeType.Array). + setArrayElementType(elementType).build(); + } + + static Statement varDef(Type tpe, String name, Expr initializer) { + VarDef.Builder b = VarDef.newBuilder(); + b.setTpe(tpe).setName(name); + if (initializer != null) { + b.setInitializer(initializer); + } + return Statement.newBuilder().setType(StatementType.VarDef). + setVarDef(b.build()).build(); + } + + static Expr newArray(Type elementType, Expr... dims) { + NewArray n = NewArray.newBuilder().setElementType(elementType). + setDimensions(dims.length).addAllDimensionExpr(asList(dims)).build(); + return Expr.newBuilder().setType(ExprType.NewArray).setNewArray(n).build(); + } + + static Expr arrayInitializer(Type elementType, Expr... values) { + NewArray n = NewArray.newBuilder().setElementType(elementType). + setDimensions(1).addAllInitExpr(asList(values)).build(); + return Expr.newBuilder().setType(ExprType.NewArray).setNewArray(n).build(); + } + + static Expr literal(int x) { + Literal l = Literal.newBuilder().setType(LiteralType.Int). + setIntValue(x).build(); + return Expr.newBuilder().setType(ExprType.Literal).setLiteral(l).build(); + } + + static Expr literal(String x) { + Literal l = Literal.newBuilder().setType(LiteralType.String). + setStringValue(x).build(); + return Expr.newBuilder().setType(ExprType.Literal).setLiteral(l).build(); + } + + static Expr assignment(Expr lhs, Expr rhs) { + Assignment a = Assignment.newBuilder().setLhs(lhs).setRhs(rhs).build(); + return Expr.newBuilder().setType(ExprType.Assignment).setAssignment(a). + build(); + } + + static Expr arrayRef(Expr array, Expr index) { + ArrayRef ref = ArrayRef.newBuilder().setArray(array).setIndex(index).build(); + return Expr.newBuilder().setType(ExprType.ArrayRef).setArrayRef(ref).build(); + } + + static FieldDef fieldDef(Type tpe, String name, Expr initializer) { + FieldDef.Builder b = FieldDef.newBuilder(); + b.setTpe(tpe).setName(name); + if (initializer != null) { + b.setInitializer(initializer); + } + return b.build(); + } + + static String capitalize(String x) { + return Character.toUpperCase(x.charAt(0)) + x.substring(1); + } + + static Modifiers modifiers(String... modifs) { + Modifiers.Builder b = Modifiers.newBuilder(); + for (String x : asList(modifs)) { + FieldDescriptor desc = Modifiers.getDescriptor(). + findFieldByName("is" + capitalize(x)); + b.setField(desc, Boolean.TRUE); + } + return b.build(); + } + + static Expr fieldRef(Expr qualifier, String enclosingType, String name, Type tpe) { + FieldRef.Builder b = FieldRef.newBuilder(); + b.setEnclosingType(toGlobalName(enclosingType)).setName(name); + b.setTpe(tpe); + if (qualifier != null) { + b.setQualifier(qualifier); + } + return Expr.newBuilder().setType(ExprType.FieldRef). + setFieldRef(b.build()).build(); + } + + static Statement block(Statement... stmts) { + Block b = Block.newBuilder().addAllStatement(asList(stmts)).build(); + return Statement.newBuilder().setType(StatementType.Block). + setBlock(b).build(); + } + + static Expr newObject(String clazz, MethodSignature signature, Expr... args) { + assert signature.getName().equals("new"); + NewObject x = NewObject.newBuilder().setClazz(toGlobalName(clazz)). + setSignature(signature).addAllArgument(asList(args)).build(); + return Expr.newBuilder().setType(ExprType.NewObject).setNewObject(x).build(); + } + + static Catch catchh(String tpe, String param, Statement body) { + return Catch.newBuilder().setTpe(toGlobalName(tpe)).setParam(param). + setBody(body).build(); + } + + static Statement tryy(Statement block, Statement finalizer, Catch... catches) { + Try.Builder b = Try.newBuilder(); + b.setBlock(block).addAllCatch(asList(catches)); + if (finalizer != null) { + b.setFinalizer(finalizer); + } + return Statement.newBuilder().setType(StatementType.Try).setTryStat(b.build()).build(); + } + + static Statement returnn(Expr expr) { + Return.Builder b = Return.newBuilder(); + if (expr != null) { + b.setExpression(expr); + } + return Statement.newBuilder().setType(StatementType.Return). + setReturnStat(b.build()).build(); + } + +} diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/jribble/JribbleAstBuilderTest.java b/dev/core/test/com/google/gwt/dev/jjs/impl/jribble/JribbleAstBuilderTest.java new file mode 100644 index 000000000..961f9d974 --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/jjs/impl/jribble/JribbleAstBuilderTest.java @@ -0,0 +1,524 @@ +/* + * Copyright 2012 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.dev.jjs.impl.jribble; + +import static com.google.gwt.dev.jjs.impl.jribble.AstUtils.*; + +import com.google.gwt.dev.jjs.ast.JClassType; +import com.google.gwt.dev.jjs.ast.JDeclarationStatement; +import com.google.gwt.dev.jjs.ast.JDeclaredType; +import com.google.gwt.dev.jjs.ast.JExpressionStatement; +import com.google.gwt.dev.jjs.ast.JMethod; +import com.google.gwt.dev.jjs.ast.JMethodBody; +import com.google.gwt.dev.jjs.ast.JMethodCall; +import com.google.gwt.dev.jjs.ast.JNewArray; +import com.google.gwt.dev.jjs.ast.JNode; +import com.google.gwt.dev.jjs.ast.JPrimitiveType; +import com.google.gwt.dev.jjs.impl.SourceGenerationVisitor; +import com.google.gwt.dev.jjs.impl.jribble.JribbleProtos.Block; +import com.google.gwt.dev.jjs.impl.jribble.JribbleProtos.Catch; +import com.google.gwt.dev.jjs.impl.jribble.JribbleProtos.Declaration; +import com.google.gwt.dev.jjs.impl.jribble.JribbleProtos.DeclaredType; +import com.google.gwt.dev.jjs.impl.jribble.JribbleProtos.Expr; +import com.google.gwt.dev.jjs.impl.jribble.JribbleProtos.Expr.ExprType; +import com.google.gwt.dev.jjs.impl.jribble.JribbleProtos.GlobalName; +import com.google.gwt.dev.jjs.impl.jribble.JribbleProtos.Literal; +import com.google.gwt.dev.jjs.impl.jribble.JribbleProtos.Literal.LiteralType; +import com.google.gwt.dev.jjs.impl.jribble.JribbleProtos.Method; +import com.google.gwt.dev.jjs.impl.jribble.JribbleProtos.MethodCall; +import com.google.gwt.dev.jjs.impl.jribble.JribbleProtos.MethodSignature; +import com.google.gwt.dev.jjs.impl.jribble.JribbleProtos.Modifiers; +import com.google.gwt.dev.jjs.impl.jribble.JribbleProtos.ParamDef; +import com.google.gwt.dev.jjs.impl.jribble.JribbleProtos.PrimitiveType; +import com.google.gwt.dev.jjs.impl.jribble.JribbleProtos.Statement; +import com.google.gwt.dev.jjs.impl.jribble.JribbleProtos.Statement.StatementType; +import com.google.gwt.dev.jjs.impl.jribble.JribbleProtos.Type; +import com.google.gwt.dev.jjs.impl.jribble.JribbleProtos.VarRef; +import com.google.gwt.dev.util.AbstractTextOutput; +import com.google.gwt.dev.util.DefaultTextOutput; +import com.google.gwt.dev.util.TextOutput; +import com.google.gwt.thirdparty.guava.common.base.Charsets; +import com.google.gwt.thirdparty.guava.common.io.Resources; + +import junit.framework.Assert; +import junit.framework.TestCase; + +import static java.util.Arrays.asList; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Collections; +import java.util.List; + +/** + * Tests for {@link JribbleAstBuilder}. + */ +public class JribbleAstBuilderTest extends TestCase { + + private static class DeclaredTypeBuilder { + private Modifiers modifs = Modifiers.getDefaultInstance(); + private GlobalName ext = toGlobalName("java.lang.Object"); + private List impls = Collections.emptyList(); + private List classBody = Collections.emptyList(); + private Declaration defaultCstr; + + private DeclaredType.Builder b = DeclaredType.newBuilder(); + + private DeclaredTypeBuilder(String name, boolean isInterface) { + b.setName(toGlobalName(name)); + b.setIsInterface(isInterface); + MethodDefBuilder cstr = new MethodDefBuilder("new"); + cstr.stmts = asList(statement(newSuperCstrCall("java.lang.Object"))); + defaultCstr = declaration(cstr.buildCstr()); + } + + private DeclaredType build() { + if (ext != null) { + b.setExt(ext); + } + b.addAllImplements(impls); + b.addAllMember(classBody); + b.setModifiers(modifs); + return b.build(); + } + } + private static class MethodDefBuilder { + private final String name; + List params = java.util.Collections.emptyList(); + List stmts = java.util.Collections.emptyList(); + Type returnType = voidType(); + + private Method.Builder b = Method.newBuilder(); + + private MethodDefBuilder(String name) { + this.name = name; + } + + private Method build() { + b.setReturnType(returnType); + b.setName(name); + b.addAllParamDef(params); + Block block = Block.newBuilder().addAllStatement(stmts).build(); + Statement stmt = Statement.newBuilder().setType(StatementType.Block) + .setBlock(block).build(); + b.setBody(stmt); + return b.build(); + } + + private Method buildCstr() { + b.setIsConstructor(true); + b.setReturnType(returnType); + b.setName(name); + b.addAllParamDef(params); + Block block = Block.newBuilder().addAllStatement(stmts).build(); + Statement stmt = Statement.newBuilder().setType(StatementType.Block) + .setBlock(block).build(); + b.setBody(stmt); + return b.build(); + } + } + private static final List noTypes = Collections.emptyList(); + + private static final Expr superRef = + Expr.newBuilder().setType(ExprType.SuperRef).build(); + + private static final Expr thisRef = + Expr.newBuilder().setType(ExprType.ThisRef).build(); + + private static Expr newSuperCstrCall(String superClassName) { + // jribble uses "super" when calling super constructors, e.g.: + // (Ljava/lang/Object;::super()V;)(); + MethodSignature.Builder msb = MethodSignature.newBuilder(); + msb.setName("new"); + msb.setOwner(toGlobalName(superClassName)); + msb.setReturnType(voidType()); + MethodCall.Builder mb = MethodCall.newBuilder(); + mb.setSignature(msb.build()); + return Expr.newBuilder().setType(ExprType.MethodCall) + .setMethodCall(mb.build()).build(); + } + + private static Statement newWindowAlert(Expr message) { + MethodCall.Builder b = MethodCall.newBuilder(); + MethodSignature s = signature("gwt.Window", "alert", + asList(toGlobalNameType("java.lang.String")), voidType()); + b.setSignature(s); + b.addArgument(message); + return statement(expr(b.build())); + } + + private static Statement newWindowAlert(String message) { + Literal lit = Literal.newBuilder().setType(LiteralType.String) + .setStringValue(message).build(); + Expr alertParam = Expr.newBuilder().setType(ExprType.Literal) + .setLiteral(lit).build(); + return newWindowAlert(alertParam); + } + + private static Statement newWindowAlertVar(String varName) { + VarRef varRef = VarRef.newBuilder().setName(varName).build(); + return newWindowAlert(expr(varRef)); + } + + private static JDeclaredType process(DeclaredTypeBuilder foo) { + return new JribbleAstBuilder().process(foo.build()).types.get(0); + } + + public void testArrays() throws Exception { + DeclaredTypeBuilder foo = new DeclaredTypeBuilder("foo.Bar", false); + MethodDefBuilder zaz = new MethodDefBuilder("zaz"); + + Type stringA = arrayType(stringType()); + Type stringAA = arrayType(stringA); + + Statement s1 = varDef(stringAA, "aa", newArray(stringType(), literal(1), literal(1))); + + Statement s2 = statement(assignment(arrayRef(arrayRef(varRef("aa"), literal(0)), + literal(0)), literal("s"))); + + Statement s3 = varDef(stringA, "a", arrayInitializer(stringType(), + literal("1"), literal("2"))); + + Statement s4 = statement(assignment(arrayRef(varRef("a"), literal(0)), literal("0"))); + + Statement s5 = varDef(stringA, "b", arrayInitializer(stringType())); + + zaz.stmts = asList(s1, s2, s3, s4, s5); + foo.classBody = asList(declaration(zaz.build()), foo.defaultCstr); + + JClassType fooType = (JClassType) process(foo); + assertEquals(fooType, "testArrays"); + JMethod zazMethod = fooType.getMethods().get(3); + // String[][] + JDeclarationStatement decl1 = (JDeclarationStatement) ((JMethodBody) zazMethod.getBody()).getStatements().get(0); + Assert.assertEquals(2, ((JNewArray) decl1.getInitializer()).dims.size()); + Assert.assertEquals(2, ((JNewArray) decl1.getInitializer()).getArrayType().getDims()); + // String[] + JDeclarationStatement decl2 = (JDeclarationStatement) ((JMethodBody) zazMethod.getBody()).getStatements().get(2); + Assert.assertEquals(null, ((JNewArray) decl2.getInitializer()).dims); + Assert.assertEquals(1, ((JNewArray) decl2.getInitializer()).getArrayType().getDims()); + // String[] {} + JDeclarationStatement decl3 = (JDeclarationStatement) ((JMethodBody) zazMethod.getBody()).getStatements().get(4); + Assert.assertEquals(null, ((JNewArray) decl3.getInitializer()).dims); + Assert.assertEquals(Collections.EMPTY_LIST, ((JNewArray) decl3.getInitializer()).initializers); + Assert.assertEquals(1, ((JNewArray) decl3.getInitializer()).getArrayType().getDims()); + } + + public void testConstructors() throws Exception { + DeclaredTypeBuilder foo = new DeclaredTypeBuilder("foo.Bar", false); + + MethodDefBuilder b1 = new MethodDefBuilder("new"); + b1.params = asList(paramDef("s", stringType())); + b1.stmts = asList(statement(newSuperCstrCall("java.lang.Object")), + newWindowAlertVar("s")); + Declaration cstr1 = declaration(b1.buildCstr()); + + // cstr2 calls cstr1, which means it shouldn't have an $init call + Expr cstr1Call = methodCall(thisRef, signature("foo.Bar", "new", + asList(stringType()), voidType()), literal("a")); + + MethodDefBuilder b2 = new MethodDefBuilder("new"); + b2.params = asList(paramDef("i", toGlobalNameType("java.lang.Integer"))); + b2.stmts = asList(statement(cstr1Call), newWindowAlertVar("i")); + Declaration cstr2 = declaration(b2.buildCstr()); + + foo.classBody = asList(cstr1, cstr2); + + JClassType fooType = (JClassType) process(foo); + assertEquals(fooType, "testConstructors"); + + // ensure cstr2 calls cstr1 + JMethod cstr1m = fooType.getMethods().get(3); + JMethod cstr2m = fooType.getMethods().get(4); + JMethodCall c = + (JMethodCall) ((JExpressionStatement) ((JMethodBody) cstr2m.getBody()).getStatements().get( + 0)).getExpr(); + Assert.assertEquals(cstr1m, c.getTarget()); + Assert.assertEquals(true, c.isStaticDispatchOnly()); + } + + public void testEmptyClass() throws Exception { + DeclaredTypeBuilder foo = new DeclaredTypeBuilder("foo.Bar", false); + foo.ext = null; + foo.classBody = asList(foo.defaultCstr); + JDeclaredType fooType = process(foo); + assertEquals(fooType, "testEmptyClass"); + } + + public void testFields() throws Exception { + DeclaredTypeBuilder foo = new DeclaredTypeBuilder("foo.Bar", false); + + // initialized + Declaration f1 = declaration(modifiers("private"), + fieldDef(stringType(), "f1", literal("f1"))); + + // un-initialized + Declaration f2 = declaration(modifiers("private"), + fieldDef(stringType(), "f2", null)); + + // static initialized + Declaration f3 = declaration(modifiers("private", "static"), + fieldDef(stringType(), "f3", literal("f3"))); + + // static un-initialized + Declaration f4 = declaration(modifiers("private", "static"), + fieldDef(stringType(), "f4", null)); + + MethodDefBuilder zaz = new MethodDefBuilder("zaz"); + zaz.params = asList(paramDef("other", toGlobalNameType("foo.Bar"))); + + Statement assignf1 = statement( + assignment(fieldRef(thisRef, "foo.Bar", "f1", stringType()), literal("f11"))); + + Statement assignf2 = statement( + assignment(fieldRef(varRef("other"), "foo.Bar", "f2", stringType()), literal("f22"))); + + Statement assignf3 = statement( + assignment(fieldRef(null, "foo.Bar", "f3", stringType()), literal("f33"))); + + Statement assignfOther = statement( + assignment(fieldRef(null, "foo.Other", "i", intType()), literal(1))); + + zaz.stmts = asList(assignf1, assignf2, assignf3, assignfOther); + foo.classBody = asList(f1, f2, f3, f4, + declaration(zaz.build()), foo.defaultCstr); + + JDeclaredType fooType = process(foo); + assertEquals(fooType, "testFields"); + assertEquals(4, fooType.getFields().size()); + } + + public void testInterface() throws Exception { + // are interface methods abstract? otherwise SourceGenerationVisitor fails + + MethodDefBuilder b = new MethodDefBuilder("zaz"); + b.returnType = stringType(); + b.params = asList(paramDef("x", toGlobalNameType("java.lang.Integer"))); + Declaration zaz = declaration( + Modifiers.newBuilder().setIsAbstract(true).build(), + b.build()); + + DeclaredTypeBuilder b1 = new DeclaredTypeBuilder("foo.SpecialList", true); + b1.modifs = Modifiers.newBuilder().setIsPrivate(true).build(); + b1.impls = asList(toGlobalName("java.util.List")); + b1.classBody = asList(zaz); + DeclaredType specialList = b1.build(); + + JDeclaredType fooType = new JribbleAstBuilder().process(specialList).types.get(0); + assertEquals(fooType, "testInterface"); + Assert.assertTrue(fooType.getMethods().get(1).isAbstract()); + } + + public void testInterfacesTreatedAsClasses() throws Exception { + JribbleAstBuilder jab = new JribbleAstBuilder(); + // do one unit that refers to another via a method call, assumed to be a class + DeclaredTypeBuilder foo = new DeclaredTypeBuilder("foo.Bar", false); + MethodDefBuilder zaz = new MethodDefBuilder("zaz"); + zaz.params = asList(paramDef("l", toGlobalNameType("java.util.List"))); + Statement s1 = statement(methodCall(varRef("l"), signature("java.util.List", "add", + asList(toGlobalNameType("java.lang.Object")), voidType()), + varRef("l"))); + zaz.stmts = asList(s1); + foo.classBody = asList(foo.defaultCstr, declaration(zaz.build())); + jab.process(foo.build()); + // now do another unit that implements java.util.List, that requires it to be an interface + DeclaredTypeBuilder foo2 = new DeclaredTypeBuilder("foo.Bar2", false); + foo2.impls = asList(toGlobalName("java.util.List")); + JDeclaredType foo2Type = process(foo2); + assertEquals(foo2Type.getImplements().get(0).getName(), "java.util.List"); + } + + public void testLocalVariables() throws Exception { + DeclaredTypeBuilder foo = new DeclaredTypeBuilder("foo.Bar", false); + MethodDefBuilder zaz = new MethodDefBuilder("zaz"); + + Statement intDef = varDef(primitive(PrimitiveType.Int), "i", null); + Statement intAssignment = statement(assignment(varRef("i"), literal(10))); + Statement stringDef = varDef(toGlobalNameType("java.lang.String"), "s", null); + Statement stringAssignment = statement(assignment(varRef("s"), literal("string"))); + + zaz.stmts = asList(intDef, intAssignment, stringDef, stringAssignment); + foo.classBody = asList(declaration(zaz.build()), foo.defaultCstr); + + JDeclaredType fooType = process(foo); + assertEquals(fooType, "testLocalVariables"); + } + + public void testMethodCall() throws Exception { + DeclaredTypeBuilder foo = new DeclaredTypeBuilder("foo.Bar", false); + MethodDefBuilder zaz = new MethodDefBuilder("zaz"); + zaz.stmts = asList(newWindowAlert("hello")); + foo.classBody = asList(foo.defaultCstr, declaration(zaz.build())); + + JDeclaredType fooType = process(foo); + JMethodCall call = + (JMethodCall) ((JExpressionStatement) ((JMethodBody) fooType.getMethods().get(4) + .getBody()).getStatements().get(0)).getExpr(); + Assert.assertEquals("alert(Ljava/lang/String;)V", call.getTarget().getSignature()); + } + + public void testNewCall() throws Exception { + DeclaredTypeBuilder foo = new DeclaredTypeBuilder("foo.Bar", false); + MethodDefBuilder zaz = new MethodDefBuilder("zaz"); + + MethodSignature cstrSig = signature("java.util.ArrayList", "new", + asList(primitive(PrimitiveType.Int)), toGlobalNameType("java.util.ArrayList")); + + Expr cstrArg = literal(1); + Statement varDef = varDef(toGlobalNameType("java.util.List"), "l", + newObject("java.util.ArrayList", cstrSig, cstrArg)); + + MethodSignature addSig = signature("java.util.List", "add", + asList(toGlobalNameType("java.lang.Object")), voidType()); + + Expr addParam = literal(1); // should be boxed + Statement addCall = statement(methodCall(varRef("l"), addSig, addParam)); + + zaz.stmts = asList(varDef, addCall); + foo.classBody = asList(declaration(zaz.build()), foo.defaultCstr); + + JDeclaredType fooType = process(foo); + assertEquals(fooType, "testNewCall"); + // ensure the external JMethod was frozen so getSignature doesn't NPE + JExpressionStatement addStmt = + (JExpressionStatement) ((JMethodBody) fooType.getMethods().get(3).getBody()).getBlock() + .getStatements().get(1); + Assert.assertEquals("add(Ljava/lang/Object;)V", ((JMethodCall) addStmt.getExpr()).getTarget().getSignature()); + } + + public void testOneStringMethod() throws Exception { + DeclaredTypeBuilder foo = new DeclaredTypeBuilder("foo.Bar", false); + MethodDefBuilder zaz = new MethodDefBuilder("zaz"); + zaz.returnType = toGlobalNameType("java.lang.String"); + zaz.stmts = asList(returnn(literal("hello"))); + foo.classBody = asList(declaration(zaz.build()), foo.defaultCstr); + + JDeclaredType fooType = process(foo); + assertEquals(fooType, "testOneStringMethod"); + Assert.assertEquals("zaz()Ljava/lang/String;", fooType.getMethods().get(3).getSignature()); + Assert.assertFalse(fooType.isExternal()); + Assert.assertTrue(fooType.getMethods().get(3).getOriginalReturnType().isExternal()); + } + + public void testOneVoidMethod() throws Exception { + DeclaredTypeBuilder foo = new DeclaredTypeBuilder("foo.Bar", false); + MethodDefBuilder zaz = new MethodDefBuilder("zaz"); + zaz.stmts = asList(returnn(null)); + foo.classBody = asList(declaration(zaz.build()),foo.defaultCstr); + + JDeclaredType fooType = process(foo); + assertEquals(fooType, "testOneVoidMethod"); + Assert.assertEquals("zaz()V", fooType.getMethods().get(3).getSignature()); + Assert.assertFalse(fooType.isExternal()); + Assert.assertEquals(JPrimitiveType.VOID, fooType.getMethods().get(3).getOriginalReturnType()); + } + + public void testSuperCall() throws Exception { + DeclaredTypeBuilder foo = new DeclaredTypeBuilder("foo.Bar", false); + MethodDefBuilder zaz = new MethodDefBuilder("toString"); + MethodCall.Builder b = MethodCall.newBuilder(); + b.setSignature(signature("java.lang.Object", "toString", noTypes, stringType())); + b.setReceiver(superRef); + Statement superCall = statement(expr(b.build())); + zaz.stmts = asList(superCall); + zaz.returnType = toGlobalNameType("java.lang.String"); + foo.classBody = asList(declaration(zaz.build()), foo.defaultCstr); + + assertEquals(process(foo), "testSuperCall"); + } + + public void testTryCatchFinally() throws Exception { + DeclaredTypeBuilder foo = new DeclaredTypeBuilder("foo.Bar", false); + MethodDefBuilder zaz = new MethodDefBuilder("zaz"); + + Statement intDef = varDef(primitive(PrimitiveType.Int), "i", null); + Statement tryBlock = block(intDef); + + Catch catchh = catchh("java.lang.Exception", "e", block(newWindowAlert("caught"))); + + Statement finalBlock = block(newWindowAlert("finally")); + + Statement tryStmt = tryy(tryBlock, finalBlock, catchh); + + zaz.stmts = asList(tryStmt); + foo.classBody = asList(declaration(zaz.build()), foo.defaultCstr); + + JDeclaredType fooType = process(foo); + assertEquals(fooType, "testTryCatchFinally"); + JMethodBody body = (JMethodBody) fooType.getMethods().get(3).getBody(); + Assert.assertEquals("zaz", body.getMethod().getName()); + // ensure both "int i" and "Exception e" are locals + Assert.assertEquals(2, body.getLocals().size()); + } + + /** + * Dumps @{code node} and checks if result is equal to one stored + * in file described by @{code name}. In case both dumps are not + * equal the actual result is dumped to file so it can be further + * inspected. + * @param node AST to be dumped + * @param name part of file name that describes the dump. + * @throws IOException + */ + private void assertEquals(JNode node, String name) throws IOException { + TextOutput out = new DefaultTextOutput(false); + SourceGenerationVisitor v = new SourceGenerationVisitor(out); + v.accept(node); + final String actual = out.toString(); + + final String expected; + { + String resourceName = + getClass().getName().replace('.', '/') + "." + name + ".ast"; + java.net.URL url = Resources.getResource(resourceName); + expected = Resources.toString(url, Charsets.UTF_8); + } + + try { + Assert.assertEquals(expected, actual); + } catch (AssertionError ex) { + dump(node, name); + throw ex; + } + } + + /** + * Dumps {@code node} to file relative to current directory. + * @param node AST to be dumped. + * @param name part of file name that describes the dump. + * @throws IOException + */ + private void dump(JNode node, String name) throws IOException { + String dumpFilePath = + getClass().getName().replace('.', '/') + "." + name + ".ast.actual"; + File dumpFile = new File(dumpFilePath); + System.out.println("Dumping ast to " + dumpFile.getCanonicalPath()); + dumpFile.getParentFile().mkdirs(); + dumpFile.createNewFile(); + + FileOutputStream os = new FileOutputStream(dumpFile, false); + final PrintWriter pw = new PrintWriter(os); + TextOutput out = new AbstractTextOutput(false) { + { + setPrintWriter(pw); + } + }; + SourceGenerationVisitor v = new SourceGenerationVisitor(out); + v.accept(node); + pw.close(); + } + +} diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/jribble/JribbleAstBuilderTest.testArrays.ast b/dev/core/test/com/google/gwt/dev/jjs/impl/jribble/JribbleAstBuilderTest.testArrays.ast new file mode 100644 index 000000000..0e3e07245 --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/jjs/impl/jribble/JribbleAstBuilderTest.testArrays.ast @@ -0,0 +1,26 @@ +class Bar extends Object { + private static final void $clinit(){ + Object.$clinit(); + } + + private final void $init(){ + } + + public Class getClass(){ + return Bar.class; + } + + void zaz(){ + String[][] aa = new String[][][1][1]; + aa[0][0] = "s"; + String[] a = new String[] {"1", "2"}; + a[0] = "0"; + String[] b = new String[] {}; + } + + public Bar(){ + this.$init(); + super(); + } + +} \ No newline at end of file diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/jribble/JribbleAstBuilderTest.testBinaryOperationTypePromotion.ast b/dev/core/test/com/google/gwt/dev/jjs/impl/jribble/JribbleAstBuilderTest.testBinaryOperationTypePromotion.ast new file mode 100644 index 000000000..4bbb0aea7 --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/jjs/impl/jribble/JribbleAstBuilderTest.testBinaryOperationTypePromotion.ast @@ -0,0 +1,22 @@ +class Bar extends Object { + private static final void $clinit(){ + Object.$clinit(); + } + + private final void $init(){ + } + + public Class getClass(){ + return Bar.class; + } + + void zaz(){ + double d = 10.0 + 1; + } + + public Bar(){ + this.$init(); + super(); + } + +} \ No newline at end of file diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/jribble/JribbleAstBuilderTest.testConstructors.ast b/dev/core/test/com/google/gwt/dev/jjs/impl/jribble/JribbleAstBuilderTest.testConstructors.ast new file mode 100644 index 000000000..91fe4c803 --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/jjs/impl/jribble/JribbleAstBuilderTest.testConstructors.ast @@ -0,0 +1,24 @@ +class Bar extends Object { + private static final void $clinit(){ + Object.$clinit(); + } + + private final void $init(){ + } + + public Class getClass(){ + return Bar.class; + } + + public Bar(String s){ + this.$init(); + super(); + Window.alert(s); + } + + public Bar(Integer i){ + this("a"); + Window.alert(i); + } + +} \ No newline at end of file diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/jribble/JribbleAstBuilderTest.testEmptyClass.ast b/dev/core/test/com/google/gwt/dev/jjs/impl/jribble/JribbleAstBuilderTest.testEmptyClass.ast new file mode 100644 index 000000000..88e6d73b4 --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/jjs/impl/jribble/JribbleAstBuilderTest.testEmptyClass.ast @@ -0,0 +1,18 @@ +class Bar extends Object { + private static final void $clinit(){ + Object.$clinit(); + } + + private final void $init(){ + } + + public Class getClass(){ + return Bar.class; + } + + public Bar(){ + this.$init(); + super(); + } + +} \ No newline at end of file diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/jribble/JribbleAstBuilderTest.testFields.ast b/dev/core/test/com/google/gwt/dev/jjs/impl/jribble/JribbleAstBuilderTest.testFields.ast new file mode 100644 index 000000000..2d8713ca3 --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/jjs/impl/jribble/JribbleAstBuilderTest.testFields.ast @@ -0,0 +1,35 @@ +class Bar extends Object { + String f1 + + String f2 + + static String f3 + + static String f4 + + private static final void $clinit(){ + Object.$clinit(); + static String f3 = "f3"; + } + + private final void $init(){ + String f1 = "f1"; + } + + public Class getClass(){ + return Bar.class; + } + + void zaz(Bar other){ + this.f1 = "f11"; + other.f2 = "f22"; + Bar.f3 = "f33"; + Other.i = 1; + } + + public Bar(){ + this.$init(); + super(); + } + +} \ No newline at end of file diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/jribble/JribbleAstBuilderTest.testInterface.ast b/dev/core/test/com/google/gwt/dev/jjs/impl/jribble/JribbleAstBuilderTest.testInterface.ast new file mode 100644 index 000000000..19ec6ca5d --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/jjs/impl/jribble/JribbleAstBuilderTest.testInterface.ast @@ -0,0 +1,8 @@ +interface SpecialList extends List { + private static final void $clinit(){ + } + + abstract String zaz(Integer x); + + +} \ No newline at end of file diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/jribble/JribbleAstBuilderTest.testLocalVariables.ast b/dev/core/test/com/google/gwt/dev/jjs/impl/jribble/JribbleAstBuilderTest.testLocalVariables.ast new file mode 100644 index 000000000..36113bd62 --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/jjs/impl/jribble/JribbleAstBuilderTest.testLocalVariables.ast @@ -0,0 +1,25 @@ +class Bar extends Object { + private static final void $clinit(){ + Object.$clinit(); + } + + private final void $init(){ + } + + public Class getClass(){ + return Bar.class; + } + + void zaz(){ + int i; + i = 10; + String s; + s = "string"; + } + + public Bar(){ + this.$init(); + super(); + } + +} \ No newline at end of file diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/jribble/JribbleAstBuilderTest.testNewCall.ast b/dev/core/test/com/google/gwt/dev/jjs/impl/jribble/JribbleAstBuilderTest.testNewCall.ast new file mode 100644 index 000000000..2a31a0e7f --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/jjs/impl/jribble/JribbleAstBuilderTest.testNewCall.ast @@ -0,0 +1,23 @@ +class Bar extends Object { + private static final void $clinit(){ + Object.$clinit(); + } + + private final void $init(){ + } + + public Class getClass(){ + return Bar.class; + } + + void zaz(){ + List l = new ArrayList(1); + l.add(1); + } + + public Bar(){ + this.$init(); + super(); + } + +} \ No newline at end of file diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/jribble/JribbleAstBuilderTest.testOneStringMethod.ast b/dev/core/test/com/google/gwt/dev/jjs/impl/jribble/JribbleAstBuilderTest.testOneStringMethod.ast new file mode 100644 index 000000000..c122adcda --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/jjs/impl/jribble/JribbleAstBuilderTest.testOneStringMethod.ast @@ -0,0 +1,22 @@ +class Bar extends Object { + private static final void $clinit(){ + Object.$clinit(); + } + + private final void $init(){ + } + + public Class getClass(){ + return Bar.class; + } + + String zaz(){ + return "hello"; + } + + public Bar(){ + this.$init(); + super(); + } + +} \ No newline at end of file diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/jribble/JribbleAstBuilderTest.testOneVoidMethod.ast b/dev/core/test/com/google/gwt/dev/jjs/impl/jribble/JribbleAstBuilderTest.testOneVoidMethod.ast new file mode 100644 index 000000000..33c64c337 --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/jjs/impl/jribble/JribbleAstBuilderTest.testOneVoidMethod.ast @@ -0,0 +1,22 @@ +class Bar extends Object { + private static final void $clinit(){ + Object.$clinit(); + } + + private final void $init(){ + } + + public Class getClass(){ + return Bar.class; + } + + void zaz(){ + return; + } + + public Bar(){ + this.$init(); + super(); + } + +} \ No newline at end of file diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/jribble/JribbleAstBuilderTest.testSuperCall.ast b/dev/core/test/com/google/gwt/dev/jjs/impl/jribble/JribbleAstBuilderTest.testSuperCall.ast new file mode 100644 index 000000000..4ba86dea5 --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/jjs/impl/jribble/JribbleAstBuilderTest.testSuperCall.ast @@ -0,0 +1,22 @@ +class Bar extends Object { + private static final void $clinit(){ + Object.$clinit(); + } + + private final void $init(){ + } + + public Class getClass(){ + return Bar.class; + } + + String toString(){ + super(); + } + + public Bar(){ + this.$init(); + super(); + } + +} \ No newline at end of file diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/jribble/JribbleAstBuilderTest.testTryCatchFinally.ast b/dev/core/test/com/google/gwt/dev/jjs/impl/jribble/JribbleAstBuilderTest.testTryCatchFinally.ast new file mode 100644 index 000000000..dab94202a --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/jjs/impl/jribble/JribbleAstBuilderTest.testTryCatchFinally.ast @@ -0,0 +1,28 @@ +class Bar extends Object { + private static final void $clinit(){ + Object.$clinit(); + } + + private final void $init(){ + } + + public Class getClass(){ + return Bar.class; + } + + void zaz(){ + try { + int i; + } catch (Exception e) { + Window.alert("caught"); + } finally { + Window.alert("finally"); + } + } + + public Bar(){ + this.$init(); + super(); + } + +} \ No newline at end of file diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/jribble/JribbleReferenceMapperTest.java b/dev/core/test/com/google/gwt/dev/jjs/impl/jribble/JribbleReferenceMapperTest.java new file mode 100644 index 000000000..affc68454 --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/jjs/impl/jribble/JribbleReferenceMapperTest.java @@ -0,0 +1,61 @@ +/* + * Copyright 2012 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.dev.jjs.impl.jribble; + +import static com.google.gwt.dev.jjs.impl.jribble.AstUtils.*; +import static com.google.gwt.thirdparty.guava.common.collect.Sets.newHashSet; + +import com.google.gwt.dev.jjs.SourceOrigin; +import com.google.gwt.dev.jjs.ast.JClassType; +import com.google.gwt.dev.jjs.impl.jribble.JribbleProtos.DeclaredType; +import com.google.gwt.dev.jjs.impl.jribble.JribbleProtos.Modifiers; +import com.google.gwt.dev.jjs.impl.jribble.JribbleProtos.PrimitiveType; + +import junit.framework.Assert; +import junit.framework.TestCase; + +/** + * Tests for {@link JribbleReferenceMapper}. + */ +public class JribbleReferenceMapperTest extends TestCase { + + public void testSourceTypeIsNotConsideredTouched() { + JribbleReferenceMapper m = new JribbleReferenceMapper(); + JClassType gwtType = new JClassType(SourceOrigin.UNKNOWN, "foo.T5", false, false); + DeclaredType decl = DeclaredType.newBuilder() + .setName(toGlobalName("foo.T5")).setModifiers(Modifiers.getDefaultInstance()).build(); + m.setSourceType(decl, gwtType); + Assert.assertEquals(newHashSet(), m.getTouchedTypes()); + } + + public void testTouchedTypes() { + JribbleReferenceMapper m = new JribbleReferenceMapper(); + m.getType(toGlobalNameType("foo.T1")); + m.getType(voidType()); + m.getType(primitive(PrimitiveType.Int)); + m.getType(arrayType(toGlobalNameType(("foo.T4")))); + m.getType(arrayType(primitive(PrimitiveType.Double))); + m.getClassType("foo.T2"); + m.getInterfaceType("foo.T3"); + Assert.assertEquals(newHashSet("foo.T1", "foo.T2", "foo.T3", "foo.T4"), m.getTouchedTypes()); + + m.clearSource(); + Assert.assertEquals(newHashSet(), m.getTouchedTypes()); + + m.getType(toGlobalNameType("foo.T1")); + m.getType(arrayType(primitive(PrimitiveType.Double))); + Assert.assertEquals(newHashSet("foo.T1"), m.getTouchedTypes()); + } + +}