diff --git a/src/main/markdown/doc/latest/compiler/introduction.md b/src/main/markdown/doc/latest/compiler/introduction.md new file mode 100644 index 000000000..7272268d0 --- /dev/null +++ b/src/main/markdown/doc/latest/compiler/introduction.md @@ -0,0 +1,217 @@ +# Introduction to the GWT Compiler + +This document is intended for developers who want to learn a little more about how the GWT compiler works, and is +not required reading for using GWT. The developers writing and updating this document are themselves still learning, +and welcome advice and feedback on both the compiler and this document. + +The GWT compiler's purpose is to translate and optimize Java source code into JavaScript output. Any optimizing +compiler's job, more broadly, is to preserve the effect of the original code's semantics, while removing unnecessary +operations and improving the efficiency of the remaining operations. We refer to the translation pieces as +"normalization" where the compiler rewrites some Java or JS construct to another form in the same language, +"transformation" where the compiler rewrites one AST to another, and "optimization" where the compiler rewrites a given +AST to produce more efficient output. Finally, "linking" refers to the process of taking the finished JS fragments and +making them usable in a JavaScript runtime. + +1. [Compiler overview](#compiler-overview) +2. [ASTs](#asts) +3. [Normalization](#normalization) +4. [Optimization passes](#optimization-passes) +5. [SourceMaps](#sourcemaps) +7. [Verifying changes and diagnosing bugs](#verifying-changes-and-diagnosing-bugs) +8. [SOYC and FlightRecorder](#soyc-and-flightrecorder) +6. [FAQ: "Will this be optimized?"](#faq) + +## Compiler overview + +## ASTs + +There are three ASTs involved in the compiler: JDT's own Java AST, used to parse Java sources, resolve references, +and perform type inference, the GWT Java AST, and the GWT JavaScript AST. Each has its own purpose, benefits, and +quirks. + +### JDT AST + +For the most part, we use JDT to parse Java sources, take advantage of its ability to handle type inference for us, and +report errors in the source quickly to the user. + +### GWT's Java AST + +From the JDT AST, the `GwtAstBuilder` class produces the GWT Java AST. We qualify this as the "GWT" Java AST, as there +are some specific node types that aren't true Java, or have not-quite-Java semantics. + +#### JDebuggerStatement + +#### JUnsafeTypeCoercion + +#### JsniMethodBody/JsniMethodRef + +#### JPermutationDependentValue + +#### JNameOf + +#### JNumericEntry + +#### JProgram + +### GWT's JS AST + +Like the GWT Java AST, this is not quite conventional JS. First, it is limited to roughly ES3 - it hasn't been updated +to support `async`/`await`, `const`/`let`, or the `for`-`of` syntax, among others. It also is missing ES3's `with`. + +#### JsProgram + +#### JsNameOf + +#### JsNumericEntry + +#### JsProgramFragment + + +## Normalization + +Devirtualizer - replaces virtual method calls with static calls on String/Number/Boolean, Array types, and JSO types. +CatchBlockNormalizer - replaces catch blocks with if statements to support Java's ability to handle exception types differently. +PostOptimizationCompoundAssignmentNormalizer +LongCastNormalizer +LongEmulationNormalizer +TypeCoercionNormalizer +SplitCaseStatementValues +ComputeCastabilityInformation/ComputeExhaustiveCastabilityInformation +ImplementCastsAndTypeChecks +ImplementJsVarargs +ArrayNormalizer +EqualityNormalizer +ResolveRuntimeTypeReferences + +ReplaceGetClassOverrides + +-- + +JsNormalizer + +EvalFunctionsAtTopScope + +JsStackEmulator + +JsBreakUpLargeVarStatements + +## Optimization passes + +Pruner - Removed unused types, methods, fields, and parameters. Roughly analogous to "tree shaking" tools that exist for +JS, but runs again after other passes have made changes to find more code to remove. +Finalizer - Marks classes, fields, methods, and variables as `final` where possible, which can enable other optimizations. +MakeCallsStatic - Rewrites instance methods to be static where there are no possible overrides, with "this" as the first +argument. Depends on the method being final, and a given callsite being specific enough to be statically resolved. The +instance method is retained, but delegates to the new static implementation. +TypeTightener +MethodCallTightener +MethodCallSpecializer +DeadCodeElimination +MethodInliner +SameParameterValueOptimizer (optional) +EnumOrdinalizer (optional) + +RemoveEmptySuperCalls (post loop) + +RemoveSpecializations (post loop) + +-- + +JsStaticEval +JsInliner +JsUnusedFunctionRemover + +DuplicateClinitRemover (only run once) +JsDuplicateCaseFolder (only run once) + +## SourceMaps + +... + +## Verifying changes and diagnosing bugs + +Implementing new passes or changes are best accompanied by unit tests that run just that pass on cases that are +expected to be impacted, or to be left alone. This isn't sufficient to catch unexpected interactions though - existing +codebases should have their ASTs printed out and compared before/after to ensure that changes are as expected. + +The Java system property `gwt.jjs.dumpAst` can be set to a path to dump the GWT ASTs to at various points throughout +compilation. It defaults to writing the entire program at once, and has no delimiter between passes. The system property +`gwt.jjs.dumpAst.filter` allows the output to be filtered to certain source files or ranges of lines within those files. +Sources are comma delimited, and line ranges are appended to the end of the file as `:-`. For +example, `com/example/MyClass.java:10-20` would only print lines 10-20 of `MyClass.java`, tracing them through +compilation, even if they are inlined into other classes. + +Unfiltered, this can be helpful to make a change to the compiler and compile a full application before and after, +and use `diff` to compare the two outputs. Consider setting specifying a large enough context for the diff to see +more of the method that is impacted to figure out where the change is in the codebase, e.g. + +```shell +diff -U5 before.ast after.ast +... +``` + +## SOYC and FlightRecorder + +... + +## FAQ + +AKA "Will this be optimized?" + +#### Does `final`/`sealed` make any difference? +Generally no, at least in terms of how the compiler looks at your code. GWT is a "whole world" compiler, seeing all +classes at once, so it already knows if a class has subclasses, if a field is assigned only once, and so on. + +The flags can still be helpful for human readers and for some language features like matching on subclasses when +compiling the Java sources. + +The `Finalizer` optimization pass will add `final` to the Java AST where possible but omitted, or where newly possible +by other optimizations. + + +#### Will local variables be inlined away? +For constants, yes. Other cases, not at this time - reordering some kinds of statements can result in side effects. + +#### Will getters/setters be inlined to simplify callers? +Usually yes, provided the body of the method is simple enough, and either not overridden or the compiler can be certain +that only one implementation can be called. + +#### Will assertions be optimized out? +By default, in production mode, yes, but you can enable them with `-ea`, same as normal Java. They are always enabled +at this time in dev mode. Writing detailed error messages here will enhance your dev mode experience, but will +not add additional runtime cost or size to your production compiled app. The downside is that if you those assertions +are disabled in production mode, there will be no exception thrown, which could break later code. + +When assertions are compiled by GWT, they presently are replaced as follows: +```java +assert : +``` +```java +if () { + GWT.debugger(); + throw new AssertionError(); +} +``` +When compiled out, both the condition and the expression will be removed, so no side effects are left. + +#### What about debugger statements? +Debugger statements, either from JSNI, from `GWT.debugger()`, from jsinterop-base's `Js.debugger()`, or from JSNI are +never optimized out. All browsers skip them, unless dev tools are open while JS is executing. Most dev tools have an +option to ignore `debugger` statements. + +#### Why does my generated code contain `null.nullMethod()` or `null.nullField`? +When the compiler determines that no constructor for a class can ever be called, it removes the class, but sometimes +references to that class are left behind. Those references must now always be `null`, so any method call or field +reference will always result in a null pointer exception. Usually this code is unreachable and pruned anyway, but it is +possible to encounter this in production code - but typically, that code would have thrown a NullPointerException anyway. + +#### What's the difference between `Js.cast(...)` and `Js.uncheckedCast(...)`? +The jsinterop-base method `Js.cast(...)` should be used when the cast is legal, but confuses Java, so you can't use a +plain Java cast. For example, when you cast `java.lang.String` to `elemental2.core.JsString` - clearly legal, but will +confuse the compiler. The code generated from `Js.cast(...)` will have the same runtime type checks as a normal Java cast, +and will fail in the same circumstances as well. Disabling runtime cast checks will also disable `Js.cast(...)`. + +On the other hand, `Js.uncheckedCast(...)` should be used when the code is trying to circumvent the type system, and +there must not be a runtime check. This could be to work around a limitation or incorrect type in elemental2, or a bug +in a browser, or to knowingly do something "wrong" that may still make sense in the browser (such as casting +an `Int8Array` to `byte[]`).