| CODENOTIFIER | HelpYou are not signed inSign in |
Project: Google Web Toolkit
Revision: 3622
Author: scottb@google.com
Date: 04 Sep 2008 18:38:41
Changes:Adds Emma integration for client classes running in hosted mode. Here are the key parts:
1) Bridge Emma's RT class. This is necessary to allow classes living in the CCL to access the outside world, in this case the Emma coverage data hook points.
2) Allow instrumented classes to load from disk. This is necessary to pick up the modifications to pre-instrumented classes, which is the most common use case with Emma. This is the only case we currently support, although tobyr has some indication that Java class transformers might also work. We only conditionally read class files off disk because it's slower; you have to stat the class and java files to compare dates, and then actually read the bytes, whereas normally the compiled bytes are already in RAM. Instant hosted mode should level the playing field by always preferring disk.
Review by: bobv
Files:| ... | ...@@ -0,0 +1,88 @@ | |
| 1 | /* | |
| 2 | * Copyright 2008 Google Inc. | |
| 3 | * | |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not | |
| 5 | * use this file except in compliance with the License. You may obtain a copy of | |
| 6 | * the License at | |
| 7 | * | |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
| 9 | * | |
| 10 | * Unless required by applicable law or agreed to in writing, software | |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |
| 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |
| 13 | * License for the specific language governing permissions and limitations under | |
| 14 | * the License. | |
| 15 | */ | |
| 16 | package com.google.gwt.dev.shell; | |
| 17 | ||
| 18 | import com.google.gwt.dev.util.Util; | |
| 19 | ||
| 20 | import java.io.IOException; | |
| 21 | import java.net.URL; | |
| 22 | import java.net.URLConnection; | |
| 23 | ||
| 24 | /** | |
| 25 | * Provides various strategies for emma integration based on runtime detection. | |
| 26 | */ | |
| 27 | abstract class EmmaStrategy { | |
| 28 | ||
| 29 | private static class NoEmmaStrategy extends EmmaStrategy { | |
| 30 | public byte[] getEmmaClassBytes(byte[] classBytes, String slashedName, | |
| 31 | long unitLastModified) { | |
| 32 | return classBytes; | |
| 33 | } | |
| 34 | } | |
| 35 | ||
| 36 | private static class PreinstrumentedEmmaStrategy extends EmmaStrategy { | |
| 37 | public byte[] getEmmaClassBytes(byte[] classBytes, String slashedName, | |
| 38 | long unitLastModified) { | |
| 39 | // Check for an existing class on the classpath. | |
| 40 | URL url = Thread.currentThread().getContextClassLoader().getResource( | |
| 41 | slashedName + ".class"); | |
| 42 | if (url != null) { | |
| 43 | // We found it on the class path. | |
| 44 | try { | |
| 45 | URLConnection conn = url.openConnection(); | |
| 46 | if (conn.getLastModified() >= unitLastModified) { | |
| 47 | // It's as new as the source file, let's use it. | |
| 48 | byte[] result = Util.readURLConnectionAsBytes(conn); | |
| 49 | if (result != null) { | |
| 50 | return result; | |
| 51 | } | |
| 52 | // Fall through. | |
| 53 | } | |
| 54 | // Fall through. | |
| 55 | } catch (IOException ignored) { | |
| 56 | // Fall through. | |
| 57 | } | |
| 58 | } | |
| 59 | ||
| 60 | // Just return what we got. | |
| 61 | return classBytes; | |
| 62 | } | |
| 63 | } | |
| 64 | ||
| 65 | /** | |
| 66 | * Classname for Emma's RT, to enable bridging. | |
| 67 | */ | |
| 68 | public static final String EMMA_RT_CLASSNAME = "com.vladium.emma.rt.RT"; | |
| 69 | ||
| 70 | /** | |
| 71 | * Gets the emma classloading strategy. | |
| 72 | */ | |
| 73 | public static EmmaStrategy get(boolean emmaIsAvailable) { | |
| 74 | /* | |
| 75 | * Theoretically, emmarun could be using an instrumented ClassLoader, but in | |
| 76 | * practice we haven't been able to make GWT run at all in this case. | |
| 77 | */ | |
| 78 | if (!emmaIsAvailable) { | |
| 79 | return new NoEmmaStrategy(); | |
| 80 | } else { | |
| 81 | return new PreinstrumentedEmmaStrategy(); | |
| 82 | } | |
| 83 | } | |
| 84 | ||
| 85 | public abstract byte[] getEmmaClassBytes(byte[] classBytes, | |
| 86 | String slashedName, long unitLastModified); | |
| 87 | ||
| 88 | } |
| ... | ...@@ -599,22 +599,12 @@ | |
| 599 | 599 | * @return null if the file could not be read |
| 600 | 600 | */ |
| 601 | 601 | public static byte[] readURLAsBytes(URL url) { |
| 602 | // ENH: add a weak cache that has an additional check against the file date | |
| 603 | InputStream input = null; | |
| 604 | 602 | try { |
| 605 | URLConnection connection = url.openConnection(); | |
| 606 | connection.setUseCaches(false); | |
| 607 | input = connection.getInputStream(); | |
| 608 | int contentLength = connection.getContentLength(); | |
| 609 | if (contentLength < 0) { | |
| 610 | return null; | |
| 611 | } | |
| 612 | ||
| 613 | return readBytesFromInputStream(input, contentLength); | |
| 603 | URLConnection conn = url.openConnection(); | |
| 604 | conn.setUseCaches(false); | |
| 605 | return readURLConnectionAsBytes(conn); | |
| 614 | 606 | } catch (IOException e) { |
| 615 | 607 | return null; |
| 616 | } finally { | |
| 617 | Utility.close(input); | |
| 618 | 608 | } |
| 619 | 609 | } |
| 620 | 610 | |
| ... | ...@@ -622,7 +612,6 @@ | |
| 622 | 612 | * @return null if the file could not be read |
| 623 | 613 | */ |
| 624 | 614 | public static char[] readURLAsChars(URL url) { |
| 625 | // ENH: add a weak cache that has an additional check against the file date | |
| 626 | 615 | byte[] bytes = readURLAsBytes(url); |
| 627 | 616 | if (bytes != null) { |
| 628 | 617 | return toString(bytes, DEFAULT_ENCODING).toCharArray(); |
| ... | ...@@ -631,6 +620,24 @@ | |
| 631 | 620 | return null; |
| 632 | 621 | } |
| 633 | 622 | |
| 623 | public static byte[] readURLConnectionAsBytes(URLConnection connection) { | |
| 624 | // ENH: add a weak cache that has an additional check against the file date | |
| 625 | InputStream input = null; | |
| 626 | try { | |
| 627 | input = connection.getInputStream(); | |
| 628 | int contentLength = connection.getContentLength(); | |
| 629 | if (contentLength < 0) { | |
| 630 | return null; | |
| 631 | } | |
| 632 | ||
| 633 | return readBytesFromInputStream(input, contentLength); | |
| 634 | } catch (IOException e) { | |
| 635 | return null; | |
| 636 | } finally { | |
| 637 | Utility.close(input); | |
| 638 | } | |
| 639 | } | |
| 640 | ||
| 634 | 641 | /** |
| 635 | 642 | * Deletes a file or recursively deletes a directory. |
| 636 | 643 | * |
| ... | ...@@ -354,10 +354,25 @@ | |
| 354 | 354 | */ |
| 355 | 355 | private static byte[] javaScriptHostBytes; |
| 356 | 356 | |
| 357 | private static EmmaStrategy emmaStrategy; | |
| 358 | ||
| 357 | 359 | static { |
| 358 | 360 | for (Class<?> c : BRIDGE_CLASSES) { |
| 359 | 361 | BRIDGE_CLASS_NAMES.put(c.getName(), c); |
| 360 | 362 | } |
| 363 | /* | |
| 364 | * Specific support for bridging to Emma since the user classloader is | |
| 365 | * generally completely isolated. | |
| 366 | */ | |
| 367 | boolean emmaIsAvailable = false; | |
| 368 | try { | |
| 369 | Class<?> emmaBridge = Class.forName(EmmaStrategy.EMMA_RT_CLASSNAME, | |
| 370 | false, Thread.currentThread().getContextClassLoader()); | |
| 371 | BRIDGE_CLASS_NAMES.put(EmmaStrategy.EMMA_RT_CLASSNAME, emmaBridge); | |
| 372 | emmaIsAvailable = true; | |
| 373 | } catch (ClassNotFoundException ignored) { | |
| 374 | } | |
| 375 | emmaStrategy = EmmaStrategy.get(emmaIsAvailable); | |
| 361 | 376 | } |
| 362 | 377 | |
| 363 | 378 | private static void classDump(String name, byte[] bytes) { |
| ... | ...@@ -637,6 +652,8 @@ | |
| 637 | 652 | injectJsniFor(compiledClass); |
| 638 | 653 | |
| 639 | 654 | byte[] classBytes = compiledClass.getBytes(); |
| 655 | classBytes = emmaStrategy.getEmmaClassBytes(classBytes, lookupClassName, | |
| 656 | compiledClass.getUnit().getLastModified()); | |
| 640 | 657 | if (classRewriter != null) { |
| 641 | 658 | byte[] newBytes = classRewriter.rewrite(className, classBytes); |
| 642 | 659 | if (CLASS_DUMP) { |