/* * Copyright 2008 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.core.ext.linker.ModuleMetricsArtifact; import com.google.gwt.core.ext.linker.PrecompilationMetricsArtifact; import com.google.gwt.dev.Permutation; import com.google.gwt.dev.jjs.ast.JProgram; import com.google.gwt.dev.js.ast.JsProgram; import com.google.gwt.dev.util.DiskCache; import com.google.gwt.dev.util.Util; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Collections; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; /** * Represents a unified, non-permutation specific AST. This AST is used to drive * per-permutation compiles. */ public class UnifiedAst implements Serializable { /** * Encapsulates the combined programs. */ public static final class AST implements Serializable { private final JProgram jProgram; private final JsProgram jsProgram; public AST(JProgram jProgram, JsProgram jsProgram) { this.jProgram = jProgram; this.jsProgram = jsProgram; } public JProgram getJProgram() { return jProgram; } public JsProgram getJsProgram() { return jsProgram; } } private static final DiskCache diskCache = DiskCache.INSTANCE; /** * The original AST; nulled out once consumed (by the first call to * {@link #getFreshAst()}. */ private transient AST initialAst; /** * Metrics for the module load phase. Stored here so they can be written out * as artifacts in the compile phase. */ private ModuleMetricsArtifact moduleMetrics; /** * Used for internal synchronization. */ private transient Object myLockObject = new Object(); /** * The compilation options. */ private final JJSOptions options; /** * Metrics for the precompilation phase. Stored here so they can be written * out as artifacts in the compile phase. */ private PrecompilationMetricsArtifact precompilationMetrics; /** * The set of all live rebind request types in the AST. */ private final SortedSet<String> rebindRequests; /** * The serialized AST. */ private transient long serializedAstToken; public UnifiedAst(JJSOptions options, AST initialAst, boolean singlePermutation, Set<String> rebindRequests) { this.options = new JJSOptionsImpl(options); this.initialAst = initialAst; this.rebindRequests = Collections.unmodifiableSortedSet(new TreeSet<String>(rebindRequests)); this.serializedAstToken = singlePermutation ? -1 : diskCache.writeObject(initialAst); } /** * Copy constructor, invalidates the original. */ UnifiedAst(UnifiedAst other) { this.options = other.options; this.initialAst = other.initialAst; other.initialAst = null; // steal its copy this.rebindRequests = other.rebindRequests; this.serializedAstToken = other.serializedAstToken; } /** * Compiles a particular permutation. * * @param logger the logger to use * @param permutation the permutation to compile * @return the permutation result * @throws UnableToCompleteException if an error other than * {@link OutOfMemoryError} occurs */ public PermutationResult compilePermutation(TreeLogger logger, Permutation permutation) throws UnableToCompleteException { return JavaToJavaScriptCompiler.compilePermutation(logger, this, permutation); } /** * Return the current AST so that clients can explicitly walk the Java or * JavaScript parse trees. * * @return the current AST object holding the Java and JavaScript trees. */ public AST getFreshAst() { synchronized (myLockObject) { if (initialAst != null) { AST result = initialAst; initialAst = null; return result; } else { if (serializedAstToken < 0) { throw new IllegalStateException( "No serialized AST was cached and AST was already consumed."); } return diskCache.readObject(serializedAstToken, AST.class); } } } /** * Returns metrics about the module load portion of the build. */ public ModuleMetricsArtifact getModuleMetrics() { return moduleMetrics; } /** * Returns the active set of JJS options associated with this compile. */ public JJSOptions getOptions() { return new JJSOptionsImpl(options); } /** * Returns metrics about the precompilation portion of the build. */ public PrecompilationMetricsArtifact getPrecompilationMetrics() { return precompilationMetrics; } /** * Returns the set of live rebind requests in the AST. */ public SortedSet<String> getRebindRequests() { return rebindRequests; } /** * Internally prepares a new AST for compilation if one is not already * prepared. */ public void prepare() { synchronized (myLockObject) { if (initialAst == null) { initialAst = diskCache.readObject(serializedAstToken, AST.class); } } } /** * Save some module load metrics in the AST. */ public void setModuleMetrics(ModuleMetricsArtifact metrics) { this.moduleMetrics = metrics; } /** * Save some precompilation metrics in the AST. */ public void setPrecompilationMetrics(PrecompilationMetricsArtifact metrics) { this.precompilationMetrics = metrics; } /** * Re-initialize lock object; copy serialized AST straight to cache. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); myLockObject = new Object(); serializedAstToken = diskCache.transferFromStream(stream); } /** * Force byte serialization of AST before writing. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); if (serializedAstToken >= 0) { // Copy the bytes. diskCache.transferToStream(serializedAstToken, stream); } else if (initialAst != null) { // Serialize into raw bytes. Util.writeObjectToStream(stream, initialAst); } else { throw new IllegalStateException("No serialized AST was cached and AST was already consumed."); } } }