| /* |
| * 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.core.ext.soyc.impl; |
| |
| import com.google.gwt.core.ext.TreeLogger; |
| import com.google.gwt.dev.jjs.InternalCompilerException; |
| import com.google.gwt.dev.jjs.ast.JMethod; |
| import com.google.gwt.dev.jjs.ast.JProgram; |
| import com.google.gwt.dev.jjs.ast.JRunAsync; |
| import com.google.gwt.dev.jjs.impl.CodeSplitter.MultipleDependencyGraphRecorder; |
| import com.google.gwt.dev.jjs.impl.ControlFlowAnalyzer; |
| import com.google.gwt.util.tools.Utility; |
| |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.io.OutputStreamWriter; |
| import java.io.UnsupportedEncodingException; |
| import java.util.ArrayList; |
| import java.util.zip.GZIPOutputStream; |
| |
| /** |
| * The control-flow dependency recorder for Compile Report. |
| */ |
| public class DependencyRecorder implements MultipleDependencyGraphRecorder { |
| /** |
| * DependencyRecorder is not allowed to throw checked exceptions, because if |
| * it did then {@link com.google.gwt.dev.jjs.impl.CodeSplitter} and |
| * {@link ControlFlowAnalyzer} would throw exceptions all over the place. |
| * Instead, this class throws NestedIOExceptions that wrap them. |
| */ |
| public static class NestedIOException extends RuntimeException { |
| public NestedIOException(IOException e) { |
| super(e); |
| } |
| } |
| |
| private final StringBuilder builder = new StringBuilder(); |
| private final OutputStream finalOutput; |
| private OutputStreamWriter writer; |
| |
| public DependencyRecorder(OutputStream out) { |
| this.finalOutput = out; |
| } |
| |
| public void close() { |
| printPost(); |
| flushOutput(); |
| try { |
| writer.close(); |
| } catch (IOException e) { |
| throw new NestedIOException(e); |
| } |
| } |
| |
| public void endDependencyGraph() { |
| builder.append("</table>"); |
| flushOutput(); |
| } |
| |
| /** |
| * Used to record the dependencies of a specific method. |
| */ |
| public void methodIsLiveBecause(JMethod liveMethod, ArrayList<JMethod> dependencyChain) { |
| printMethodDependency(dependencyChain); |
| } |
| |
| public void open() { |
| try { |
| this.writer = new OutputStreamWriter(new GZIPOutputStream(finalOutput), "UTF-8"); |
| } catch (UnsupportedEncodingException e) { |
| throw new InternalCompilerException("UTF-8 is an unsupported encoding", e); |
| } catch (IOException e) { |
| throw new NestedIOException(e); |
| } |
| |
| printPre(); |
| } |
| |
| public void startDependencyGraph(String identifier, String extnds) { |
| builder.append("<table name=\""); |
| builder.append(identifier); |
| builder.append("\""); |
| if (extnds != null) { |
| builder.append(" extends=\""); |
| builder.append(extnds); |
| builder.append("\""); |
| } |
| builder.append(">\n"); |
| } |
| |
| /** |
| * Used to record dependencies of a program. |
| */ |
| protected void recordDependenciesImpl(TreeLogger logger, JProgram jprogram) { |
| |
| logger = logger.branch(TreeLogger.DEBUG, "Creating dependencies file for the compile report"); |
| |
| ControlFlowAnalyzer dependencyAnalyzer = new ControlFlowAnalyzer(jprogram); |
| dependencyAnalyzer.setDependencyRecorder(this); |
| |
| try { |
| printPre(); |
| for (JMethod method : jprogram.getEntryMethods()) { |
| dependencyAnalyzer.traverseFrom(method); |
| maybeFlushOutput(); |
| } |
| for (JRunAsync runAsync : jprogram.getRunAsyncs()) { |
| dependencyAnalyzer.traverseFromRunAsync(runAsync); |
| maybeFlushOutput(); |
| } |
| printPost(); |
| |
| flushOutput(); |
| Utility.close(writer); |
| |
| } catch (Throwable e) { |
| logger.log(TreeLogger.ERROR, "Could not write dependency file.", e); |
| } |
| } |
| |
| private void flushOutput() { |
| try { |
| writer.write(builder.toString()); |
| } catch (IOException e) { |
| throw new NestedIOException(e); |
| } |
| builder.setLength(0); |
| } |
| |
| private void maybeFlushOutput() { |
| if (builder.length() > 8 * 1024) { |
| flushOutput(); |
| } |
| } |
| |
| /** |
| * Records one dependency chain to a file. More specifically, it records the |
| * last link of the dependency chain. The full dependency chain can be |
| * recovered by code that reads the entire dependencies file, because it can |
| * do repeated lookups into the dependencies table to follow the chain. |
| */ |
| private void printMethodDependency(ArrayList<JMethod> dependencyChain) { |
| int size = dependencyChain.size(); |
| if (size < 2) { |
| return; |
| } |
| |
| JMethod curMethod = dependencyChain.get(size - 1); |
| builder.append("<method name=\""); |
| if (curMethod.getEnclosingType() != null) { |
| builder.append(curMethod.getEnclosingType().getName()); |
| builder.append("::"); |
| } |
| builder.append(curMethod.getName()); |
| builder.append("\">\n"); |
| |
| JMethod depMethod = dependencyChain.get(size - 2); |
| builder.append("<called by=\""); |
| if (depMethod.getEnclosingType() != null) { |
| builder.append(depMethod.getEnclosingType().getName()); |
| builder.append("::"); |
| } |
| builder.append(depMethod.getName()); |
| builder.append("\"/>\n"); |
| builder.append("</method>\n"); |
| } |
| |
| /** |
| * Prints the closing lines necessary for the dependencies file. |
| */ |
| private void printPost() { |
| builder.append("</soyc-dependencies>\n"); |
| } |
| |
| /** |
| * Prints the preamble necessary for the dependencies file. |
| */ |
| private void printPre() { |
| builder.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<soyc-dependencies>\n"); |
| } |
| } |