Fixes issue #2334; native method within enum breaks in hosted mode.
Previously, JsniInjector worked by dropping static initializer blocks into the start of any class containing native methods. These static initializer blocks would inject native method definitions into the browser window at class load time, so they would subsequently be available to be called.
However, there is no possible way to produce static initializer code in an enum that runs before that
enum's fields get initialized; trying to put a static initializer block over the enum declaration is a syntax error. It's important that native methods initialize before the enum values are constructed, because the constructors could potentially rely on calling those native methods.
The solution is a complete redo of how native methods are injected. I removed the static initializers method in favor of injecting a class-level annotation, @JsniMethods, into the source code containing all of necessary information for CompilingClassLoader to inject the methods when the class is loaded, before it has a chance to initialize.
Found by: cromwellian
Review by: jat
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@2577 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/jdt/CacheManager.java b/dev/core/src/com/google/gwt/dev/jdt/CacheManager.java
index 5b67a73..c6895db 100644
--- a/dev/core/src/com/google/gwt/dev/jdt/CacheManager.java
+++ b/dev/core/src/com/google/gwt/dev/jdt/CacheManager.java
@@ -23,10 +23,12 @@
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.dev.shell.JavaScriptHost;
+import com.google.gwt.dev.shell.JsniMethods;
import com.google.gwt.dev.shell.ShellGWT;
import com.google.gwt.dev.shell.ShellJavaScriptHost;
-import com.google.gwt.dev.util.Util;
+import com.google.gwt.dev.shell.JsniMethods.JsniMethod;
import com.google.gwt.dev.util.PerfLogger;
+import com.google.gwt.dev.util.Util;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
@@ -517,18 +519,18 @@
* to be taken as given by the bytecode compiler.
*/
public static final Class<?>[] BOOTSTRAP_CLASSES = new Class<?>[] {
- JavaScriptHost.class, ShellJavaScriptHost.class, ShellGWT.class};
+ JavaScriptHost.class, ShellJavaScriptHost.class, ShellGWT.class,
+ JsniMethods.class, JsniMethod.class};
/**
* The set of bootstrap classes, which are marked transient, but are
* nevertheless not recompiled each time, as they are bootstrap classes.
*/
- private static final Set<String> TRANSIENT_CLASS_NAMES;
+ private static final Set<String> TRANSIENT_CLASS_NAMES = new HashSet<String>();
static {
- TRANSIENT_CLASS_NAMES = new HashSet<String>(BOOTSTRAP_CLASSES.length + 3);
- for (int i = 0; i < BOOTSTRAP_CLASSES.length; i++) {
- TRANSIENT_CLASS_NAMES.add(BOOTSTRAP_CLASSES[i].getName());
+ for (Class<?> c : BOOTSTRAP_CLASSES) {
+ TRANSIENT_CLASS_NAMES.add(c.getName());
}
}
diff --git a/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java b/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java
index af00878..437cd8b 100644
--- a/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java
+++ b/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java
@@ -23,6 +23,7 @@
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.dev.jdt.ByteCodeCompiler;
import com.google.gwt.dev.jdt.CacheManager;
+import com.google.gwt.dev.shell.JsniMethods.JsniMethod;
import com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter;
import com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.InstanceMethodOracle;
import com.google.gwt.dev.util.JsniRef;
@@ -34,6 +35,8 @@
import java.io.IOException;
import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
@@ -325,14 +328,36 @@
}
}
+ /**
+ * The names of the bridge classes.
+ */
+ private static final Map<String, Class<?>> BRIDGE_CLASS_NAMES = new HashMap<String, Class<?>>();
+
+ /**
+ * The set of classes exposed into user space that actually live in hosted
+ * space (thus, they bridge across the spaces).
+ */
+ private static final Class<?>[] BRIDGE_CLASSES = new Class<?>[] {
+ ShellJavaScriptHost.class, JsniMethods.class, JsniMethod.class};
+
+ static {
+ for (Class<?> c : BRIDGE_CLASSES) {
+ BRIDGE_CLASS_NAMES.put(c.getName(), c);
+ }
+ }
+
private final HostedModeClassRewriter classRewriter;
private final ByteCodeCompiler compiler;
private final DispatchClassInfoOracle dispClassInfoOracle = new DispatchClassInfoOracle();
+ private Class<?> javaScriptHostClass;
+
private final TreeLogger logger;
+ private ShellJavaScriptHost shellJavaScriptHost;
+
private final TypeOracle typeOracle;
@SuppressWarnings("unchecked")
@@ -344,11 +369,13 @@
AbstractReferenceMap.HARD, AbstractReferenceMap.WEAK);
public CompilingClassLoader(TreeLogger logger, ByteCodeCompiler compiler,
- TypeOracle typeOracle) throws UnableToCompleteException {
+ TypeOracle typeOracle, ShellJavaScriptHost javaScriptHost)
+ throws UnableToCompleteException {
super(null);
this.logger = logger;
this.compiler = compiler;
this.typeOracle = typeOracle;
+ this.shellJavaScriptHost = javaScriptHost;
// Assertions are always on in hosted mode.
setDefaultAssertionStatus(true);
@@ -360,8 +387,7 @@
// JavaScriptHost is special because its type cannot be known to the user.
// It is referenced only from generated code and GWT.create.
//
- for (int i = 0; i < CacheManager.BOOTSTRAP_CLASSES.length; i++) {
- Class<?> clazz = CacheManager.BOOTSTRAP_CLASSES[i];
+ for (Class<?> clazz : CacheManager.BOOTSTRAP_CLASSES) {
String className = clazz.getName();
try {
String path = clazz.getName().replace('.', '/').concat(".class");
@@ -490,11 +516,9 @@
throw new ClassNotFoundException(className);
}
- // MAGIC: this allows JavaScriptHost (in user space) to bridge to the real
- // class in host space.
- //
- if (className.equals(ShellJavaScriptHost.class.getName())) {
- return ShellJavaScriptHost.class;
+ // Check for a bridge class that spans hosted and user space.
+ if (BRIDGE_CLASS_NAMES.containsKey(className)) {
+ return BRIDGE_CLASS_NAMES.get(className);
}
// Get the bytes, compiling if necessary.
@@ -509,13 +533,32 @@
if (classRewriter != null) {
classBytes = classRewriter.rewrite(className, classBytes);
}
- return defineClass(className, classBytes, 0, classBytes.length);
+ Class<?> newClass = defineClass(className, classBytes, 0,
+ classBytes.length);
+
+ if (className.equals(JavaScriptHost.class.getName())) {
+ javaScriptHostClass = newClass;
+ updateJavaScriptHost();
+ }
+
+ JsniMethods jsniMethods = newClass.getAnnotation(JsniMethods.class);
+ if (jsniMethods != null) {
+ for (JsniMethod jsniMethod : jsniMethods.value()) {
+ shellJavaScriptHost.createNative(jsniMethod.file(),
+ jsniMethod.line(), jsniMethod.name(), jsniMethod.paramNames(),
+ jsniMethod.body());
+ }
+ }
+ return newClass;
} catch (UnableToCompleteException e) {
throw new ClassNotFoundException(className);
}
}
void clear() {
+ // Release our references to the shell.
+ shellJavaScriptHost = null;
+ updateJavaScriptHost();
weakJsoCache.clear();
weakJavaWrapperCache.clear();
dispClassInfoOracle.clear();
@@ -551,4 +594,38 @@
return false;
}
+
+ /**
+ * Tricky one, this. Reaches over into this modules's JavaScriptHost class and
+ * sets its static 'host' field to be the specified ModuleSpace instance
+ * (which will either be this ModuleSpace or null).
+ *
+ * @param moduleSpace the ModuleSpace instance to store using
+ * JavaScriptHost.setHost().
+ * @see JavaScriptHost
+ */
+ private void updateJavaScriptHost() {
+ // Find the application's JavaScriptHost interface.
+ //
+ Throwable caught;
+ try {
+ final Class<?>[] paramTypes = new Class[] {ShellJavaScriptHost.class};
+ Method setHostMethod = javaScriptHostClass.getMethod("setHost",
+ paramTypes);
+ setHostMethod.invoke(javaScriptHostClass,
+ new Object[] {shellJavaScriptHost});
+ return;
+ } catch (SecurityException e) {
+ caught = e;
+ } catch (NoSuchMethodException e) {
+ caught = e;
+ } catch (IllegalArgumentException e) {
+ caught = e;
+ } catch (IllegalAccessException e) {
+ caught = e;
+ } catch (InvocationTargetException e) {
+ caught = e.getTargetException();
+ }
+ throw new RuntimeException("Error initializing JavaScriptHost", caught);
+ }
}
diff --git a/dev/core/src/com/google/gwt/dev/shell/JavaScriptHost.java b/dev/core/src/com/google/gwt/dev/shell/JavaScriptHost.java
index 7d84f14..b0cd9f8 100644
--- a/dev/core/src/com/google/gwt/dev/shell/JavaScriptHost.java
+++ b/dev/core/src/com/google/gwt/dev/shell/JavaScriptHost.java
@@ -23,20 +23,6 @@
private static ShellJavaScriptHost sHost;
- /**
- * Defines a new native JavaScript function.
- *
- * @param file source file of the function
- * @param line starting line number of the function
- * @param jsniSignature the function's jsni signature
- * @param paramNames the parameter types
- * @param js the script body
- */
- public static void createNative(String file, int line, String jsniSignature,
- String[] paramNames, String js) {
- sHost.createNative(file, line, jsniSignature, paramNames, js);
- }
-
public static void exceptionCaught(Object exception) {
sHost.exceptionCaught(exception);
}
@@ -134,7 +120,7 @@
public static <T> T rebindAndCreate(Class<?> requestedClass) {
String className = requestedClass.getName();
try {
- return sHost.<T>rebindAndCreate(className);
+ return sHost.<T> rebindAndCreate(className);
} catch (Throwable e) {
String msg = "Deferred binding failed for '" + className
+ "' (did you forget to inherit a required module?)";
@@ -143,9 +129,9 @@
}
/**
- * This method is called via reflection from the shell, providing the hosted
- * mode application with all of the methods it needs to interface with the
- * browser and the server (for deferred binding).
+ * This method is called via reflection from the {@link CompilingClassLoader},
+ * providing the hosted mode application with all of the methods it needs to
+ * interface with the browser and the server (for deferred binding).
*/
public static void setHost(ShellJavaScriptHost host) {
sHost = host;
diff --git a/dev/core/src/com/google/gwt/dev/shell/JsniInjector.java b/dev/core/src/com/google/gwt/dev/shell/JsniInjector.java
index 723eadc..87eb407 100644
--- a/dev/core/src/com/google/gwt/dev/shell/JsniInjector.java
+++ b/dev/core/src/com/google/gwt/dev/shell/JsniInjector.java
@@ -26,11 +26,13 @@
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.dev.jdt.CompilationUnitProviderWithAlternateSource;
import com.google.gwt.dev.js.ast.JsBlock;
+import com.google.gwt.dev.shell.JsniMethods.JsniMethod;
import com.google.gwt.dev.util.Jsni;
import com.google.gwt.dev.util.StringCopier;
import com.google.gwt.dev.util.Util;
import java.io.File;
+import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.IdentityHashMap;
@@ -72,6 +74,12 @@
}
}
+ private static final String JSNIMETHOD_NAME = JsniMethod.class.getName().replace(
+ '$', '.');
+
+ private static final String JSNIMETHODS_NAME = JsniMethods.class.getName();
+
+ private final Map<JClassType, List<JsniMethod>> jsniMethodMap = new IdentityHashMap<JClassType, List<JsniMethod>>();
private final TypeOracle oracle;
private final Map<JMethod, JsBlock> parsedJsByMethod = new IdentityHashMap<JMethod, JsBlock>();
@@ -125,59 +133,107 @@
}
}
- /**
- * Static initialization: generate one call to 'JavaScriptHost.createNative()'
- * for each native method, to define the JavaScript code that will be invoked
- * later.
- */
- private char[] genInitializerBlock(String file, char[] source,
- JMethod[] methods) {
+ private JsniMethod createJsniMethod(JMethod method, String file, char[] source) {
- String escapedFile = Jsni.escapeQuotesAndSlashes(file);
+ final String escapedFile = Jsni.escapeQuotesAndSlashes(file);
- StringBuffer sb = new StringBuffer();
- sb.append(" static {");
- for (int i = 0; i < methods.length; ++i) {
- JMethod method = methods[i];
+ final int line = Jsni.countNewlines(source, 0, method.getBodyStart()) + 1;
- JsBlock jsniBody = parsedJsByMethod.get(method);
- if (jsniBody == null) {
- // Not a JSNI method.
- //
- continue;
+ final String name = Jsni.getJsniSignature(method);
+
+ JParameter[] params = method.getParameters();
+ final String[] paramNames = new String[params.length];
+ for (int i = 0; i < params.length; ++i) {
+ paramNames[i] = params[i].getName();
+ }
+
+ /*
+ * Surround the original JS body statements with a try/catch so that we can
+ * map JavaScript exceptions back into Java. Note that the method body
+ * itself will print curly braces, so we don't need them around the
+ * try/catch.
+ */
+ JsBlock jsniBody = parsedJsByMethod.get(method);
+ assert (jsniBody != null);
+ final String jsTry = "try ";
+ final String jsCatch = " catch (e) {\\n" + " __static[\\\"@"
+ + Jsni.JAVASCRIPTHOST_NAME + "::exceptionCaught"
+ + "(Ljava/lang/Object;)\\\"]" + "(e == null ? null : e);\\n" + "}\\n";
+ final String body = jsTry
+ + Jsni.generateEscapedJavaScriptForHostedMode(jsniBody) + jsCatch;
+
+ return new JsniMethod() {
+
+ public Class<? extends Annotation> annotationType() {
+ return JsniMethod.class;
}
- JParameter[] params = method.getParameters();
- String paramNamesArray = getParamNamesArrayExpr(params);
+ public String body() {
+ return body;
+ }
- final String jsTry = "try ";
- final String jsCatch = " catch (e) {\\n"
- + " __static[\\\"@"
- + Jsni.JAVASCRIPTHOST_NAME
- + "::exceptionCaught"
- + "(Ljava/lang/Object;)\\\"]"
- + "(e == null ? null : e);\\n"
- + "}\\n";
+ public String file() {
+ return escapedFile;
+ }
- /*
- * Surround the original JS body statements with a try/catch so that we
- * can map JavaScript exceptions back into Java. Note that the method body
- * itself will print curly braces, so we don't need them around the
- * try/catch.
- */
- String js = jsTry + Jsni.generateEscapedJavaScriptForHostedMode(jsniBody)
- + jsCatch;
- String jsniSig = Jsni.getJsniSignature(method);
+ public int line() {
+ return line;
+ }
- // figure out starting line number
- int bodyStart = method.getBodyStart();
- int line = Jsni.countNewlines(source, 0, bodyStart) + 1;
+ public String name() {
+ return name;
+ }
- sb.append(" " + Jsni.JAVASCRIPTHOST_NAME + ".createNative(\""
- + escapedFile + "\", " + line + ", " + "\"@" + jsniSig + "\", "
- + paramNamesArray + ", \"" + js + "\");");
+ public String[] paramNames() {
+ return paramNames;
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append("@" + JSNIMETHOD_NAME + "(file=\"");
+ sb.append(escapedFile);
+ sb.append("\",line=");
+ sb.append(line);
+ sb.append(",name=\"@");
+ sb.append(name);
+ sb.append("\",paramNames=");
+ sb.append(toStringParamArray());
+ sb.append(",body=\"");
+ sb.append(body);
+ sb.append("\")");
+ return sb.toString();
+ }
+
+ private String toStringParamArray() {
+ StringBuffer sb = new StringBuffer();
+ sb.append("{");
+ for (String paramName : paramNames) {
+ sb.append('\"');
+ sb.append(paramName);
+ sb.append('\"');
+ sb.append(", ");
+ }
+ sb.append("}");
+ return sb.toString();
+ }
+ };
+ }
+
+ /**
+ * Generate annotation metadata for all the JSNI methods in a list.
+ */
+ private char[] genJsniMethodsAnnotation(List<JsniMethod> jsniMethods,
+ boolean pretty) {
+ StringBuffer sb = new StringBuffer();
+ String nl = pretty ? "\n " : "";
+ sb.append("@" + JSNIMETHODS_NAME + "({");
+ for (JsniMethod jsniMethod : jsniMethods) {
+ sb.append(jsniMethod.toString());
+ sb.append(',');
+ sb.append(nl);
}
- sb.append("}");
+ sb.append("})");
return sb.toString().toCharArray();
}
@@ -284,35 +340,27 @@
return sb.toString();
}
- private String getParamNamesArrayExpr(JParameter[] params) {
- StringBuffer sb = new StringBuffer();
- sb.append("new String[] {");
- for (int i = 0, n = params.length; i < n; ++i) {
- if (i > 0) {
- sb.append(", ");
- }
-
- JParameter param = params[i];
- sb.append('\"');
- sb.append(param.getName());
- sb.append('\"');
- }
- sb.append("}");
- return sb.toString();
- }
-
private void rewriteCompilationUnit(TreeLogger logger, char[] source,
List<Replacement> changes, CompilationUnitProvider cup, boolean pretty)
throws UnableToCompleteException {
- // Hit all the types in the compilation unit.
+ // First create replacements for all native methods.
JClassType[] types = oracle.getTypesInCompilationUnit(cup);
- for (int i = 0; i < types.length; i++) {
- JClassType type = types[i];
+ for (JClassType type : types) {
if (!type.getQualifiedSourceName().startsWith("java.")) {
rewriteType(logger, source, changes, type, pretty);
}
}
+
+ // Then annotate the appropriate types with JsniMethod annotations.
+ for (JClassType type : types) {
+ List<JsniMethod> jsniMethods = jsniMethodMap.get(type);
+ if (jsniMethods != null) {
+ char[] annotation = genJsniMethodsAnnotation(jsniMethods, pretty);
+ int declStart = type.getDeclStart();
+ changes.add(new Replacement(declStart, declStart, annotation));
+ }
+ }
}
private void rewriteType(TreeLogger logger, char[] source,
@@ -375,18 +423,20 @@
branch.log(TreeLogger.SPAM, patched[i].getReadableDeclaration(), null);
}
- // Insert an initializer block immediately after the opening brace of the
- // class.
- char[] block = genInitializerBlock(loc, source, patched);
-
- // If this is a non-static inner class, actually put the initializer block
- // in the first enclosing static or top-level class instead.
- while (type.getEnclosingType() != null && !type.isStatic()) {
+ // Locate the nearest non-local type.
+ while (type.isLocalType()) {
type = type.getEnclosingType();
}
- int bodyStart = type.getBodyStart();
- changes.add(new Replacement(bodyStart, bodyStart, block));
+ // Add JsniMethod infos to the nearest non-inner type for each method.
+ List<JsniMethod> jsniMethods = jsniMethodMap.get(type);
+ if (jsniMethods == null) {
+ jsniMethods = new ArrayList<JsniMethod>();
+ jsniMethodMap.put(type, jsniMethods);
+ }
+ for (JMethod m : patched) {
+ jsniMethods.add(createJsniMethod(m, loc, source));
+ }
}
}
}
diff --git a/dev/core/src/com/google/gwt/dev/shell/JsniMethods.java b/dev/core/src/com/google/gwt/dev/shell/JsniMethods.java
new file mode 100644
index 0000000..e0613a6
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/shell/JsniMethods.java
@@ -0,0 +1,65 @@
+/*
+ * 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.shell;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Encodes all JSNI methods into a compiled hosted mode class file.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface JsniMethods {
+
+ /**
+ * Encodes a JSNI method into a compiled hosted mode class file.
+ */
+ @Target({})
+ public @interface JsniMethod {
+ /**
+ * Source file of the method.
+ */
+ String file();
+
+ /**
+ * Starting line number of the method.
+ */
+ int line();
+
+ /**
+ * The mangled method name (a jsni signature).
+ */
+ String name();
+
+ /**
+ * The parameter names.
+ */
+ String[] paramNames();
+
+ /**
+ * The script body.
+ */
+ String body();
+ }
+
+ /**
+ * The set of all methods.
+ */
+ JsniMethod[] value();
+}
diff --git a/dev/core/src/com/google/gwt/dev/shell/ModuleSpace.java b/dev/core/src/com/google/gwt/dev/shell/ModuleSpace.java
index c43267c..7af4f31 100644
--- a/dev/core/src/com/google/gwt/dev/shell/ModuleSpace.java
+++ b/dev/core/src/com/google/gwt/dev/shell/ModuleSpace.java
@@ -73,42 +73,6 @@
return threadLocalLogger.get();
}
- /**
- * Tricky one, this. Reaches over into this modules's JavaScriptHost class and
- * sets its static 'host' field to be the specified ModuleSpace instance
- * (which will either be this ModuleSpace or null).
- *
- * @param moduleSpace the ModuleSpace instance to store using
- * JavaScriptHost.setHost().
- * @see JavaScriptHost
- */
- private static void setJavaScriptHost(ModuleSpace moduleSpace, ClassLoader cl) {
- // Find the application's JavaScriptHost interface.
- //
- Throwable caught;
- try {
- final String jsHostClassName = JavaScriptHost.class.getName();
- Class<?> jsHostClass = Class.forName(jsHostClassName, true, cl);
- final Class<?>[] paramTypes = new Class[] {ShellJavaScriptHost.class};
- Method setHostMethod = jsHostClass.getMethod("setHost", paramTypes);
- setHostMethod.invoke(jsHostClass, new Object[] {moduleSpace});
- return;
- } catch (ClassNotFoundException e) {
- caught = e;
- } catch (SecurityException e) {
- caught = e;
- } catch (NoSuchMethodException e) {
- caught = e;
- } catch (IllegalArgumentException e) {
- caught = e;
- } catch (IllegalAccessException e) {
- caught = e;
- } catch (InvocationTargetException e) {
- caught = e.getTargetException();
- }
- throw new RuntimeException("Error initializing JavaScriptHost", caught);
- }
-
private final ModuleSpaceHost host;
private final Object key;
@@ -124,12 +88,8 @@
}
public void dispose() {
- // Tell the user-space JavaScript host object that we're done
- //
- clearJavaScriptHost();
-
- // Clear out the class loader's cache
- host.getClassLoader().clear();
+ // Clear our class loader.
+ getIsolatedClassLoader().clear();
}
public void exceptionCaught(Object exception) {
@@ -164,7 +124,7 @@
public String getModuleName() {
return moduleName;
}
-
+
public boolean invokeNativeBoolean(String name, Object jthis,
Class<?>[] types, Object[] args) throws Throwable {
JsValue result = invokeNative(name, jthis, types, args);
@@ -302,10 +262,6 @@
//
host.onModuleReady(this);
- // Tell the user-space JavaScript host object how to get back here.
- //
- setJavaScriptHost();
-
// Make sure we can resolve JSNI references to static Java names.
//
try {
@@ -511,13 +467,6 @@
}
}
- /**
- * Clear the module's JavaScriptHost 'host' field.
- */
- private void clearJavaScriptHost() {
- setJavaScriptHost(null, getIsolatedClassLoader());
- }
-
private String composeResultErrorMsgPrefix(String name, String typePhrase) {
return "Something other than " + typePhrase + " was returned from JSNI method '" + name + "'";
}
@@ -543,12 +492,4 @@
}
}
}
-
- /**
- * Set the module's JavaScriptHost 'host' field to this ModuleSpace instance.
- */
- private void setJavaScriptHost() {
- setJavaScriptHost(this, getIsolatedClassLoader());
- }
-
}
diff --git a/dev/core/src/com/google/gwt/dev/shell/ShellModuleSpaceHost.java b/dev/core/src/com/google/gwt/dev/shell/ShellModuleSpaceHost.java
index 109d96a..2175316 100644
--- a/dev/core/src/com/google/gwt/dev/shell/ShellModuleSpaceHost.java
+++ b/dev/core/src/com/google/gwt/dev/shell/ShellModuleSpaceHost.java
@@ -126,7 +126,8 @@
// accidentally 'escaping' its domain and loading classes from the system
// class loader (the one that loaded the shell itself).
//
- classLoader = new CompilingClassLoader(logger, compiler, typeOracle);
+ classLoader = new CompilingClassLoader(logger, compiler, typeOracle,
+ readySpace);
}
public String rebind(TreeLogger rebindLogger, String sourceTypeName)
diff --git a/dev/core/src/com/google/gwt/dev/util/Jsni.java b/dev/core/src/com/google/gwt/dev/util/Jsni.java
index 1b8ed8d..655bb13 100644
--- a/dev/core/src/com/google/gwt/dev/util/Jsni.java
+++ b/dev/core/src/com/google/gwt/dev/util/Jsni.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2007 Google Inc.
+ * 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
@@ -127,7 +127,8 @@
public static final String JSNI_BLOCK_START = "/*-{";
/**
- * Generates the code to wrap a set of parameters as an object array.
+ * Generates the code to wrap a set of parameters as an object array. In Java
+ * 1.5 we can take advantage of autoboxing to not have to wrap primitives.
*/
public static String buildArgList(JMethod method) {
StringBuffer sb = new StringBuffer();
@@ -135,40 +136,8 @@
JParameter[] params = method.getParameters();
for (int i = 0; i < params.length; ++i) {
- if (i > 0) {
- sb.append(", ");
- }
-
- JType type = params[i].getType();
- String typeName = type.getQualifiedSourceName();
-
- if ((type.isArray() == null) && (type.isPrimitive() != null)) {
- // Primitive types have to be wrapped for reflection invoke().
- //
- if (typeName.equals("boolean")) {
- sb.append("new Boolean(" + params[i].getName() + ")");
- } else if (typeName.equals("byte")) {
- sb.append("new Byte(" + params[i].getName() + ")");
- } else if (typeName.equals("char")) {
- sb.append("new Character(" + params[i].getName() + ")");
- } else if (typeName.equals("short")) {
- sb.append("new Short(" + params[i].getName() + ")");
- } else if (typeName.equals("int")) {
- sb.append("new Integer(" + params[i].getName() + ")");
- } else if (typeName.equals("float")) {
- sb.append("new Float(" + params[i].getName() + ")");
- } else if (typeName.equals("double")) {
- sb.append("new Double(" + params[i].getName() + ")");
- } else if (typeName.equals("long")) {
- sb.append("new Long(" + params[i].getName() + ")");
- } else {
- throw new RuntimeException("Unexpected primitive parameter type");
- }
- } else {
- // Reference types pass through as themselves.
- //
- sb.append(params[i].getName());
- }
+ sb.append(params[i].getName());
+ sb.append(", ");
}
sb.append("}");
@@ -186,14 +155,10 @@
JParameter[] params = method.getParameters();
for (int i = 0; i < params.length; ++i) {
- if (i > 0) {
- sb.append(", ");
- }
-
JType type = params[i].getType();
String typeName = type.getErasedType().getQualifiedSourceName();
sb.append(typeName);
- sb.append(".class");
+ sb.append(".class, ");
}
sb.append("}");
@@ -207,7 +172,7 @@
switch (buf[start]) {
case '\r':
++total;
- // if the next character is a linefeed, eat it too
+ // if the next character is a line feed, eat it too
if (start + 1 < end && buf[start + 1] == '\n') {
++start;
}
diff --git a/user/test/com/google/gwt/dev/jjs/test/HostedTest.java b/user/test/com/google/gwt/dev/jjs/test/HostedTest.java
index 56af8ca..eb3f89e 100644
--- a/user/test/com/google/gwt/dev/jjs/test/HostedTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/HostedTest.java
@@ -80,7 +80,20 @@
}
private static enum TestEnum {
- VAL1, VAL2, VAL3
+ VAL1, VAL2, VAL3() {
+ @Override
+ public native String foo() /*-{
+ return "VAL3-foo";
+ }-*/;
+ };
+
+ public static native String sFoo() /*-{
+ return "sFoo";
+ }-*/;
+
+ public native String foo() /*-{
+ return "foo";
+ }-*/;
}
static String sFoo(String s) {
@@ -272,6 +285,13 @@
assertEquals(TestEnum.VAL2.name(), name);
}
+ public void testEnumJsni() {
+ assertEquals("sFoo", TestEnum.sFoo());
+ assertEquals("sFoo", TestEnum.VAL1.sFoo());
+ assertEquals("foo", TestEnum.VAL1.foo());
+ assertEquals("VAL3-foo", TestEnum.VAL3.foo());
+ }
+
public void testFloat() {
storeFloat(Float.MIN_VALUE);
float f = getFloat();