001: /*
002: * Copyright 2007 Google Inc.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License. You may obtain a copy of
006: * the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013: * License for the specific language governing permissions and limitations under
014: * the License.
015: */
016: package com.google.gwt.dev.jdt.test;
017:
018: import com.google.gwt.core.ext.TreeLogger;
019: import com.google.gwt.core.ext.UnableToCompleteException;
020: import com.google.gwt.core.ext.typeinfo.CompilationUnitProvider;
021: import com.google.gwt.dev.jdt.ByteCodeCompiler;
022: import com.google.gwt.dev.jdt.RebindOracle;
023: import com.google.gwt.dev.jdt.SourceOracle;
024: import com.google.gwt.dev.jdt.URLCompilationUnitProvider;
025: import com.google.gwt.dev.util.FileOracle;
026: import com.google.gwt.dev.util.FileOracleFactory;
027: import com.google.gwt.dev.util.FileOracleFactory.FileFilter;
028: import com.google.gwt.dev.util.log.Loggers;
029:
030: import junit.framework.TestCase;
031:
032: import java.net.URL;
033: import java.util.HashMap;
034: import java.util.HashSet;
035: import java.util.Map;
036: import java.util.Set;
037:
038: public class ByteCodeCompilerTest extends TestCase {
039:
040: private static class TestByteCodeCompilerHost implements
041: SourceOracle, RebindOracle {
042:
043: private abstract class TestCup implements
044: CompilationUnitProvider {
045:
046: public TestCup(String packageName, String onlyTypeName) {
047: this (packageName, new String[] { onlyTypeName });
048: }
049:
050: public TestCup(String packageName, String[] typeNames) {
051: this .packageName = packageName;
052: registerPackage(packageName);
053: for (int i = 0; i < typeNames.length; i++) {
054: if (packageName.length() > 0) {
055: registerType(packageName + "." + typeNames[i],
056: this );
057: } else {
058: // In the default package.
059: //
060: registerType(typeNames[i], this );
061: }
062: }
063: firstTypeName = typeNames[0];
064: }
065:
066: public long getLastModified()
067: throws UnableToCompleteException {
068: return 0;
069: }
070:
071: public String getLocation() {
072: return "transient source for " + packageName + "."
073: + firstTypeName;
074: }
075:
076: public String getMainTypeName() {
077: return null;
078: }
079:
080: public String getPackageName() {
081: return packageName;
082: }
083:
084: public abstract char[] getSource();
085:
086: public boolean isTransient() {
087: return true;
088: }
089:
090: private final String firstTypeName;
091: private final String packageName;
092: }
093:
094: public TestByteCodeCompilerHost() {
095: registerPackage(""); // the default package
096: }
097:
098: public final CompilationUnitProvider findCompilationUnit(
099: TreeLogger logger, String typeName) {
100: return cupsByTypeName.get(typeName);
101: }
102:
103: public final boolean isPackage(String possiblePackageName) {
104: return pkgNames.contains(possiblePackageName);
105: }
106:
107: // Override for specific test cases.
108: //
109: public String rebind(TreeLogger logger, String typeName)
110: throws UnableToCompleteException {
111: return typeName;
112: }
113:
114: public final void registerPackage(String packageName) {
115: String[] packageParts = packageName.split("\\.");
116: String toRegister = null;
117: for (int i = 0; i < packageParts.length; i++) {
118: String part = packageParts[i];
119: if (toRegister != null) {
120: toRegister += "." + part;
121: } else {
122: toRegister = part;
123: }
124: pkgNames.add(toRegister);
125: }
126: }
127:
128: public final void registerType(String typeName, TestCup cup) {
129: cupsByTypeName.put(typeName, cup);
130: }
131:
132: {
133: pkgNames = new HashSet<String>();
134: cupsByTypeName = new HashMap<String, CompilationUnitProvider>();
135: }
136:
137: final CompilationUnitProvider CU_AB = new TestCup("test",
138: new String[] { "A", "A.B" }) {
139: public char[] getSource() {
140: StringBuffer sb = new StringBuffer();
141: sb.append("package test;\n");
142: sb.append("public class A {\n");
143: sb.append(" public static class B extends A { }\n");
144: sb.append("}\n");
145: return sb.toString().toCharArray();
146: }
147: };
148:
149: final CompilationUnitProvider CU_C = new TestCup("test",
150: new String[] { "C", "C.Message" }) {
151: public char[] getSource() {
152: StringBuffer sb = new StringBuffer();
153: sb.append("package test;\n");
154: sb.append("import com.google.gwt.core.client.GWT;\n");
155: sb.append("public class C {\n");
156: sb.append(" public static String getMessage() {\n");
157: sb
158: .append(" return ((Message)GWT.create(Message.class)).f();\n");
159: sb.append(" }\n");
160: sb.append(" public static class Message {\n");
161: sb.append(" public String f() {\n");
162: sb.append(" return \"C.Message\";\n");
163: sb.append(" }\n");
164: sb.append(" }\n");
165: sb.append("}\n");
166: return sb.toString().toCharArray();
167: }
168: };
169:
170: final CompilationUnitProvider CU_CLASS = new TestCup(
171: "java.lang", "Class") {
172:
173: public char[] getSource() {
174: StringBuffer sb = new StringBuffer();
175: sb.append("package java.lang;\n");
176: sb.append("public class Class { }\n");
177: return sb.toString().toCharArray();
178: }
179: };
180:
181: /**
182: * This one is different because D is not public and it lives in the default
183: * package.
184: */
185: final CompilationUnitProvider CU_DE = new TestCup("",
186: new String[] { "D", "D.E" }) {
187: public char[] getSource() {
188: StringBuffer sb = new StringBuffer();
189: sb.append("class D extends test.C.Message {\n");
190: sb.append(" public static class E extends D {\n");
191: sb.append(" public String getMessage() {\n");
192: sb.append(" return \"D.E.Message\";\n");
193: sb.append(" }\n");
194: sb.append(" }\n");
195: sb.append("}\n");
196: return sb.toString().toCharArray();
197: }
198: };
199:
200: final CompilationUnitProvider CU_GWT = new TestCup(
201: "com.google.gwt.core.client", "GWT") {
202:
203: public char[] getSource() {
204: StringBuffer sb = new StringBuffer();
205: sb.append("package com.google.gwt.core.client;\n");
206: sb.append("public final class GWT {\n");
207: sb
208: .append(" public static Object create(Class classLiteral) { return null; }\n");
209: sb.append("}\n");
210: return sb.toString().toCharArray();
211: }
212: };
213:
214: final CompilationUnitProvider CU_MAIN = new TestCup("test",
215: "Main") {
216:
217: public char[] getSource() {
218: StringBuffer sb = new StringBuffer();
219: sb.append("package test;\n");
220: sb.append("import com.google.gwt.core.client.GWT;\n");
221: sb.append("public class Main {\n");
222: sb
223: .append(" public static void main(String[] args) {\n");
224: sb.append(" A a = (A)GWT.create(A.class);\n");
225: sb.append(" }\n");
226: sb.append("}\n");
227: return sb.toString().toCharArray();
228: }
229: };
230:
231: final TestCup CU_OBJECT = new TestCup("java.lang", "Object") {
232: public char[] getSource() {
233: StringBuffer sb = new StringBuffer();
234: sb.append("package java.lang;\n");
235: sb.append("public class Object { }\n");
236: return sb.toString().toCharArray();
237: }
238: };
239:
240: final CompilationUnitProvider CU_STRING = new TestCup(
241: "java.lang", "String") {
242:
243: public char[] getSource() {
244: StringBuffer sb = new StringBuffer();
245: sb.append("package java.lang;\n");
246: sb.append("public class String { }\n");
247: return sb.toString().toCharArray();
248: }
249: };
250:
251: private final Map<String, CompilationUnitProvider> cupsByTypeName;
252: private final Set<String> pkgNames;
253: }
254:
255: private static void scanAndCompile(TreeLogger logger)
256: throws UnableToCompleteException {
257: FileOracleFactory fof = new FileOracleFactory();
258: fof.addPackage("", new FileFilter() {
259: public boolean accept(String string) {
260: return string.endsWith(".java");
261: }
262: });
263: final FileOracle fo = fof.create(logger);
264:
265: final SourceOracle host = new SourceOracle() {
266:
267: public CompilationUnitProvider findCompilationUnit(
268: TreeLogger logger, String typeName) {
269: // ONLY LOOK FOR TOP-LEVEL TYPES.
270: //
271: CompilationUnitProvider cup = cups.get(typeName);
272: if (cup == null) {
273: String path = typeName.replace('.', '/') + ".java";
274: URL url = fo.find(path);
275: if (url != null) {
276: String pkgName = "";
277: int len = findLengthOfPackagePart(typeName);
278: if (len > 0) {
279: pkgName = typeName.substring(0, len);
280: }
281: return new URLCompilationUnitProvider(url,
282: pkgName);
283: } else {
284: return null;
285: }
286: }
287: return cup;
288: }
289:
290: public boolean isPackage(String possiblePackageName) {
291: String path = possiblePackageName.replace('.', '/')
292: + "/";
293: URL url = fo.find(path);
294: if (url != null) {
295: return true;
296: } else {
297: return false;
298: }
299: }
300:
301: private int findLengthOfPackagePart(String typeName) {
302: int maxDotIndex = 0;
303: int i = typeName.indexOf('.');
304: while (i != -1) {
305: if (!isPackage(typeName.substring(0, i))) {
306: break;
307: } else {
308: maxDotIndex = i;
309: i = typeName.indexOf('.', i + 1);
310: }
311: }
312: return maxDotIndex;
313: }
314:
315: private Map<String, CompilationUnitProvider> cups = new HashMap<String, CompilationUnitProvider>();
316: };
317:
318: ByteCodeCompiler cs = new ByteCodeCompiler(host);
319: String[] allJava = fo.getAllFiles();
320:
321: for (int i = 0; i < 3; ++i) {
322: long before = System.currentTimeMillis();
323:
324: for (int j = 0; j < allJava.length; j++) {
325: String typeName = allJava[j].substring(0,
326: allJava[j].length() - 5).replace('/', '.');
327: cs.getClassBytes(logger, typeName);
328: }
329:
330: long after = System.currentTimeMillis();
331: System.out.println("Iter " + i + " took "
332: + (after - before) + " ms");
333: }
334: }
335:
336: // This one is standalone.
337: //
338: public void testJavaLangObject() throws Exception {
339: ByteCodeCompiler cs = new ByteCodeCompiler(testHost);
340: assertNotNull(cs.getClassBytes(logger, "java.lang.Object"));
341: }
342:
343: // This one requires java.lang.Object.
344: //
345: public void testJavaLangString() throws Exception {
346: ByteCodeCompiler cs = new ByteCodeCompiler(testHost);
347: assertNotNull(cs.getClassBytes(logger, "java.lang.Object"));
348: assertNotNull(cs.getClassBytes(logger, "java.lang.String"));
349: }
350:
351: // Try deferred binding that takes three compile iterations.
352: // - In Main, we rebind from A to C
353: // - In C, we rebind from C to D.E
354: //
355: public void testRebindCreateTransitive() throws Exception {
356: ByteCodeCompiler cs = new ByteCodeCompiler(
357: new TestByteCodeCompilerHost() {
358: public String rebind(TreeLogger logger,
359: String typeName)
360: throws com.google.gwt.core.ext.UnableToCompleteException {
361: if ("test.C.Message".equals(typeName)) {
362: return "D.E";
363: } else {
364: return typeName;
365: }
366: }
367: });
368:
369: assertNotNull(cs.getClassBytes(logger, "test.C"));
370: assertNotNull(cs.getClassBytes(logger, "test.C$Message"));
371: assertNotNull(cs.getClassBytes(logger, "D"));
372: assertNotNull(cs.getClassBytes(logger, "D$E"));
373:
374: assertNotNull(cs.getClassBytes(logger, "java.lang.Object"));
375: assertNotNull(cs.getClassBytes(logger, "java.lang.String"));
376: assertNotNull(cs.getClassBytes(logger, "java.lang.Class"));
377: assertNotNull(cs.getClassBytes(logger,
378: "com.google.gwt.core.client.GWT"));
379: }
380:
381: // Try deferred binding that works.
382: //
383: public void testRebindCreateWithSuccess() throws Exception {
384: ByteCodeCompiler cs = new ByteCodeCompiler(
385: new TestByteCodeCompilerHost() {
386: public String rebind(TreeLogger logger,
387: String typeName)
388: throws com.google.gwt.core.ext.UnableToCompleteException {
389: if ("test.A".equals(typeName)) {
390: return "test.A.B";
391: } else {
392: return typeName;
393: }
394: }
395: });
396:
397: assertNotNull(cs.getClassBytes(logger, "java.lang.Object"));
398: assertNotNull(cs.getClassBytes(logger, "java.lang.String"));
399: assertNotNull(cs.getClassBytes(logger, "java.lang.Class"));
400: assertNotNull(cs.getClassBytes(logger,
401: "com.google.gwt.core.client.GWT"));
402:
403: assertNotNull(cs.getClassBytes(logger, "test.Main"));
404: assertNotNull(cs.getClassBytes(logger, "test.A"));
405: assertNotNull(cs.getClassBytes(logger, "test.A$B"));
406:
407: // Check again for the same class to make sure it's cached.
408: // (Although you have to run this test with "-Dgwt.useGuiLogger" defined
409: // to see what it does.)
410: //
411: assertNotNull(cs.getClassBytes(logger, "java.lang.Object"));
412: }
413:
414: protected void setUp() throws Exception {
415: logger = Loggers.createOptionalGuiTreeLogger();
416: testHost = new TestByteCodeCompilerHost();
417: }
418:
419: private TreeLogger logger = TreeLogger.NULL;
420: private TestByteCodeCompilerHost testHost = null;
421: }
|