/* * Copyright 2010 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.GeneratedUnit; import com.google.gwt.dev.javac.JdtCompiler.AdditionalTypeProviderDelegate; import com.google.gwt.dev.javac.testing.impl.MockJavaResource; import com.google.gwt.dev.jjs.ast.JDeclaredType; import com.google.gwt.dev.jjs.ast.JMethod; import com.google.gwt.dev.jjs.ast.JProgram; /** * Tests that a AdditionalTypeProviderDelegate correctly gets control when an unknown * class is found, and that source for an unknown class gets correctly parsed. */ public class AdditionalTypeProviderDelegateTest extends OptimizerTestBase { /** * Compilation unit for a class generated at runtime when an unknown * reference appears. */ private class JavaWrapperCompilationUnit implements GeneratedUnit { private final long createTime = System.currentTimeMillis(); public String optionalFileLocation() { // The named file location requires a non-Java extension, // or else the file won't get compiled correctly. return "myPackage/InsertedClass.notjava"; } public String getStrongHash() { return "InsertedClass"; } public long creationTime() { return createTime; } public String getSource() { String classSource = "package myPackage;\n" + "public class InsertedClass {\n" + " public static int getSmallNumber() {\n" + " return 5;\n" + " }\n" + "}"; return classSource; } public String getTypeName() { return "myPackage.InsertedClass"; } public long getSourceToken() { return -1; } } public boolean insertInsertedClass = false; public void setUp() { // Create a source class that passes fine (just to test infrastructure.) sourceOracle.addOrReplace(new MockJavaResource("test.A") { @Override public CharSequence getContent() { StringBuffer code = new StringBuffer(); code.append("package test;\n"); code.append("class A {\n"); code.append(" void myFunc() {\n"); code.append(" }\n"); code.append("}\n"); return code; } }); // Create a source file containing a reference to a class in another // package that we don't yet know about. That code will be inserted // by the AdditionalTypeProviderDelegate. sourceOracle.addOrReplace(new MockJavaResource("test.B") { @Override public CharSequence getContent() { StringBuffer code = new StringBuffer(); code.append("package test;\n"); code.append("import myPackage.InsertedClass;"); code.append("class B {\n"); code.append(" int func() {\n"); // Reference an unknown class that will be substituted on the fly. code.append(" return myPackage.InsertedClass.getSmallNumber();\n"); code.append(" }\n"); code.append("}\n"); return code; } }); // Create a source file containing a reference to a class in another // package, but that lacks an import directive. Are we creating the // class anyway? sourceOracle.addOrReplace(new MockJavaResource("test.B1") { @Override public CharSequence getContent() { StringBuffer code = new StringBuffer(); code.append("package test;\n"); code.append("class B1 {\n"); code.append(" int func() {\n"); // Reference an unknown class that will be substituted on the fly. code.append(" return myPackage.InsertedClass.getSmallNumber();\n"); code.append(" }\n"); code.append("}\n"); return code; } }); } public void testInsertedClass() throws UnableToCompleteException { JProgram program = compileSnippet("void", "new test.B().func();"); // Make sure the compiled classes appeared. JDeclaredType bType = findType(program, "test.B"); assertNotNull("Unknown type B", bType); JDeclaredType insertedClassType = findType(program, "myPackage.InsertedClass"); assertNotNull("Unknown type InsertedClass", insertedClassType); } public void testInsertedClass2() throws UnableToCompleteException { JProgram program = compileSnippet("void", "new test.B1().func();"); // Make sure the compiled classes appeared. JDeclaredType bType = findType(program, "test.B1"); assertNotNull("Unknown type B1", bType); JDeclaredType insertedClassType = findType(program, "myPackage.InsertedClass"); assertNotNull("Unknown type InsertedClass", insertedClassType); } // Make sure regular code not using the AdditionalTypeProviderDelegate still works. public void testSimpleParse() throws UnableToCompleteException { JProgram program = compileSnippet("void", "new test.A();"); JDeclaredType goodClassType = findType(program, "test.A"); assertNotNull("Unknown class A", goodClassType); } public void testUnknownClass() { // Create a source file containing a reference to an unknown class. sourceOracle.addOrReplace(new MockJavaResource("test.C") { @Override public CharSequence getContent() { StringBuffer code = new StringBuffer(); code.append("package test;\n"); code.append("import myPackage.UnknownClass;"); code.append("class C {\n"); code.append(" int func() {\n"); // Reference an unknown class. code.append(" return myPackage.UnknownClass.getSmallNumber();\n"); code.append(" }\n"); code.append("}\n"); return code; } }); try { compileSnippet("void", "new test.C();"); fail("Shouldn't have compiled"); } catch (UnableToCompleteException expected) { } } public void testUnknownClassNoImport() { // Create a source file with a reference to an unknown class and no // import statement. sourceOracle.addOrReplace(new MockJavaResource("test.D") { @Override public CharSequence getContent() { StringBuffer code = new StringBuffer(); code.append("package test;\n"); code.append("class D {\n"); code.append(" int func() {\n"); // Reference an unknown class. code.append(" return myPackage.UnknownClass.getSmallNumber();\n"); code.append(" }\n"); code.append("}\n"); return code; } }); try { compileSnippet("void", "new test.D();"); fail("Shouldn't have compiled"); } catch (UnableToCompleteException expected) { } } @Override protected AdditionalTypeProviderDelegate getAdditionalTypeProviderDelegate() { // We'll provide a simple compiler delegate that will provide source // for a class called myPackage.InsertedClass. return new AdditionalTypeProviderDelegate() { public boolean doFindAdditionalPackage(String slashedPackageName) { if (slashedPackageName.compareTo("myPackage") == 0) { return true; } return false; } public GeneratedUnit doFindAdditionalType(String binaryName) { if (binaryName.compareTo("myPackage/InsertedClass") == 0) { return new JavaWrapperCompilationUnit(); } return null; } }; } protected boolean optimizeMethod(JProgram program, JMethod method) { return false; } }