1 16 package com.google.gwt.dev.shell; 17 18 import com.google.gwt.core.ext.TreeLogger; 19 import com.google.gwt.core.ext.UnableToCompleteException; 20 21 import java.lang.reflect.Constructor ; 22 import java.lang.reflect.InvocationTargetException ; 23 import java.lang.reflect.Method ; 24 import java.lang.reflect.Modifier ; 25 26 31 public abstract class ModuleSpace implements ShellJavaScriptHost { 32 33 private static ThreadLocal sCaughtJavaExceptionObject = new ThreadLocal (); 34 35 private static ThreadLocal sLastThrownJavaException = new ThreadLocal (); 36 37 private static ThreadLocal sThrownJavaExceptionObject = new ThreadLocal (); 38 39 42 private static ThreadLocal threadLocalLogger = new ThreadLocal (); 43 44 public static void setThrownJavaException(Throwable t) { 45 Throwable was = (Throwable ) sLastThrownJavaException.get(); 46 if (was != t) { 47 getLogger().log(TreeLogger.WARN, "Exception thrown into JavaScript", t); 49 sLastThrownJavaException.set(t); 50 } 51 sThrownJavaExceptionObject.set(t); 52 } 53 54 protected static RuntimeException createJavaScriptException(ClassLoader cl, 55 String name, String desc) { 56 Exception caught; 57 try { 58 Class javaScriptExceptionClass = Class.forName( 59 "com.google.gwt.core.client.JavaScriptException", true, cl); 60 Class string = String .class; 61 Constructor ctor = javaScriptExceptionClass.getDeclaredConstructor(new Class [] { 62 string, string}); 63 return (RuntimeException ) ctor.newInstance(new Object [] {name, desc}); 64 } catch (InstantiationException e) { 65 caught = e; 66 } catch (IllegalAccessException e) { 67 caught = e; 68 } catch (SecurityException e) { 69 caught = e; 70 } catch (ClassNotFoundException e) { 71 caught = e; 72 } catch (NoSuchMethodException e) { 73 caught = e; 74 } catch (IllegalArgumentException e) { 75 caught = e; 76 } catch (InvocationTargetException e) { 77 caught = e; 78 } 79 throw new RuntimeException ("Error creating JavaScriptException", caught); 80 } 81 82 protected static TreeLogger getLogger() { 83 return (TreeLogger) threadLocalLogger.get(); 84 } 85 86 95 private static void setJavaScriptHost(ModuleSpace moduleSpace, ClassLoader cl) { 96 Throwable caught; 99 try { 100 final String jsHostClassName = JavaScriptHost.class.getName(); 101 Class jsHostClass = Class.forName(jsHostClassName, true, cl); 102 final Class [] paramTypes = new Class [] {ShellJavaScriptHost.class}; 103 Method setHostMethod = jsHostClass.getMethod("setHost", paramTypes); 104 setHostMethod.invoke(jsHostClass, new Object [] {moduleSpace}); 105 return; 106 } catch (ClassNotFoundException e) { 107 caught = e; 108 } catch (SecurityException e) { 109 caught = e; 110 } catch (NoSuchMethodException e) { 111 caught = e; 112 } catch (IllegalArgumentException e) { 113 caught = e; 114 } catch (IllegalAccessException e) { 115 caught = e; 116 } catch (InvocationTargetException e) { 117 caught = e.getTargetException(); 118 } 119 throw new RuntimeException ("Error initializing JavaScriptHost", caught); 120 } 121 122 private final ModuleSpaceHost host; 123 124 private final Object key; 125 126 private final String moduleName; 127 128 protected ModuleSpace(ModuleSpaceHost host, String moduleName, Object key) { 129 this.host = host; 130 this.moduleName = moduleName; 131 this.key = key; 132 TreeLogger hostLogger = host.getLogger(); 133 threadLocalLogger.set(hostLogger); 134 } 135 136 public void dispose() { 137 clearJavaScriptHost(); 140 141 sLastThrownJavaException.set(null); 143 144 host.getClassLoader().clear(); 146 } 147 148 public void exceptionCaught(int number, String name, String message) { 149 Throwable thrown = (Throwable ) sThrownJavaExceptionObject.get(); 150 151 if (thrown != null) { 152 if (isExceptionSame(thrown, number, name, message)) { 154 sCaughtJavaExceptionObject.set(thrown); 155 sThrownJavaExceptionObject.set(null); 156 return; 157 } 158 } 159 160 sCaughtJavaExceptionObject.set(createJavaScriptException( 161 getIsolatedClassLoader(), name, message)); 162 } 163 164 169 public Object getKey() { 170 return key; 171 } 172 173 178 public String getModuleName() { 179 return moduleName; 180 } 181 182 public boolean invokeNativeBoolean(String name, Object jthis, Class [] types, 183 Object [] args) throws Throwable { 184 JsValue result = invokeNative(name, jthis, types, args); 185 Boolean value = (Boolean ) JsValueGlue.get(result, Boolean .class, 186 "invokeNativeBoolean(" + name + ")"); 187 return value.booleanValue(); 188 } 189 190 public byte invokeNativeByte(String name, Object jthis, Class [] types, 191 Object [] args) throws Throwable { 192 JsValue result = invokeNative(name, jthis, types, args); 193 Byte value = (Byte ) JsValueGlue.get(result, Byte .class, "invokeNativeByte(" 194 + name + ")"); 195 return value.byteValue(); 196 } 197 198 public char invokeNativeChar(String name, Object jthis, Class [] types, 199 Object [] args) throws Throwable { 200 JsValue result = invokeNative(name, jthis, types, args); 201 Character value = (Character ) JsValueGlue.get(result, Character .class, 202 "invokeNativeCharacter(" + name + ")"); 203 return value.charValue(); 204 } 205 206 public double invokeNativeDouble(String name, Object jthis, Class [] types, 207 Object [] args) throws Throwable { 208 JsValue result = invokeNative(name, jthis, types, args); 209 Double value = (Double ) JsValueGlue.get(result, Double .class, 210 "invokeNativeDouble(" + name + ")"); 211 return value.doubleValue(); 212 } 213 214 public float invokeNativeFloat(String name, Object jthis, Class [] types, 215 Object [] args) throws Throwable { 216 JsValue result = invokeNative(name, jthis, types, args); 217 Float value = (Float ) JsValueGlue.get(result, Float .class, 218 "invokeNativeFloat(" + name + ")"); 219 return value.floatValue(); 220 } 221 222 public Object invokeNativeHandle(String name, Object jthis, Class returnType, 223 Class [] types, Object [] args) throws Throwable { 224 225 JsValue result = invokeNative(name, jthis, types, args); 226 return JsValueGlue.get(result, returnType, "invokeNativeHandle(" + name 227 + ")"); 228 } 229 230 public int invokeNativeInt(String name, Object jthis, Class [] types, 231 Object [] args) throws Throwable { 232 JsValue result = invokeNative(name, jthis, types, args); 233 Integer value = (Integer ) JsValueGlue.get(result, Integer .class, 234 "invokeNativeInteger(" + name + ")"); 235 return value.intValue(); 236 } 237 238 public long invokeNativeLong(String name, Object jthis, Class [] types, 239 Object [] args) throws Throwable { 240 JsValue result = invokeNative(name, jthis, types, args); 241 Long value = (Long ) JsValueGlue.get(result, Long .class, "invokeNativeLong(" 242 + name + ")"); 243 return value.longValue(); 244 } 245 246 public Object invokeNativeObject(String name, Object jthis, Class [] types, 247 Object [] args) throws Throwable { 248 JsValue result = invokeNative(name, jthis, types, args); 249 return JsValueGlue.get(result, Object .class, "invokeNativeObject(" + name 250 + ")"); 251 } 252 253 public short invokeNativeShort(String name, Object jthis, Class [] types, 254 Object [] args) throws Throwable { 255 JsValue result = invokeNative(name, jthis, types, args); 256 Short value = (Short ) JsValueGlue.get(result, Short .class, 257 "invokeNativeShort(" + name + ")"); 258 return value.shortValue(); 259 } 260 261 public String invokeNativeString(String name, Object jthis, Class [] types, 262 Object [] args) throws Throwable { 263 JsValue result = invokeNative(name, jthis, types, args); 264 return (String ) JsValueGlue.get(result, String .class, "invokeNativeString(" 265 + name + ")"); 266 } 267 268 public void invokeNativeVoid(String name, Object jthis, Class [] types, 269 Object [] args) throws Throwable { 270 JsValue result = invokeNative(name, jthis, types, args); 271 if (!result.isUndefined()) { 272 getLogger().log( 273 TreeLogger.WARN, 274 "JSNI method '" + name + "' returned a value of type " 275 + result.getTypeString() + "; should not have returned a value", 276 null); 277 } 278 } 279 280 283 public void log(String message, Throwable e) { 284 TreeLogger logger = host.getLogger(); 285 TreeLogger.Type type = TreeLogger.INFO; 286 if (e != null) { 287 type = TreeLogger.ERROR; 288 } 289 logger.log(type, message, e); 290 } 291 292 295 public final void onLoad(TreeLogger logger) throws UnableToCompleteException { 296 host.onModuleReady(this); 299 300 setJavaScriptHost(); 303 304 try { 307 Object staticDispatch = getStaticDispatcher(); 308 createNative("initializeStaticDispatcher", 0, "__defineStatic", 309 new String [] {"__arg0"}, "window.__static = __arg0;"); 310 invokeNativeVoid("__defineStatic", null, new Class [] {Object .class}, 311 new Object [] {staticDispatch}); 312 } catch (Throwable e) { 313 logger.log(TreeLogger.ERROR, "Unable to initialize static dispatcher", e); 314 throw new UnableToCompleteException(); 315 } 316 317 String entryPointTypeName = null; 320 try { 321 String [] entryPoints = host.getEntryPointTypeNames(); 322 if (entryPoints.length > 0) { 323 for (int i = 0; i < entryPoints.length; i++) { 324 entryPointTypeName = entryPoints[i]; 325 Class clazz = loadClassFromSourceName(entryPointTypeName); 326 Method onModuleLoad = null; 327 try { 328 onModuleLoad = clazz.getMethod("onModuleLoad", null); 329 if (!Modifier.isStatic(onModuleLoad.getModifiers())) { 330 onModuleLoad = null; 332 } 333 } catch (NoSuchMethodException e) { 334 } 336 Object module = null; 337 if (onModuleLoad == null) { 338 module = rebindAndCreate(entryPointTypeName); 339 onModuleLoad = module.getClass().getMethod("onModuleLoad", null); 340 } 341 onModuleLoad.setAccessible(true); 342 onModuleLoad.invoke(module, null); 343 } 344 } else { 345 logger.log( 346 TreeLogger.WARN, 347 "The module has no entry points defined, so onModuleLoad() will never be called", 348 null); 349 } 350 } catch (Throwable e) { 351 Throwable caught = e; 352 353 if (e instanceof InvocationTargetException ) { 354 caught = ((InvocationTargetException ) e).getTargetException(); 355 } 356 357 if (caught instanceof ExceptionInInitializerError ) { 358 caught = ((ExceptionInInitializerError ) caught).getException(); 359 } 360 361 String unableToLoadMessage = "Unable to load module entry point class " 362 + entryPointTypeName; 363 if (caught != null) { 364 unableToLoadMessage += " (see associated exception for details)"; 365 } 366 logger.log(TreeLogger.ERROR, unableToLoadMessage, caught); 367 throw new UnableToCompleteException(); 368 } 369 } 370 371 public Object rebindAndCreate(String requestedClassName) 372 throws UnableToCompleteException { 373 Throwable caught = null; 374 String msg = null; 375 String resultName = null; 376 try { 377 String sourceName = requestedClassName.replace('$', '.'); 380 resultName = rebind(sourceName); 381 Class resolvedClass = loadClassFromSourceName(resultName); 382 if (Modifier.isAbstract(resolvedClass.getModifiers())) { 383 msg = "Deferred binding result type '" + resultName 384 + "' should not be abstract"; 385 } else { 386 Constructor ctor = resolvedClass.getDeclaredConstructor(null); 387 ctor.setAccessible(true); 388 return ctor.newInstance(null); 389 } 390 } catch (ClassNotFoundException e) { 391 msg = "Could not load deferred binding result type '" + resultName + "'"; 392 caught = e; 393 } catch (InstantiationException e) { 394 caught = e; 395 } catch (IllegalAccessException e) { 396 caught = e; 397 } catch (ExceptionInInitializerError e) { 398 caught = e.getException(); 399 } catch (NoSuchMethodException e) { 400 msg = "Rebind result '" + resultName 401 + "' has no default (zero argument) constructors."; 402 caught = e; 403 } catch (InvocationTargetException e) { 404 caught = e.getTargetException(); 405 } 406 407 if (msg == null) { 411 msg = "Failed to create an instance of '" + requestedClassName 412 + "' via deferred binding "; 413 } 414 host.getLogger().log(TreeLogger.ERROR, msg, caught); 415 throw new UnableToCompleteException(); 416 } 417 418 protected String createNativeMethodInjector(String jsniSignature, 419 String [] paramNames, String js) { 420 String newScript = "window[\"" + jsniSignature + "\"] = function("; 421 422 for (int i = 0; i < paramNames.length; ++i) { 423 if (i > 0) { 424 newScript += ", "; 425 } 426 427 newScript += paramNames[i]; 428 } 429 430 newScript += ") { " + js + " };\n"; 431 return newScript; 432 } 433 434 443 protected abstract JsValue doInvoke(String name, Object jthis, Class [] types, 444 Object [] args) throws Throwable ; 445 446 protected CompilingClassLoader getIsolatedClassLoader() { 447 return host.getClassLoader(); 448 } 449 450 453 protected abstract Object getStaticDispatcher(); 454 455 464 protected final JsValue invokeNative(String name, Object jthis, 465 Class [] types, Object [] args) throws Throwable { 466 JsValue.mainThreadCleanup(); 468 JsValue result = doInvoke(name, jthis, types, args); 469 Throwable thrown = (Throwable ) sCaughtJavaExceptionObject.get(); 471 if (thrown == null) { 472 return result; 473 } 474 sCaughtJavaExceptionObject.set(null); 475 476 481 thrown.fillInStackTrace(); 482 throw thrown; 483 } 484 485 protected boolean isExceptionSame(Throwable original, int number, 486 String name, String message) { 487 return (name == null && message == null); 490 } 491 492 protected String rebind(String sourceName) throws UnableToCompleteException { 493 try { 494 String result = host.rebind(host.getLogger(), sourceName); 495 if (result != null) { 496 return result; 497 } else { 498 return sourceName; 499 } 500 } catch (UnableToCompleteException e) { 501 String msg = "Deferred binding failed for '" + sourceName 502 + "'; expect subsequent failures"; 503 host.getLogger().log(TreeLogger.ERROR, msg, e); 504 throw new UnableToCompleteException(); 505 } 506 } 507 508 511 private void clearJavaScriptHost() { 512 setJavaScriptHost(null, getIsolatedClassLoader()); 513 } 514 515 518 private Class loadClassFromSourceName(String sourceName) 519 throws ClassNotFoundException { 520 String toTry = sourceName; 521 while (true) { 522 try { 523 return Class.forName(toTry, true, getIsolatedClassLoader()); 524 } catch (ClassNotFoundException e) { 525 int i = toTry.lastIndexOf('.'); 528 if (i == -1) { 529 throw e; 530 } 531 532 toTry = toTry.substring(0, i) + "$" + toTry.substring(i + 1); 533 } 534 } 535 } 536 537 540 private void setJavaScriptHost() { 541 setJavaScriptHost(this, getIsolatedClassLoader()); 542 } 543 544 } 545 | Popular Tags |