/* * Copyright 2009 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; import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.dev.javac.testing.impl.MockJavaResource; import com.google.gwt.dev.jjs.ast.JField; import com.google.gwt.dev.jjs.ast.JMethod; import com.google.gwt.dev.jjs.ast.JNode; import com.google.gwt.dev.jjs.ast.JPrimitiveType; import com.google.gwt.dev.jjs.ast.JProgram; import com.google.gwt.dev.jjs.impl.JsniRefLookup.ErrorReporter; import com.google.gwt.dev.util.JsniRef; /** * Tests class {@link JsniRefLookup}. */ public class JsniRefLookupTest extends JJSTestBase { private class MockErrorReporter implements ErrorReporter { private String error = null; public void assertHasError() { assertTrue("Expected a lookup failure", error != null); } public void assertNoError() { assertTrue("Unexpected error: " + error, error == null); } public void reportError(String error) { this.error = error; } } private JProgram program; @Override public void setUp() { sourceOracle.addOrReplace(new MockJavaResource("test.Intf") { @Override public CharSequence getContent() { StringBuffer code = new StringBuffer(); code.append("package test;\n"); code.append("public interface Intf {\n"); code.append(" public int addTwoOverloaded(int x);\n"); code.append(" public int addOne(int x);\n"); code.append(" public int foo(int x);\n"); code.append(" public double foo(double x);\n"); code.append("}\n"); return code; } }); sourceOracle.addOrReplace(new MockJavaResource("test.Foo") { @Override public CharSequence getContent() { StringBuffer code = new StringBuffer(); code.append("package test;\n"); code.append("public class Foo implements Intf {\n"); code.append(" public Foo() { }\n"); code.append(" public Foo(int x) { }\n"); code.append(" public static int intStatic;\n"); code.append(" public int intInstance;\n"); code.append(" public int addOne(int x) { return x+1; }\n"); code.append(" public int addTwoOverloaded(int x) { return x+2; }\n"); code.append(" public double addTwoOverloaded(double x) { return x+2; }\n"); code.append(" public int foo(int x) { return x+1; }\n"); code.append(" public double foo(double x) { return x+1; }\n"); code.append(" public int bar(int x) { return x+1; }\n"); code.append("}\n"); return code; } }); sourceOracle.addOrReplace(new MockJavaResource("test.Bar") { @Override public CharSequence getContent() { StringBuffer code = new StringBuffer(); code.append("package test;\n"); code.append("public class Bar extends Foo {\n"); code.append(" public Bar() { }\n"); code.append(" public int foo(int x) { return x+1; }\n"); code.append(" public int bar(int x) { return x+1; }\n"); code.append(" public double bar(double x) { return x+1; }\n"); code.append("}\n"); return code; } }); sourceOracle.addOrReplace(new MockJavaResource("test.GenericClass") { @Override public CharSequence getContent() { StringBuffer code = new StringBuffer(); code.append("package test;\n"); code.append("public abstract class GenericClass<T> {\n"); code.append(" abstract void set(T x);\n"); code.append("}\n"); return code; } }); sourceOracle.addOrReplace(new MockJavaResource("test.ClassWithBridge") { @Override public CharSequence getContent() { StringBuffer code = new StringBuffer(); code.append("package test;\n"); code.append("class ClassWithBridge extends GenericClass<String> {\n"); code.append(" void set(String x) { }\n"); code.append("}\n"); return code; } }); sourceOracle.addOrReplace(new MockJavaResource("test.PrivateSup") { @Override public CharSequence getContent() { StringBuffer code = new StringBuffer(); code.append("package test;\n"); code.append("public class PrivateSup {\n"); code.append(" private static int field;\n"); code.append(" private static int method() { return 0; }\n"); code.append(" private static int fieldSup;\n"); code.append(" private static int methodSuP() { return 0; }\n"); code.append("}\n"); return code; } }); sourceOracle.addOrReplace(new MockJavaResource("test.PrivateSub") { @Override public CharSequence getContent() { StringBuffer code = new StringBuffer(); code.append("package test;\n"); code.append("public class PrivateSub extends PrivateSup {\n"); code.append(" private static float field;\n"); code.append(" private static float method() { return 0; }\n"); code.append(" private static float methodSub() { return 0; }\n"); code.append("}\n"); return code; } }); sourceOracle.addOrReplace(new MockJavaResource("test.DiffRetSuper") { @Override public CharSequence getContent() { StringBuffer code = new StringBuffer(); code.append("package test;\n"); code.append("public interface DiffRetSuper {\n"); code.append(" Object foo();\n"); code.append("}\n"); return code; } }); sourceOracle.addOrReplace(new MockJavaResource("test.DiffRetSub") { @Override public CharSequence getContent() { StringBuffer code = new StringBuffer(); code.append("package test;\n"); code.append("public interface DiffRetSub extends DiffRetSuper {\n"); code.append(" String foo();\n"); code.append("}\n"); return code; } }); addSnippetImport("test.DiffRetSub"); try { // The snippet must reference the classes so they will be compiled in program = compileSnippet("void", "new test.Foo(); new test.Bar(); new ClassWithBridge(); new PrivateSub();"); } catch (UnableToCompleteException e) { throw new RuntimeException(e); } } public void testBasicLookups() { { MockErrorReporter errors = new MockErrorReporter(); JField res = (JField) lookup("test.Foo::intStatic", errors); errors.assertNoError(); assertEquals("test.Foo", res.getEnclosingType().getName()); assertEquals("intStatic", res.getName()); assertTrue(res.isStatic()); } { MockErrorReporter errors = new MockErrorReporter(); JField res = (JField) lookup("test.Foo::intInstance", errors); errors.assertNoError(); assertEquals("test.Foo", res.getEnclosingType().getName()); assertEquals("intInstance", res.getName()); assertFalse(res.isStatic()); } { MockErrorReporter errors = new MockErrorReporter(); JMethod res = (JMethod) lookup("test.Foo::addOne(I)", errors); errors.assertNoError(); assertEquals("test.Foo", res.getEnclosingType().getName()); assertEquals("addOne", res.getName()); } { MockErrorReporter errors = new MockErrorReporter(); JMethod res = (JMethod) lookup("test.Foo::addTwoOverloaded(I)", errors); errors.assertNoError(); assertEquals("test.Foo", res.getEnclosingType().getName()); assertEquals("addTwoOverloaded", res.getName()); assertEquals(JPrimitiveType.INT, res.getParams().get(0).getType()); } { MockErrorReporter errors = new MockErrorReporter(); JMethod res = (JMethod) lookup("test.Foo::addTwoOverloaded(D)", errors); errors.assertNoError(); assertEquals("test.Foo", res.getEnclosingType().getName()); assertEquals("addTwoOverloaded", res.getName()); assertEquals(JPrimitiveType.DOUBLE, res.getParams().get(0).getType()); } { MockErrorReporter errors = new MockErrorReporter(); JMethod res = (JMethod) lookup("test.Foo::new()", errors); errors.assertNoError(); assertEquals("test.Foo", res.getEnclosingType().getName()); assertEquals("Foo", res.getName()); } { MockErrorReporter errors = new MockErrorReporter(); lookup("test.Foo::bogoField", errors); errors.assertHasError(); } { MockErrorReporter errors = new MockErrorReporter(); lookup("test.Foo::bogoMethod()", errors); errors.assertHasError(); } { MockErrorReporter errors = new MockErrorReporter(); lookup("test.Foo::new(J)", errors); errors.assertHasError(); } } public void testBridgeMethods() { { MockErrorReporter errors = new MockErrorReporter(); JMethod res = (JMethod) lookup( "test.ClassWithBridge::set(Ljava/lang/String;)", errors); errors.assertNoError(); assertEquals("test.ClassWithBridge", res.getEnclosingType().getName()); assertEquals("set", res.getName()); assertEquals("java.lang.String", res.getParams().get(0).getType().getName()); } { MockErrorReporter errors = new MockErrorReporter(); JMethod res = (JMethod) lookup("test.ClassWithBridge::set(*)", errors); errors.assertNoError(); assertEquals("test.ClassWithBridge", res.getEnclosingType().getName()); assertEquals("set", res.getName()); assertEquals("java.lang.String", res.getParams().get(0).getType().getName()); } { // For backward compatibility, allow calling a bridge method directly MockErrorReporter errors = new MockErrorReporter(); JMethod res = (JMethod) lookup( "test.ClassWithBridge::set(Ljava/lang/Object;)", errors); errors.assertNoError(); assertEquals("test.ClassWithBridge", res.getEnclosingType().getName()); assertEquals("set", res.getName()); assertEquals("java.lang.Object", res.getParams().get(0).getType().getName()); } } public void testConstructors() { { MockErrorReporter errors = new MockErrorReporter(); JMethod res = (JMethod) lookup("test.Foo::new()", errors); errors.assertNoError(); assertEquals("test.Foo", res.getEnclosingType().getName()); assertEquals("Foo", res.getName()); assertEquals(0, res.getParams().size()); } { MockErrorReporter errors = new MockErrorReporter(); JMethod res = (JMethod) lookup("test.Foo::new(I)", errors); errors.assertNoError(); assertEquals("test.Foo", res.getEnclosingType().getName()); assertEquals("Foo", res.getName()); assertEquals(1, res.getParams().size()); assertSame(JPrimitiveType.INT, res.getParams().get(0).getType()); } { MockErrorReporter errors = new MockErrorReporter(); lookup("test.Foo::new(*)", errors); errors.assertHasError(); } { MockErrorReporter errors = new MockErrorReporter(); JMethod res = (JMethod) lookup("test.Bar::new()", errors); errors.assertNoError(); assertEquals("test.Bar", res.getEnclosingType().getName()); assertEquals("Bar", res.getName()); assertEquals(0, res.getParams().size()); } { MockErrorReporter errors = new MockErrorReporter(); JMethod res = (JMethod) lookup("test.Bar::new(*)", errors); errors.assertNoError(); assertEquals("test.Bar", res.getEnclosingType().getName()); assertEquals("Bar", res.getName()); assertEquals(0, res.getParams().size()); } { MockErrorReporter errors = new MockErrorReporter(); lookup("test.Bar::new(I)", errors); errors.assertHasError(); } } public void testInheritance() { // test lookups of methods where the subtype is specified { MockErrorReporter errors = new MockErrorReporter(); JMethod res = (JMethod) lookup("test.Bar::addOne(I)", errors); errors.assertNoError(); assertEquals("test.Foo", res.getEnclosingType().getName()); assertEquals("addOne", res.getName()); } { MockErrorReporter errors = new MockErrorReporter(); JMethod res = (JMethod) lookup("test.Bar::addTwoOverloaded(I)", errors); errors.assertNoError(); assertEquals("test.Foo", res.getEnclosingType().getName()); assertEquals("addTwoOverloaded", res.getName()); assertEquals(JPrimitiveType.INT, res.getParams().get(0).getType()); } { MockErrorReporter errors = new MockErrorReporter(); JMethod res = (JMethod) lookup("test.Bar::addTwoOverloaded(D)", errors); errors.assertNoError(); assertEquals("test.Foo", res.getEnclosingType().getName()); assertEquals("addTwoOverloaded", res.getName()); assertEquals(JPrimitiveType.DOUBLE, res.getParams().get(0).getType()); } { MockErrorReporter errors = new MockErrorReporter(); JMethod res = (JMethod) lookup("test.Bar::addOne(*)", errors); errors.assertNoError(); assertEquals("test.Foo", res.getEnclosingType().getName()); assertEquals("addOne", res.getName()); } { MockErrorReporter errors = new MockErrorReporter(); lookup("test.Bar::addTwoOverloaded(*)", errors); errors.assertHasError(); } /* * test wildcard lookups when the subtype overloads but the supertype does * not, and vice versa */ { MockErrorReporter errors = new MockErrorReporter(); JMethod res = (JMethod) lookup("test.Bar::foo(I)", errors); errors.assertNoError(); assertEquals("test.Bar", res.getEnclosingType().getName()); assertEquals("foo", res.getName()); } { MockErrorReporter errors = new MockErrorReporter(); JMethod res = (JMethod) lookup("test.Bar::bar(I)", errors); errors.assertNoError(); assertEquals("test.Bar", res.getEnclosingType().getName()); assertEquals("bar", res.getName()); } { MockErrorReporter errors = new MockErrorReporter(); JMethod res = (JMethod) lookup("test.Bar::bar(D)", errors); errors.assertNoError(); assertEquals("test.Bar", res.getEnclosingType().getName()); assertEquals("bar", res.getName()); } { MockErrorReporter errors = new MockErrorReporter(); lookup("test.Bar::foo(*)", errors); errors.assertHasError(); } /* * Test a lookup where the subtype has a narrower return type than the * supertype. */ { MockErrorReporter errors = new MockErrorReporter(); JMethod res = (JMethod) lookup("test.DiffRetSub::foo()", errors); errors.assertNoError(); assertEquals("test.DiffRetSub", res.getEnclosingType().getName()); assertEquals("foo", res.getName()); } } public void testInterfaces() { // Test lookups in the interface that specify the types { MockErrorReporter errors = new MockErrorReporter(); JMethod res = (JMethod) lookup("test.Intf::addTwoOverloaded(I)", errors); errors.assertNoError(); assertEquals("test.Intf", res.getEnclosingType().getName()); assertEquals("addTwoOverloaded", res.getName()); } { MockErrorReporter errors = new MockErrorReporter(); JMethod res = (JMethod) lookup("test.Intf::addOne(I)", errors); errors.assertNoError(); assertEquals("test.Intf", res.getEnclosingType().getName()); assertEquals("addOne", res.getName()); } { MockErrorReporter errors = new MockErrorReporter(); JMethod res = (JMethod) lookup("test.Intf::foo(I)", errors); errors.assertNoError(); assertEquals("test.Intf", res.getEnclosingType().getName()); assertEquals("foo", res.getName()); } { MockErrorReporter errors = new MockErrorReporter(); JMethod res = (JMethod) lookup("test.Intf::foo(D)", errors); errors.assertNoError(); assertEquals("test.Intf", res.getEnclosingType().getName()); assertEquals("foo", res.getName()); } // Test lookups that use wildcards { MockErrorReporter errors = new MockErrorReporter(); JMethod res = (JMethod) lookup("test.Intf::addTwoOverloaded(*)", errors); errors.assertNoError(); assertEquals("test.Intf", res.getEnclosingType().getName()); assertEquals("addTwoOverloaded", res.getName()); } { MockErrorReporter errors = new MockErrorReporter(); JMethod res = (JMethod) lookup("test.Intf::addOne(*)", errors); errors.assertNoError(); assertEquals("test.Intf", res.getEnclosingType().getName()); assertEquals("addOne", res.getName()); } { MockErrorReporter errors = new MockErrorReporter(); lookup("test.Intf::foo(*)", errors); errors.assertHasError(); } } public void testPrivate() { // test private entries in the requested class { MockErrorReporter errors = new MockErrorReporter(); JMethod res = (JMethod) lookup("test.PrivateSub::method()", errors); errors.assertNoError(); assertEquals("test.PrivateSub", res.getEnclosingType().getName()); assertEquals("method", res.getName()); } { MockErrorReporter errors = new MockErrorReporter(); JField res = (JField) lookup("test.PrivateSub::field", errors); errors.assertNoError(); assertEquals("test.PrivateSub", res.getEnclosingType().getName()); assertEquals("field", res.getName()); } // test private entries in the superclass { MockErrorReporter errors = new MockErrorReporter(); lookup("test.PrivateSub::methodSup()", errors); errors.assertHasError(); } { MockErrorReporter errors = new MockErrorReporter(); lookup("test.PrivateSub::fieldSup", errors); errors.assertHasError(); } } public void testWildcardLookups() { { MockErrorReporter errors = new MockErrorReporter(); JMethod res = (JMethod) lookup("test.Foo::addOne(*)", errors); errors.assertNoError(); assertEquals("test.Foo", res.getEnclosingType().getName()); assertEquals("addOne", res.getName()); } { MockErrorReporter errors = new MockErrorReporter(); lookup("test.Foo::addTwoOverloaded(*)", errors); errors.assertHasError(); } { MockErrorReporter errors = new MockErrorReporter(); lookup("test.Foo::bogoMethod(*)", errors); errors.assertHasError(); } } private JNode lookup(String refString, MockErrorReporter errors) { return JsniRefLookup.findJsniRefTarget(JsniRef.parse(refString), program, errors); } }