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 import com.google.gwt.core.ext.typeinfo.JClassType; 21 import com.google.gwt.core.ext.typeinfo.TypeOracle; 22 import com.google.gwt.dev.jdt.ByteCodeCompiler; 23 import com.google.gwt.dev.jdt.CacheManager; 24 import com.google.gwt.util.tools.Utility; 25 26 import java.io.IOException ; 27 import java.io.InputStream ; 28 import java.lang.reflect.Method ; 29 import java.net.URL ; 30 import java.util.ArrayList ; 31 import java.util.HashMap ; 32 import java.util.Map ; 33 34 41 public final class CompilingClassLoader extends ClassLoader { 42 43 47 private final class DispatchClassInfoOracle { 48 49 52 private final ArrayList classIdToClassInfo = new ArrayList (); 53 54 57 private final Map classNameToClassInfo = new HashMap (); 58 59 62 public synchronized void clear() { 63 classIdToClassInfo.clear(); 64 classNameToClassInfo.clear(); 65 } 66 67 73 public synchronized DispatchClassInfo getClassInfoByDispId(int dispId) { 74 int classId = extractClassIdFromDispId(dispId); 75 76 return (DispatchClassInfo) classIdToClassInfo.get(classId); 77 } 78 79 86 public synchronized int getDispId(String jsniMemberRef) { 87 93 if (jsniMemberRef.equals("toString")) { 94 jsniMemberRef = "@java.lang.Object::toString()"; 95 } 96 97 int endClassName = jsniMemberRef.indexOf("::"); 100 if (endClassName == -1 || jsniMemberRef.length() < 1 101 || jsniMemberRef.charAt(0) != '@') { 102 logger.log(TreeLogger.WARN, "Malformed JSNI reference '" 103 + jsniMemberRef + "'; expect subsequent failures", 104 new NoSuchFieldError (jsniMemberRef)); 105 return -1; 106 } 107 108 String className = jsniMemberRef.substring(1, endClassName); 109 110 DispatchClassInfo dispClassInfo = getClassInfoFromClassName(className); 112 if (dispClassInfo != null) { 113 String memberName = jsniMemberRef.substring(endClassName + 2); 114 int memberId = dispClassInfo.getMemberId(memberName); 115 116 return synthesizeDispId(dispClassInfo.getClassId(), memberId); 117 } 118 119 logger.log(TreeLogger.WARN, "Class '" + className 120 + "' in JSNI reference '" + jsniMemberRef 121 + "' could not be found; expect subsequent failures", 122 new ClassNotFoundException (className)); 123 return -1; 124 } 125 126 132 private int extractClassIdFromDispId(int dispId) { 133 return (dispId >> 16) & 0xffff; 134 } 135 136 144 private Class getClassFromBinaryName(String binaryClassName) { 145 try { 146 return Class.forName(binaryClassName, true, CompilingClassLoader.this); 147 } catch (ClassNotFoundException e) { 148 return null; 149 } 150 } 151 152 159 private Class getClassFromBinaryOrSourceName(String className) { 160 JClassType type = typeOracle.findType(className.replace('$', '.')); 162 if (type != null) { 163 String jniSig = type.getJNISignature(); 165 jniSig = jniSig.substring(1, jniSig.length() - 1); 166 className = jniSig.replace('/', '.'); 167 } 168 return getClassFromBinaryName(className); 169 } 170 171 182 private DispatchClassInfo getClassInfoFromClassName(String className) { 183 184 DispatchClassInfo dispClassInfo = (DispatchClassInfo) classNameToClassInfo.get(className); 185 if (dispClassInfo != null) { 186 return dispClassInfo; 188 } 189 190 Class cls = getClassFromBinaryOrSourceName(className); 191 if (cls == null) { 192 196 return null; 197 } 198 199 if (dispClassInfo == null) { 200 204 int classId = classIdToClassInfo.size(); 205 206 dispClassInfo = new DispatchClassInfo(cls, classId); 207 classIdToClassInfo.add(dispClassInfo); 208 } 209 210 214 classNameToClassInfo.put(className, dispClassInfo); 215 216 return dispClassInfo; 217 } 218 219 226 private int synthesizeDispId(int classId, int memberId) { 227 return (classId << 16) | memberId; 228 } 229 } 230 231 private final ByteCodeCompiler compiler; 232 233 private final DispatchClassInfoOracle dispClassInfoOracle = new DispatchClassInfoOracle(); 234 235 private final TreeLogger logger; 236 237 private final Map methodToDispatch = new HashMap (); 238 239 private final TypeOracle typeOracle; 240 241 public CompilingClassLoader(TreeLogger logger, ByteCodeCompiler compiler, 242 TypeOracle typeOracle) throws UnableToCompleteException { 243 super(null); 244 this.logger = logger; 245 this.compiler = compiler; 246 this.typeOracle = typeOracle; 247 248 for (int i = 0; i < CacheManager.BOOTSTRAP_CLASSES.length; i++) { 256 Class clazz = CacheManager.BOOTSTRAP_CLASSES[i]; 257 String className = clazz.getName(); 258 try { 259 String path = clazz.getName().replace('.', '/').concat(".class"); 260 ClassLoader cl = Thread.currentThread().getContextClassLoader(); 261 URL url = cl.getResource(path); 262 if (url != null) { 263 byte classBytes[] = getClassBytesFromStream(url.openStream()); 264 String loc = url.toExternalForm(); 265 compiler.putClassBytes(logger, className, classBytes, loc); 266 } else { 267 logger.log(TreeLogger.ERROR, 268 "Could not find required bootstrap class '" + className 269 + "' in the classpath", null); 270 throw new UnableToCompleteException(); 271 } 272 } catch (IOException e) { 273 logger.log(TreeLogger.ERROR, "Error reading class bytes for " 274 + className, e); 275 throw new UnableToCompleteException(); 276 } 277 } 278 compiler.removeStaleByteCode(logger); 279 } 280 281 288 public DispatchClassInfo getClassInfoByDispId(int dispId) { 289 return dispClassInfoOracle.getClassInfoByDispId(dispId); 290 } 291 292 298 public int getDispId(String jsniMemberRef) { 299 return dispClassInfoOracle.getDispId(jsniMemberRef); 300 } 301 302 public Object getMethodDispatch(Method method) { 303 synchronized (methodToDispatch) { 304 return methodToDispatch.get(method); 305 } 306 } 307 308 public void putMethodDispatch(Method method, Object methodDispatch) { 309 synchronized (methodToDispatch) { 310 methodToDispatch.put(method, methodDispatch); 311 } 312 } 313 314 protected synchronized Class findClass(String className) 315 throws ClassNotFoundException { 316 if (className == null) { 317 throw new ClassNotFoundException ("null class name", 318 new NullPointerException ()); 319 } 320 321 if (isInStandardJavaPackage(className)) { 324 throw new ClassNotFoundException (className); 326 } 327 328 if (className.equals(ShellJavaScriptHost.class.getName())) { 332 return ShellJavaScriptHost.class; 333 } 334 335 try { 339 byte[] classBytes = compiler.getClassBytes(logger, className); 340 return defineClass(className, classBytes, 0, classBytes.length); 341 } catch (UnableToCompleteException e) { 342 throw new ClassNotFoundException (className); 343 } 344 } 345 346 void clear() { 347 dispClassInfoOracle.clear(); 348 349 synchronized (methodToDispatch) { 350 methodToDispatch.clear(); 351 } 352 } 353 354 private byte[] getClassBytesFromStream(InputStream is) throws IOException { 355 try { 356 byte classBytes[] = new byte[is.available()]; 357 int read = 0; 358 while (read < classBytes.length) { 359 read += is.read(classBytes, read, classBytes.length - read); 360 } 361 return classBytes; 362 } finally { 363 Utility.close(is); 364 } 365 } 366 367 private boolean isInStandardJavaPackage(String className) { 368 if (className.startsWith("java.")) { 369 return true; 370 } 371 372 if (className.startsWith("javax.")) { 373 return true; 374 } 375 376 return false; 377 } 378 } 379 | Popular Tags |