All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.gwt.core.client.impl.Impl Maven / Gradle / Ivy

There is a newer version: 2.10.0
Show newest version
/*
 * 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.core.client.impl;

import com.google.gwt.core.client.Duration;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.GWT.UncaughtExceptionHandler;
import com.google.gwt.core.client.GwtScriptOnly;
import com.google.gwt.core.client.JavaScriptException;
import com.google.gwt.core.client.JavaScriptObject;

/**
 * Private implementation class for GWT core. This API is should not be
 * considered public or stable.
 */
public final class Impl {

  static {
    if (GWT.isScript() && StackTraceCreator.collector != null) {
      // Just enforces loading of StackTraceCreator early on, nothing else to do here...
    }
  }

  private static final int WATCHDOG_ENTRY_DEPTH_CHECK_INTERVAL_MS = 2000;

  /**
   * Used by {@link #entry0(Object, Object)} to handle reentrancy.
   */
  private static int entryDepth = 0;

  /**
   * TimeStamp indicating last scheduling of the entry depth watchdog.
   */
  private static double watchdogEntryDepthLastScheduled;

  /**
   * Timer id of the entry depth watchdog. -1 if not scheduled.
   */
  private static int watchdogEntryDepthTimerId = -1;

  /**
   * This method should be used whenever GWT code is entered from a JS context
   * and there is no GWT code in the same module on the call stack. Examples
   * include event handlers, exported methods, and module initialization.
   * 

* The GWT compiler and Development Mode will provide a module-scoped * variable, $entry, which is an alias for this method. *

* This method can be called reentrantly, which will simply delegate to the * function. *

* The function passed to this method will be invoked via * Function.apply() with the current this value and * the invocation arguments passed to $entry. * * @param jsFunction a JS function to invoke, which is typically a JSNI * reference to a static Java method * @return the value returned when jsFunction is invoked, or * undefined if the UncaughtExceptionHandler catches an * exception raised by jsFunction */ public static native JavaScriptObject entry(JavaScriptObject jsFunction) /*-{ return function() { if (@com.google.gwt.core.client.GWT::isScript()()) { return @Impl::entry0(*)(jsFunction, this, arguments); } else { var _ = @Impl::entry0(*)(jsFunction, this, arguments); if (_ != null) { // Unwraps for Development Mode (see #apply()) _ = _.val; } return _; } }; }-*/; public static native String getHostPageBaseURL() /*-{ var s = $doc.location.href; // Pull off any hash. var i = s.indexOf('#'); if (i != -1) s = s.substring(0, i); // Pull off any query string. i = s.indexOf('?'); if (i != -1) s = s.substring(0, i); // Rip off everything after the last slash. i = s.lastIndexOf('/'); if (i != -1) s = s.substring(0, i); // Ensure a final slash if non-empty. return s.length > 0 ? s + "/" : ""; }-*/; public static native String getModuleBaseURL() /*-{ // Check to see if DevModeRedirectHook has set an alternate value. // The key should match DevModeRedirectHook.js. var key = "__gwtDevModeHook:" + $moduleName + ":moduleBase"; var global = $wnd || self; return global[key] || $moduleBase; }-*/; public static native String getModuleBaseURLForStaticFiles() /*-{ return $moduleBase; }-*/; public static native String getModuleName() /*-{ return $moduleName; }-*/; /** * Returns the obfuscated name of members in the compiled output. This is a thin wrapper around * JNameOf AST nodes and is therefore meaningless to implement in Development Mode. * If the requested member is a method, the method will not be devirtualized, inlined or prunned. * * @param jsniIdent a string literal specifying a type, field, or method. Raw * type names may also be used to obtain the name of the type's seed * function. * @return the name by which the named member can be accessed at runtime, or * null if the requested member has been pruned from the * output. */ public static String getNameOf(String jsniIdent) { /* * In Production Mode, the compiler directly replaces calls to this method * with a string literal expression. */ assert !GWT.isScript() : "ReplaceRebinds failed to replace this method"; throw new UnsupportedOperationException( "Impl.getNameOf() is unimplemented in Development Mode"); } public static native String getPermutationStrongName() /*-{ return $strongName; }-*/; /** * UncaughtExceptionHandler that is used by unit tests to spy on uncaught * exceptions. */ private static UncaughtExceptionHandler uncaughtExceptionHandlerForTest; /** * Set an uncaught exception handler to spy on uncaught exceptions in unit * tests. *

* Setting this method will not interfere with any exception handling logic; * i.e. {@link GWT#getUncaughtExceptionHandler()} will still return null if a * handler is not set via {@link GWT#setUncaughtExceptionHandler}. */ public static void setUncaughtExceptionHandlerForTest( UncaughtExceptionHandler handler) { uncaughtExceptionHandlerForTest = handler; } private static boolean onErrorInitialized; public static void maybeInitializeWindowOnError() { if ("IGNORE".equals(System.getProperty("gwt.uncaughtexceptionhandler.windowonerror"))) { return; } if (onErrorInitialized) { return; } onErrorInitialized = true; boolean alwaysReport = "REPORT" .equals(System.getProperty("gwt.uncaughtexceptionhandler.windowonerror")); registerWindowOnError(alwaysReport); } // Make sure dev mode does not try to parse the JSNI method since it contains a reference to // Throwable.of which is not standard Java @SuppressWarnings("deprecation") @GwtScriptOnly public static native void registerWindowOnError(boolean reportAlways) /*-{ function errorHandler(msg, url, line, column, error) { // IE8, IE9, IE10, safari 9, do not have an error passed if (!error) { error = msg + " (" + url + ":" + line // IE8 and IE9 do not have the column number if (column) { error += ":" + column } error += ")"; } var throwable = @java.lang.Throwable::of(*)(error); @Impl::reportWindowOnError(*)(throwable); }; function addOnErrorHandler(windowRef) { var origHandler = windowRef.onerror; if (origHandler && !reportAlways) { return; } windowRef.onerror = function() { errorHandler.apply(this, arguments); if (origHandler) { origHandler.apply(this, arguments); } return false; }; } // Note we need to trap both window.onerror and $wnd.onerror // Chrome 58 & Safari (10.1) & HtmlUnit uses $wnd.error, // while FF (53) /IE (even edge) listens on window.error addOnErrorHandler($wnd); addOnErrorHandler(window); }-*/; private static void reportWindowOnError(Throwable e) { // If the error is coming from window.onerror that we registered on, we can not report it // back to the browser since we would end up being called again reportUncaughtException(e, false); } public static void reportUncaughtException(Throwable e) { reportUncaughtException(e, true); } private static void reportUncaughtException( Throwable e, boolean reportSwallowedExceptionToBrowser) { if (Impl.uncaughtExceptionHandlerForTest != null) { Impl.uncaughtExceptionHandlerForTest.onUncaughtException(e); } UncaughtExceptionHandler handler = GWT.getUncaughtExceptionHandler(); if (handler != null) { if (handler == Impl.uncaughtExceptionHandlerForTest) { return; // Already reported so we're done. } // TODO(goktug): Handler might throw an exception but catching and reporting it to browser // here breaks assumptions of some existing hybrid apps that uses UCE for exception // conversion. We don't have an alternative functionality (yet) and it is too risky to include // the change in the release at last minute. handler.onUncaughtException(e); return; // Done. } // Make sure that the exception is not swallowed if (GWT.isClient() && reportSwallowedExceptionToBrowser) { reportToBrowser(e); } else { System.err.print("Uncaught exception "); e.printStackTrace(System.err); } } private static void reportToBrowser(Throwable e) { reportToBrowser(e instanceof JavaScriptException ? ((JavaScriptException) e).getThrown() : e); } private static native void reportToBrowser(Object e) /*-{ $wnd.setTimeout(function () { throw e; }, 0); }-*/; /** * Indicates if $entry has been called. */ public static boolean isEntryOnStack() { return entryDepth > 0; } /** * Indicates if $entry is present on the stack more than once. */ public static boolean isNestedEntry() { return entryDepth > 1; } /** * Implicitly called by JavaToJavaScriptCompiler.findEntryPoints(). */ public static native JavaScriptObject registerEntry() /*-{ if (@com.google.gwt.core.client.GWT::isScript()()) { // Assignment to $entry is done by the compiler return @Impl::entry(*); } else { // But we have to do in in Development Mode return $entry = @Impl::entry(*); } }-*/; private static native Object apply(Object jsFunction, Object thisObj, Object args) /*-{ if (@com.google.gwt.core.client.GWT::isScript()()) { return jsFunction.apply(thisObj, args); } else { var _ = jsFunction.apply(thisObj, args); if (_ != null) { // Wrap for Development Mode (unwrapped in #entry()) _ = { val: _ }; } return _; } }-*/; /** * Called by ModuleSpace in Development Mode when running onModuleLoads. */ private static boolean enter() { assert entryDepth >= 0 : "Negative entryDepth value at entry " + entryDepth; if (GWT.isScript() && entryDepth != 0) { double now = Duration.currentTimeMillis(); if (now - watchdogEntryDepthLastScheduled > WATCHDOG_ENTRY_DEPTH_CHECK_INTERVAL_MS) { watchdogEntryDepthLastScheduled = now; watchdogEntryDepthTimerId = watchdogEntryDepthSchedule(); } } // We want to disable some actions in the reentrant case if (entryDepth++ == 0) { SchedulerImpl.INSTANCE.flushEntryCommands(); return true; } return false; } /** * Implements {@link #entry(JavaScriptObject)}. */ private static Object entry0(Object jsFunction, Object thisObj, Object args) throws Throwable { boolean initialEntry = enter(); try { /* * Always invoke the UCE if we have one so that the exception never * percolates up to the browser's event loop, even in a reentrant * situation. */ if (GWT.getUncaughtExceptionHandler() != null) { /* * This try block is guarded by the if statement so that we don't molest * the exception object traveling up the stack unless we're capable of * doing something useful with it. */ try { return apply(jsFunction, thisObj, args); } catch (Throwable t) { reportUncaughtException(t); return undefined(); } } else { // Can't handle any exceptions, let them percolate normally return apply(jsFunction, thisObj, args); } /* * DO NOT ADD catch(Throwable t) here, it would always wrap the thrown * value. Instead, entry() has a general catch-all block. */ } finally { exit(initialEntry); } } /** * Called by ModuleSpace in Development Mode when running onModuleLoads. */ private static void exit(boolean initialEntry) { if (initialEntry) { SchedulerImpl.INSTANCE.flushFinallyCommands(); } // Decrement after we call flush entryDepth--; assert entryDepth >= 0 : "Negative entryDepth value at exit " + entryDepth; if (initialEntry) { assert entryDepth == 0 : "Depth not 0" + entryDepth; if (GWT.isScript() && watchdogEntryDepthTimerId != -1) { watchdogEntryDepthCancel(watchdogEntryDepthTimerId); watchdogEntryDepthTimerId = -1; } } } private static native Object undefined() /*-{ // Intentionally not returning a value return; }-*/; private static native void watchdogEntryDepthCancel(int timerId) /*-{ $wnd.clearTimeout(timerId); }-*/; private static void watchdogEntryDepthRun() { // Note: this must NEVER be called nested in a $entry() call. // This method is call from a "setTimeout": entryDepth should be set to 0. if (GWT.isScript() && entryDepth != 0) { entryDepth = 0; } watchdogEntryDepthTimerId = -1; // Timer has run. } private static native int watchdogEntryDepthSchedule() /*-{ return $wnd.setTimeout(@Impl::watchdogEntryDepthRun(), 10); }-*/; }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy