JavaAstConstructor uses UnifyAst. http://gwt-code-reviews.appspot.com/1453810/ git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@10312 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/jjs/AstConstructor.java b/dev/core/src/com/google/gwt/dev/jjs/AstConstructor.java new file mode 100644 index 0000000..4b4c7f7 --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/jjs/AstConstructor.java
@@ -0,0 +1,100 @@ +/* + * Copyright 2011 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; + +import com.google.gwt.core.ext.TreeLogger; +import com.google.gwt.core.ext.UnableToCompleteException; +import com.google.gwt.dev.javac.CompilationState; +import com.google.gwt.dev.javac.StandardGeneratorContext; +import com.google.gwt.dev.jdt.RebindPermutationOracle; +import com.google.gwt.dev.jjs.CorrelationFactory.DummyCorrelationFactory; +import com.google.gwt.dev.jjs.ast.JProgram; +import com.google.gwt.dev.jjs.impl.AssertionNormalizer; +import com.google.gwt.dev.jjs.impl.AssertionRemover; +import com.google.gwt.dev.jjs.impl.FixAssignmentToUnbox; +import com.google.gwt.dev.jjs.impl.GwtAstBuilder; +import com.google.gwt.dev.jjs.impl.ImplementClassLiteralsAsFields; +import com.google.gwt.dev.jjs.impl.ReplaceRunAsyncs; +import com.google.gwt.dev.jjs.impl.UnifyAst; +import com.google.gwt.dev.js.ast.JsProgram; + +/** + * Constructs a full Java AST from source. + */ +public class AstConstructor { + + /** + * Construct an simple AST representing an entire {@link CompilationState}. + * Does not support deferred binding. Implementation mostly copied from + * {@link JavaToJavaScriptCompiler}. + */ + public static JProgram construct(TreeLogger logger, final CompilationState state, + JJSOptions options) throws UnableToCompleteException { + + InternalCompilerException.preload(); + + RebindPermutationOracle rpo = new RebindPermutationOracle() { + @Override + public void clear() { + } + + @Override + public String[] getAllPossibleRebindAnswers(TreeLogger logger, String sourceTypeName) + throws UnableToCompleteException { + return new String[0]; + } + + public CompilationState getCompilationState() { + return state; + } + + public StandardGeneratorContext getGeneratorContext() { + return null; + } + }; + + JProgram jprogram = new JProgram(DummyCorrelationFactory.INSTANCE); + JsProgram jsProgram = new JsProgram(DummyCorrelationFactory.INSTANCE); + assert GwtAstBuilder.ENABLED; + UnifyAst unifyAst = new UnifyAst(jprogram, jsProgram, options, rpo); + unifyAst.buildEverything(logger); + + // Compute all super type/sub type info + jprogram.typeOracle.computeBeforeAST(); + + // (3) Perform Java AST normalizations. + FixAssignmentToUnbox.exec(jprogram); + + /* + * TODO: If we defer this until later, we could maybe use the results of the + * assertions to enable more optimizations. + */ + if (options.isEnableAssertions()) { + // Turn into assertion checking calls. + AssertionNormalizer.exec(jprogram); + } else { + // Remove all assert statements. + AssertionRemover.exec(jprogram); + } + + if (options.isRunAsyncEnabled()) { + ReplaceRunAsyncs.exec(logger, jprogram); + } + + ImplementClassLiteralsAsFields.exec(jprogram); + return jprogram; + } +}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java index e7b17f1..7d0957b 100644 --- a/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java +++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java
@@ -2902,6 +2902,13 @@ return result; } + SourceInfo makeSourceInfo(AbstractMethodDeclaration x) { + int startLine = + Util.getLineNumber(x.sourceStart, curCud.separatorPositions, 0, + curCud.separatorPositions.length - 1); + return SourceOrigin.create(x.sourceStart, x.bodyEnd, startLine, curCud.fileName); + } + SourceInfo makeSourceInfo(ASTNode x) { int startLine = Util.getLineNumber(x.sourceStart, curCud.separatorPositions, 0,
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRunAsyncs.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRunAsyncs.java index af0afd8..7d50f49 100644 --- a/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRunAsyncs.java +++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRunAsyncs.java
@@ -37,6 +37,7 @@ import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger.Event; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -172,9 +173,14 @@ } if (matches.size() > 1) { TreeLogger branch = error(info, "Multiple runAsync calls are named " + name); + List<String> errors = new ArrayList<String>(); for (JRunAsync match : matches) { - branch.log(TreeLogger.ERROR, "One call is at '" + match.getSourceInfo().getFileName() - + ':' + match.getSourceInfo().getStartLine() + "'"); + errors.add("One call is at '" + match.getSourceInfo().getFileName() + ':' + + match.getSourceInfo().getStartLine() + "'"); + } + Collections.sort(errors); + for (String error : errors) { + branch.log(TreeLogger.ERROR, error); } return; }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/UnifyAst.java b/dev/core/src/com/google/gwt/dev/jjs/impl/UnifyAst.java index 0ebe6e8..b29358a 100644 --- a/dev/core/src/com/google/gwt/dev/jjs/impl/UnifyAst.java +++ b/dev/core/src/com/google/gwt/dev/jjs/impl/UnifyAst.java
@@ -71,6 +71,7 @@ import com.google.gwt.dev.js.ast.JsRootScope; import com.google.gwt.dev.util.JsniRef; import com.google.gwt.dev.util.Name.BinaryName; +import com.google.gwt.dev.util.Name.InternalName; import com.google.gwt.dev.util.collect.IdentityHashSet; import com.google.gwt.dev.util.collect.Lists; @@ -461,6 +462,7 @@ CLASS_IS_CLASS_METADATA_ENABLED)); private final Map<String, CompiledClass> classFileMap; + private boolean errorsFound = false; private final Map<String, JField> fieldMap = new HashMap<String, JField>(); /** @@ -521,7 +523,41 @@ } } - public void exec(TreeLogger logger) { + /** + * Special AST construction, useful for tests. Everything is resolved, + * translated, and unified. + */ + public void buildEverything(TreeLogger logger) throws UnableToCompleteException { + this.logger = logger; + for (String internalName : classFileMap.keySet()) { + String typeName = InternalName.toBinaryName(internalName); + searchForType(typeName); + } + + for (JDeclaredType type : program.getDeclaredTypes()) { + instantiate(type); + for (JField field : type.getFields()) { + flowInto(field); + } + for (JMethod method : type.getMethods()) { + flowInto(method); + } + } + + mainLoop(); + + computeOverrides(); + if (errorsFound) { + throw new UnableToCompleteException(); + } + } + + /** + * For normal compilation, only translate and stitch types reachable from + * entry points. This reduces memory and improves compile speed. Any + * unreachable elements are pruned. + */ + public void exec(TreeLogger logger) throws UnableToCompleteException { this.logger = logger; // Trace execution from entry points. @@ -577,14 +613,7 @@ } } - /* - * Main loop: run through the queue doing deferred resolution. We could have - * made this entirely recursive, but a work queues uses much less max stack. - */ - UnifyVisitor visitor = new UnifyVisitor(); - while (!todo.isEmpty()) { - visitor.accept(todo.poll()); - } + mainLoop(); // Post-stitching clean-ups. @@ -614,21 +643,9 @@ } } - // Compute overrides. - for (JDeclaredType type : program.getDeclaredTypes()) { - Map<String, Set<JMethod>> collected = new HashMap<String, Set<JMethod>>(); - for (JMethod method : type.getMethods()) { - if (method.canBePolymorphic()) { - collected.put(method.getSignature(), new LinkedHashSet<JMethod>()); - } - } - collectUpRefsInSupers(type, collected); - for (JMethod method : type.getMethods()) { - if (method.canBePolymorphic()) { - Set<JMethod> uprefs = collected.get(method.getSignature()); - method.addOverrides(Lists.create(uprefs)); - } - } + computeOverrides(); + if (errorsFound) { + throw new UnableToCompleteException(); } } @@ -674,16 +691,48 @@ } } + /** + * Compute all overrides. + */ + private void computeOverrides() { + for (JDeclaredType type : program.getDeclaredTypes()) { + Map<String, Set<JMethod>> collected = new HashMap<String, Set<JMethod>>(); + for (JMethod method : type.getMethods()) { + if (method.canBePolymorphic()) { + collected.put(method.getSignature(), new LinkedHashSet<JMethod>()); + } + } + collectUpRefsInSupers(type, collected); + for (JMethod method : type.getMethods()) { + if (method.canBePolymorphic()) { + Set<JMethod> uprefs = collected.get(method.getSignature()); + method.addOverrides(Lists.create(uprefs)); + } + } + } + } + private void error(JNode x, String errorMessage) { - // TODO: logging. - throw new InternalCompilerException(x, errorMessage, null); + errorsFound = true; + TreeLogger branch = + logger + .branch(TreeLogger.ERROR, "Errors in '" + x.getSourceInfo().getFileName() + "'", null); + // Append 'Line #: msg' to the error message. + StringBuffer msgBuf = new StringBuffer(); + int line = x.getSourceInfo().getStartLine(); + if (line > 0) { + msgBuf.append("Line "); + msgBuf.append(line); + msgBuf.append(": "); + } + msgBuf.append(errorMessage); + branch.log(TreeLogger.ERROR, msgBuf.toString()); } private void flowInto(JField field) { - if (field == JField.NULL_FIELD) { + if (field == JField.NULL_FIELD || field.isExternal()) { return; } - assert !field.isExternal(); if (!liveFieldsAndMethods.contains(field)) { liveFieldsAndMethods.add(field); field.setType(translate(field.getType())); @@ -694,10 +743,9 @@ } private void flowInto(JMethod method) { - if (method == JMethod.NULL_METHOD) { + if (method == JMethod.NULL_METHOD || method.isExternal()) { return; } - assert !method.isExternal(); if (!liveFieldsAndMethods.contains(method)) { liveFieldsAndMethods.add(method); JType originalReturnType = translate(method.getOriginalReturnType()); @@ -785,6 +833,17 @@ return type == program.getJavaScriptObject() || isJso(type.getSuperClass()); } + /** + * Main loop: run through the queue doing deferred resolution. We could have + * made this entirely recursive, but a work queue uses much less max stack. + */ + private void mainLoop() { + UnifyVisitor visitor = new UnifyVisitor(); + while (!todo.isEmpty()) { + visitor.accept(todo.poll()); + } + } + private void mapApi(JDeclaredType type) { assert !type.isExternal(); for (JField field : type.getFields()) { @@ -888,47 +947,75 @@ } private JDeclaredType translate(JDeclaredType type) { - if (type.isExternal()) { - String typeName = type.getName(); - type = searchForType(typeName); + if (!type.isExternal()) { + return type; } - assert !type.isExternal(); + + String typeName = type.getName(); + JDeclaredType newType = searchForType(typeName); + if (newType != null) { + assert !newType.isExternal(); + return newType; + } + // Error condition, should be logged. + errorsFound = true; return type; } private JField translate(JField field) { - if (field.isExternal()) { - JDeclaredType enclosingType = field.getEnclosingType(); - String sig = enclosingType.getName() + '.' + field.getSignature(); - field = fieldMap.get(sig); - if (field == null) { - mapApi(translate(enclosingType)); - // Now the field should be there. - field = fieldMap.get(sig); - if (field == null) { - // TODO: error logging - throw new NoSuchFieldError(sig); - } - } + if (!field.isExternal()) { + return field; } + + JDeclaredType enclosingType = field.getEnclosingType(); + String sig = enclosingType.getName() + '.' + field.getSignature(); + JField newField = fieldMap.get(sig); + if (newField != null) { + return newField; + } + + enclosingType = translate(enclosingType); + if (enclosingType.isExternal()) { + // Error condition, should be logged. + return field; + } + mapApi(enclosingType); + + // Now the field should be there. + field = fieldMap.get(sig); + if (field == null) { + // TODO: error logging + throw new NoSuchFieldError(sig); + } + assert !field.isExternal(); return field; } private JMethod translate(JMethod method) { - if (method.isExternal()) { - JDeclaredType enclosingType = method.getEnclosingType(); - String sig = enclosingType.getName() + '.' + method.getSignature(); - method = methodMap.get(sig); - if (method == null) { - mapApi(translate(enclosingType)); - // Now the method should be there. - method = methodMap.get(sig); - if (method == null) { - // TODO: error logging - throw new NoSuchMethodError(sig); - } - } + if (!method.isExternal()) { + return method; + } + + JDeclaredType enclosingType = method.getEnclosingType(); + String sig = enclosingType.getName() + '.' + method.getSignature(); + JMethod newMethod = methodMap.get(sig); + if (newMethod != null) { + return newMethod; + } + + enclosingType = translate(enclosingType); + if (enclosingType.isExternal()) { + // Error condition, should be logged. + return method; + } + mapApi(enclosingType); + + // Now the method should be there. + method = methodMap.get(sig); + if (method == null) { + // TODO: error logging + throw new NoSuchMethodError(sig); } assert !method.isExternal(); return method;
diff --git a/dev/core/test/com/google/gwt/dev/jjs/JavaAstConstructor.java b/dev/core/test/com/google/gwt/dev/jjs/JavaAstConstructor.java index a333f54..94a6be6 100644 --- a/dev/core/test/com/google/gwt/dev/jjs/JavaAstConstructor.java +++ b/dev/core/test/com/google/gwt/dev/jjs/JavaAstConstructor.java
@@ -20,31 +20,13 @@ import com.google.gwt.dev.javac.CompilationState; import com.google.gwt.dev.javac.testing.impl.JavaResourceBase; import com.google.gwt.dev.javac.testing.impl.MockJavaResource; -import com.google.gwt.dev.jdt.AbstractCompiler.CompilationResults; -import com.google.gwt.dev.jdt.BasicWebModeCompiler; -import com.google.gwt.dev.jdt.FindDeferredBindingSitesVisitor; -import com.google.gwt.dev.jjs.CorrelationFactory.DummyCorrelationFactory; import com.google.gwt.dev.jjs.ast.JDeclaredType; import com.google.gwt.dev.jjs.ast.JMethod; import com.google.gwt.dev.jjs.ast.JProgram; -import com.google.gwt.dev.jjs.impl.AssertionNormalizer; -import com.google.gwt.dev.jjs.impl.BuildTypeMap; -import com.google.gwt.dev.jjs.impl.FixAssignmentToUnbox; -import com.google.gwt.dev.jjs.impl.GenerateJavaAST; -import com.google.gwt.dev.jjs.impl.ImplementClassLiteralsAsFields; -import com.google.gwt.dev.jjs.impl.TypeLinker; -import com.google.gwt.dev.jjs.impl.TypeMap; -import com.google.gwt.dev.js.ast.JsProgram; - -import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; -import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.Set; -import java.util.TreeSet; /** * Constructs a Java AST for testing. @@ -214,65 +196,9 @@ public static JProgram construct(TreeLogger logger, CompilationState state, String... entryPoints) throws UnableToCompleteException { - return construct(logger, state, TypeLinker.NULL_TYPE_LINKER, entryPoints); - } - - public static JProgram construct(TreeLogger logger, CompilationState state, TypeLinker linker, - String... entryPoints) throws UnableToCompleteException { - Set<String> allRootTypes = new TreeSet<String>(Arrays.asList(entryPoints)); - for (MockJavaResource resource : getCompilerTypes()) { - allRootTypes.add(resource.getTypeName()); - } - - CompilationResults units = - BasicWebModeCompiler.getCompilationUnitDeclarations(logger, state, linker, allRootTypes - .toArray(new String[allRootTypes.size()])); - - CompilationUnitDeclaration[] goldenCuds = units.compiledUnits; - - // Check for compilation problems. We don't log here because any problems - // found here will have already been logged by AbstractCompiler. - // - JavaToJavaScriptCompiler.checkForErrors(logger, goldenCuds, false); - - - // Find errors in usage of GWT.create() calls - for (CompilationUnitDeclaration jdtCud : goldenCuds) { - jdtCud.traverse(new FindDeferredBindingSitesVisitor(), jdtCud.scope); - } - - JavaToJavaScriptCompiler.checkForErrors(logger, goldenCuds, true); - - CorrelationFactory correlator = DummyCorrelationFactory.INSTANCE; - JProgram jprogram = new JProgram(correlator); - JsProgram jsProgram = new JsProgram(correlator); - - /* - * (1) Build a flattened map of TypeDeclarations => JType. The resulting map - * contains entries for all reference types. BuildTypeMap also parses all - * JSNI. - */ - TypeMap typeMap = new TypeMap(jprogram); - TypeDeclaration[] allTypeDeclarations = BuildTypeMap.exec(typeMap, units, jsProgram, linker); - - // BuildTypeMap can uncover syntactic JSNI errors; report & abort - JavaToJavaScriptCompiler.checkForErrors(logger, goldenCuds, true); - - // Compute all super type/sub type info - jprogram.typeOracle.computeBeforeAST(); - - // (2) Create our own Java AST from the JDT AST. JJSOptionsImpl options = new JJSOptionsImpl(); options.setEnableAssertions(true); - GenerateJavaAST.exec(allTypeDeclarations, typeMap, jprogram, options); - - // GenerateJavaAST can uncover semantic JSNI errors; report & abort - JavaToJavaScriptCompiler.checkForErrors(logger, goldenCuds, true); - - // (3) Perform Java AST normalizations. - FixAssignmentToUnbox.exec(jprogram); - // Turn into assertion checking calls. - AssertionNormalizer.exec(jprogram); + JProgram jprogram = AstConstructor.construct(logger, state, options); // Add entry methods for entry points. for (String entryPoint : entryPoints) { @@ -283,9 +209,6 @@ } } } - - ImplementClassLiteralsAsFields.exec(jprogram); - // Tree is now ready to optimize. return jprogram; }
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/JJSTestBase.java b/dev/core/test/com/google/gwt/dev/jjs/impl/JJSTestBase.java index 21ae7b5..a1087b0 100644 --- a/dev/core/test/com/google/gwt/dev/jjs/impl/JJSTestBase.java +++ b/dev/core/test/com/google/gwt/dev/jjs/impl/JJSTestBase.java
@@ -235,11 +235,17 @@ } }); addBuiltinClasses(sourceOracle); - CompilationState state = CompilationStateBuilder.buildFrom(logger, - sourceOracle.getResources(), getAdditionalTypeProviderDelegate()); - JProgram program = JavaAstConstructor.construct(logger, state, - "test.EntryPoint", "com.google.gwt.lang.Exceptions"); - return program; + boolean wasEnabled = GwtAstBuilder.ENABLED; + try { + GwtAstBuilder.ENABLED = true; + CompilationState state = CompilationStateBuilder.buildFrom(logger, + sourceOracle.getResources(), getAdditionalTypeProviderDelegate()); + JProgram program = JavaAstConstructor.construct(logger, state, + "test.EntryPoint", "com.google.gwt.lang.Exceptions"); + return program; + } finally { + GwtAstBuilder.ENABLED = wasEnabled; + } } protected void addBuiltinClasses(MockResourceOracle sourceOracle) {