#summary This document describes the steps taken to support Emma in GWT. == Introduction == This document describes the design of how GWT supports Emma, a widely used code coverage tool for Java. == Background == As of Jan 2009, in its normal operation, GWT does not directly read class files from the disk. It only reads java source files. It internally uses the jdt compiler to compile the java source to java bytecode and obtain additional meta-data like the type hierarchy tree. To be able to interact with other Emma tools, GWT uses Emma in the offline mode, i.e., it uses classes instrumented by Emma over the classes it obtains by compiling the java source files. These instrumented classes are typically present on the disk and are typically generated using a compiler other than jdt. Commonly, this compiler is javac. As one can imagine, the two compilers do not always produce "compatible" class files. The rest of this document explains and addresses the specific incompatibilities this approach results in. As an aside, GWT currently uses a heuristic to determine if it should operate in Emma mode or not -- it operates in Emma mode if Emma is available on the classpath. This heuristic is present in the {{{CompilingClassLoader}}} (CCL), which handles the loading of classes. == Problem 1: Loading classes whose names differ between the two compilers == Whenever there is a switch statement on an enum in a class like the code below, javac produces a synthetic class like !EnumTest$1 whereas jdt does not generate any such class. Jdt just produces a switch table for this code. {{{ class EnumTest { .... enum Letter { A, B, C, } Letter letter = Letter.A; switch (letter) { case A: case B: log (letter); break; default: break; } ... } }}} Besides synthetic classes, as in the example above, jdt and javac might assign different names to anonymous classes. Typically, both synthetic classes and anonymous classes are assigned names from the same name-space -- names that end with $1, $2, or such. Due to (a) synthetic classes produced only by javac (and not jdt), and (b) only javac assigning a name to an anonymous class that can be statically removed during dead-code elimination, the names of anonymous classes might differ between the two compilers. Before loading a type, CCL tries to find the type in the meta-data it has. Since its meta-data was generated using jdt, this problem resulted in a java.lang.!NoClassDefFoundError. ===Solution=== Our approach is to permit CCL to load a type even when it does not find the type in the meta-data it has, as long as: * The classname is generated by the compiler. * The base type for the type is present in the meta-data of CCL. == Problem 2: Mapping of Jsni methods between classes whose names differ between the two compilers== This problem is a harder version of the previous problem. To handle JSNI code, GWT uses a two step approach: (i) collect the JSNI methods from the source code and inject them in the hosted mode browser, (ii) rewrite the native methods in the loaded bytecode to call the injected methods. In the JSNI-injection step, jdt-generated names are used, whereas in the rewriting step in Emma mode, javac-generated names are used. Since the same class might be assigned different names by jdt and javac, GWT needs to create a mapping between the classnames for correct execution. ===Solution=== We currently use a heuristic to create this mapping -- both javac and jdt assign names in the same order to all classes with generated names except synthetic classes. For example, if javac has produced classes Foo$1, Foo$2, Foo$3, of which Foo$1 is synthetic, and jdt has produced classes Foo$1 and Foo$2, Foo$2 of javac corresponds to Foo$1 of jdt and Foo$3 of javac corresponds to Foo$2 of jdt. {{{CompilingClassLoader}}} (CCL) creates this mapping for a type when CCL loads it or any of its nested types for the first time in Emma mode. The steps involved in creating the mapping are: * *Get list of all jdt classes with jdt-generated names that are not synthetic:* CCL gets this list from the {{{CompilationUnit}}} corresponding to the type. * *Get list of all javac classes with javac-generated names that are not synthetic:* CCL gets this list using ASM from the bytecode of the enclosing class. * *Order both the list:* A custom comparator is used to order both the lists. The custom comparator first looks at the nesting level, as determined by the number of $ signs in the name, and then compares each token between two $ signs separately. When comparing the tokens between two $ signs, it uses numeric comparison if both tokens can be converted to a number. For example, the order produced by the comparator is: Foo$1 < Foo$9 < Foo$11 < Foo$1$2 < Foo$2$1 < Foo$1$1$1. * *Create a mapping between the two ordered lists* Finally, the class rewriter uses the mapping to ensure that the correct native code is executed.