blob: c31fbf96ad981cd5199d945ee43f2e70396730e9 [file] [log] [blame]
/*
* 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.impl;
import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.PropertyOracle;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.dev.cfg.BindingProperty;
import com.google.gwt.dev.cfg.ConfigurationProperty;
import com.google.gwt.dev.cfg.StaticPropertyOracle;
import com.google.gwt.dev.javac.StandardGeneratorContext;
import com.google.gwt.dev.jdt.FindDeferredBindingSitesVisitor;
import java.io.PrintWriter;
/**
* Generates code for loading an island. The pattern of generated classes is
* more complicated than otherwise necessary so that the loader code is
* precisely handled by TypeTightener and LivenessAnalyzer.
*
* TODO(spoon) Remove this generator by making LivenessAnalyzer know about
* runAsync and how it works.
*/
public class FragmentLoaderCreator {
public static final String ASYNC_FRAGMENT_LOADER = "com.google.gwt.core.client.impl.AsyncFragmentLoader";
public static final String ASYNC_LOADER_CLASS_PREFIX = "AsyncLoader";
public static final String ASYNC_LOADER_PACKAGE = "com.google.gwt.lang.asyncloaders";
public static final String BROWSER_LOADER = "AsyncFragmentLoader.BROWSER_LOADER";
public static final String CALLBACK_LIST_SUFFIX = "__Callback";
public static final String LOADER_METHOD_RUN_ASYNC = "runAsync";
public static final String RUN_ASYNC_CALLBACK = "com.google.gwt.core.client.RunAsyncCallback";
public static final String RUN_CALLBACKS = "runCallbacks";
private static final String GWT_CLASS = FindDeferredBindingSitesVisitor.MAGIC_CLASS;
private static final String PROP_RUN_ASYNC_NEVER_RUNS = "gwt.jjs.runAsyncNeverRuns";
private static final String UNCAUGHT_EXCEPTION_HANDLER_CLASS = "GWT.UncaughtExceptionHandler";
private final StandardGeneratorContext context;
private int entryNumber = 0;
private final PropertyOracle propOracle;
/**
* Construct a FragmentLoaderCreator. The reason it needs so many parameters
* is that it uses generator infrastructure.
*/
public FragmentLoaderCreator(StandardGeneratorContext context) {
// An empty property oracle is fine, because fragment loaders aren't
// affected by properties anyway
this.propOracle = new StaticPropertyOracle(new BindingProperty[0],
new String[0], new ConfigurationProperty[0]);
this.context = context;
}
public String create(TreeLogger logger) throws UnableToCompleteException {
// First entry is 1.
++entryNumber;
context.setPropertyOracle(propOracle);
PrintWriter loaderWriter = getSourceWriterForLoader(logger, context);
if (loaderWriter == null) {
logger.log(TreeLogger.ERROR, "Failed to create island loader named "
+ getLoaderQualifiedName());
throw new UnableToCompleteException();
}
generateLoaderFields(loaderWriter);
generateOnLoadMethod(loaderWriter);
generateRunAsyncMethod(loaderWriter);
generateRunCallbacksMethod(loaderWriter);
generateRunCallbackOnFailuresMethod(loaderWriter);
loaderWriter.println("}");
loaderWriter.close();
context.commit(logger, loaderWriter);
writeCallbackListClass(logger, context);
return getLoaderQualifiedName();
}
private void generateLoaderFields(PrintWriter srcWriter) {
srcWriter.println("// Callbacks that are pending");
srcWriter.println("private static " + getCallbackListSimpleName()
+ " callbacksHead = null;");
srcWriter.println("// The tail of the callbacks list");
srcWriter.println("private static " + getCallbackListSimpleName()
+ " callbacksTail = null;");
srcWriter.println("// A callback caller for this entry point");
srcWriter.println("private static " + getLoaderSimpleName()
+ " instance = null;");
}
private void generateOnLoadMethod(PrintWriter srcWriter) {
srcWriter.println("public static void onLoad() {");
srcWriter.println("instance = new " + getLoaderSimpleName() + "();");
srcWriter.println(BROWSER_LOADER + ".fragmentHasLoaded(" + entryNumber
+ ");");
srcWriter.println(BROWSER_LOADER + ".logEventProgress(\""
+ RUN_CALLBACKS + entryNumber + "\", \"begin\");");
srcWriter.println("instance." + RUN_CALLBACKS + "();");
srcWriter.println(BROWSER_LOADER + ".logEventProgress(\""
+ RUN_CALLBACKS + entryNumber + "\", \"end\");");
srcWriter.println("}");
}
/**
* Generate the <code>runAsync</code> method. Calls to
* <code>GWT.runAsync</code> are replaced by calls to this method.
*/
private void generateRunAsyncMethod(PrintWriter srcWriter) {
srcWriter.println("public static void " + LOADER_METHOD_RUN_ASYNC
+ "(RunAsyncCallback callback) {");
srcWriter.println(getCallbackListSimpleName() + " newCallback = new "
+ getCallbackListSimpleName() + "();");
srcWriter.println("newCallback.callback = callback;");
srcWriter.println("if (callbacksTail != null) {");
srcWriter.println(" callbacksTail.next = newCallback;");
srcWriter.println("}");
srcWriter.println("callbacksTail = newCallback;");
srcWriter.println("if (callbacksHead == null) {");
srcWriter.println(" callbacksHead = newCallback;");
srcWriter.println("}");
srcWriter.println("if (instance != null) {");
srcWriter.println(" instance." + RUN_CALLBACKS + "();");
srcWriter.println(" return;");
srcWriter.println("}");
srcWriter.println("if (!" + BROWSER_LOADER + ".isLoading(" + entryNumber
+ ")) {");
srcWriter.println(" " + BROWSER_LOADER + ".inject(" + entryNumber + ",");
srcWriter.println(" new AsyncFragmentLoader.LoadTerminatedHandler() {");
srcWriter.println(" public void loadTerminated(Throwable reason) {");
srcWriter.println(" runCallbackOnFailures(reason);");
srcWriter.println(" }");
srcWriter.println(" });");
srcWriter.println("}");
srcWriter.println("}");
}
private void generateRunCallbackOnFailuresMethod(PrintWriter srcWriter) {
srcWriter.println("private static void runCallbackOnFailures(Throwable e) {");
srcWriter.println("while (callbacksHead != null) {");
srcWriter.println(" callbacksHead.callback.onFailure(e);");
srcWriter.println(" callbacksHead = callbacksHead.next;");
srcWriter.println("}");
srcWriter.println("callbacksTail = null;");
srcWriter.println("}");
}
private void generateRunCallbacksMethod(PrintWriter srcWriter) {
srcWriter.println("public void " + RUN_CALLBACKS + "() {");
srcWriter.println("while (callbacksHead != null) {");
srcWriter.println(" " + UNCAUGHT_EXCEPTION_HANDLER_CLASS + " handler = "
+ "GWT.getUncaughtExceptionHandler();");
srcWriter.println(" " + getCallbackListSimpleName()
+ " next = callbacksHead;");
srcWriter.println(" callbacksHead = callbacksHead.next;");
srcWriter.println(" if (callbacksHead == null) {");
srcWriter.println(" callbacksTail = null;");
srcWriter.println(" }");
if (!Boolean.getBoolean(PROP_RUN_ASYNC_NEVER_RUNS)) {
// TODO(spoon): this runs the callbacks immediately; deferred would be
// better
srcWriter.println(" if (handler == null) {");
srcWriter.println(" next.callback.onSuccess();");
srcWriter.println(" } else {");
srcWriter.println(" try {");
srcWriter.println(" next.callback.onSuccess();");
srcWriter.println(" } catch (Throwable e) {");
srcWriter.println(" handler.onUncaughtException(e);");
srcWriter.println(" }");
srcWriter.println(" }");
}
srcWriter.println("}");
srcWriter.println("}");
}
private String getCallbackListQualifiedName() {
return ASYNC_LOADER_PACKAGE + getCallbackListSimpleName();
}
private String getCallbackListSimpleName() {
return getLoaderSimpleName() + CALLBACK_LIST_SUFFIX;
}
private String getLoaderQualifiedName() {
return ASYNC_LOADER_PACKAGE + "." + getLoaderSimpleName();
}
private String getLoaderSimpleName() {
return ASYNC_LOADER_CLASS_PREFIX + entryNumber;
}
private String getPackage() {
return ASYNC_LOADER_PACKAGE;
}
private PrintWriter getSourceWriterForLoader(TreeLogger logger,
GeneratorContext ctx) {
PrintWriter printWriter = ctx.tryCreate(logger, getPackage(),
getLoaderSimpleName());
if (printWriter == null) {
return null;
}
printWriter.println("package " + getPackage() + ";");
String[] imports = new String[] {
GWT_CLASS, RUN_ASYNC_CALLBACK, ASYNC_FRAGMENT_LOADER};
for (String imp : imports) {
printWriter.println("import " + imp + ";");
}
printWriter.println("public class " + getLoaderSimpleName() + " {");
return printWriter;
}
private void writeCallbackListClass(TreeLogger logger, GeneratorContext ctx)
throws UnableToCompleteException {
PrintWriter printWriter = ctx.tryCreate(logger, getPackage(),
getCallbackListSimpleName());
if (printWriter == null) {
logger.log(TreeLogger.ERROR, "Could not create type: "
+ getCallbackListQualifiedName());
throw new UnableToCompleteException();
}
printWriter.println("package " + getPackage() + ";");
printWriter.println("public class " + getCallbackListSimpleName() + "{");
printWriter.println(RUN_ASYNC_CALLBACK + " callback;");
printWriter.println(getCallbackListSimpleName() + " next;");
printWriter.println("}");
printWriter.close();
ctx.commit(logger, printWriter);
}
}