| /* |
| * 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); |
| } |
| |
| @Override |
| 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() { |
| StringBuilder code = new StringBuilder(); |
| 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() { |
| StringBuilder code = new StringBuilder(); |
| 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() { |
| StringBuilder code = new StringBuilder(); |
| 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() { |
| StringBuilder code = new StringBuilder(); |
| 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() { |
| StringBuilder code = new StringBuilder(); |
| 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() { |
| StringBuilder code = new StringBuilder(); |
| 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() { |
| StringBuilder code = new StringBuilder(); |
| 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() { |
| StringBuilder code = new StringBuilder(); |
| 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() { |
| StringBuilder code = new StringBuilder(); |
| 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();", true); |
| } 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); |
| } |
| } |