Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Breaking] Introduce Bundle Compiler #631

Merged
merged 42 commits into from Aug 28, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
2c52930
WIP
wycats Aug 2, 2017
ca32e07
[WIP]
wycats Aug 2, 2017
080eedd
[BREAKING] Extract Opcode Compiler into its own package
wycats Aug 4, 2017
d7527f5
Remove unnecessary InlineBlock construct
wycats Aug 4, 2017
cb471d6
Inject OpcodeBuilder
chadhietala Aug 4, 2017
55b216a
WIP
wycats Aug 8, 2017
1008d71
[WIP] More passing tests
wycats Aug 10, 2017
4659065
Fix curried component bug
wycats Aug 10, 2017
6ad283a
WIP tom mode
wycats Aug 10, 2017
1910bc6
Get tests passing with handles
wycats Aug 11, 2017
61c65a1
[WIP] Updates to the bundle compiler
wycats Aug 14, 2017
255bf2f
[WIP] Starting whole-flow tests
wycats Aug 15, 2017
94d67ce
Tests pass
wycats Aug 15, 2017
ffb0f20
Cleanup some type errors
chadhietala Aug 15, 2017
a4a57f9
Reorganize tests to share more between envs
wycats Aug 16, 2017
45c3339
fix tslint
chadhietala Aug 16, 2017
dabac96
[HACKS] Targeted fix for tbody
chadhietala Aug 17, 2017
ff5a57c
[HAX] Fix the property tests
chadhietala Aug 17, 2017
7d76733
Implemented BasicComponent in the bundling environment
wycats Aug 18, 2017
a81c1de
Fix type errors
chadhietala Aug 18, 2017
88851d4
Resolve all merge conflicts and also make sure changes were migrated
chadhietala Aug 18, 2017
a1e5aff
No type errors however lots of failing tests
chadhietala Aug 18, 2017
45a1efa
Make the tests pass post-rebase
chadhietala Aug 18, 2017
7d2196d
Add correct deps to packages
chadhietala Aug 18, 2017
53a7a3d
Fix package.json
chadhietala Aug 19, 2017
2978b95
[WIP] Bundle compiler works with components
wycats Aug 22, 2017
51ef55e
Change BundleCompiler constructor to take an options hash
tomdale Aug 23, 2017
60e3e77
specifier -> specifierFor
tomdale Aug 23, 2017
2066eb2
Swap order of arguments to BundleCompiler's `add()` method
tomdale Aug 23, 2017
36c681d
Return serializable entities from BundleCompiler's `compile()`
tomdale Aug 23, 2017
51284e5
Some {{curly}}s implemented in Bundle compiler
wycats Aug 24, 2017
2648108
Return serializable representation of heap from bundle compiler
tomdale Aug 24, 2017
af4b001
Add compiler architecture overview
tomdale Aug 24, 2017
b68846f
Remove QUnit.skip
chancancode Aug 24, 2017
e84f1ee
Bundle Compiler working w/ entire component suite
wycats Aug 24, 2017
bd973b5
Allow AOT mode to invoke static directly
wycats Aug 24, 2017
618e16e
Remove native Map implementation for now
chadhietala Aug 25, 2017
b82c117
Remove object spreading and last Map implementation
chadhietala Aug 25, 2017
5e019c3
Mitigate rehydration of props in ie9
chadhietala Aug 25, 2017
1b60e6c
Fix IE9 serialization issues in tests
chadhietala Aug 25, 2017
9b1822c
Treat LookupMap items as Opaque
chadhietala Aug 25, 2017
728c6e7
Bring back logging for bundle compiler
chadhietala Aug 26, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
19 changes: 18 additions & 1 deletion build/broccoli/build-packages.js
Expand Up @@ -9,6 +9,7 @@ const UnwatchedDir = require('broccoli-source').UnwatchedDir;
const transpileToES5 = require('./transpile-to-es5');
const writePackageJSON = require('./write-package-json');
const writeLicense = require('./write-license');
const debugMacros = require('babel-plugin-debug-macros').default;

const Project = require('../utils/project');
const project = Project.from('packages');
Expand Down Expand Up @@ -136,7 +137,23 @@ function transpileCommonJS(pkgName, esVersion, tree) {

let options = {
annotation: `Transpile CommonJS - ${pkgName} - ${esVersion}`,
plugins: ['transform-es2015-modules-commonjs'],
plugins: [
[debugMacros, {
envFlags: {
source: '@glimmer/local-debug-flags',
flags: {
DEBUG: false
}
},
debugTools: {
source: '@glimmer/debug'
},
externalizeHelpers: {
module: true
}
}],
'transform-es2015-modules-commonjs'
],
sourceMaps: 'inline'
};

Expand Down
93 changes: 93 additions & 0 deletions guides/01-introduction.md
Expand Up @@ -34,6 +34,99 @@ The code examples in this document are written in [TypeScript][typescript].
[handlebars]: http://handlebarsjs.com
[typescript]: http://www.typescriptlang.org

## Architecture

The key insight of Glimmer is that Handlebars is a declarative programming
language for building and updating DOM. By structuring web UI around Handlebars
templates as the central abstraction, we can use advanced techniques from
programming languages and compilers to significantly boost the performance of
web applications in practice.

Because of this, Glimmer's architecture has more in common with compiler
toolchains like clang/LLVM or javac/JVM than traditional JavaScript libraries.

At a high level, Glimmer is made up of two parts:

1. The compiler, which turns templates into optimized binary bytecode.
2. The runtime, which evaluates that bytecode and translates its instructions into
things like creating DOM elements or instantiating JavaScript component classes.

### Compiler

The compiler is responsible for turning your program's Handlebars templates into
Glimmer binary bytecode.

Because Glimmer is an optimizing compiler, it must know about all of the
templates in a program in order to understand how they work together. This is in
contrast to transpilers like Babel, which can transform each file in isolation.

As the compiler traverses your application and discovers templates, it parses
each one and creates an _intermediate representation_ (IR). The IR is similar to
the final bytecode program but contains symbolic references to external objects
(other templates, helpers, etc.) that may not have been discovered yet.

Once all of the templates have been parsed into IR, the compiler performs a
final pass that resolves symbolic addresses and writes the final opcodes into a
shared binary buffer. In native compiler terms, you can think of this as the
"linking" step that produces the final executable.

This binary executable is saved to disk as a `.gbx` file that can be served to a
browser and evaluated with the runtime.

But we're not quite done yet. The bytecode program will be evaluated in the
browser where it needs to interoperate with JavaScript. For example, users
implement their template helpers as JavaScript functions. In our compiled
program, how do we know what function to call if the user types `{{formatDate
user.createdAt}}`?

During compilation, Glimmer will assign unique numeric identifiers to each
referenced external object (like a helper or component). We call these
identifiers _handles_, and they are how we refer to "live" JavaScript objects
like functions in the binary bytecode.

When evaluating Glimmer bytecode in the browser, instead of asking for the
`"formatDate"` helper, the runtime might ask for the object with handle `4`.

In order to satisfy this request, the compiler also produces a data structure
called the _external module table_ that maps each handle to its associated
JavaScript object.

For example, imagine we compile a template that invokes two helpers, `formatDate` and
`pluralize`. These helpers get assigned handles `0` and `1` respectively. In order to
allow the runtime to turn those handles into the correct function object, the compiler
might produce a map like this:

```js
// module-table.ts
import formatDate from 'app/helpers/format-date';
import pluralize from 'app/helpers/pluralize';

export default [formatDate, pluralize];
```

With this data structure, we can easily implement a function that translates handles into
the appropriate live object:

```ts
import moduleTable from './module-table';

function resolveHandle<T>(handle: number): T {
return moduleTable[handle];
}
```

You can think of the external module table as the bridge between Glimmer's
bytecode and the JavaScript VM. We can compactly represent references to
external objects in the bytecode using handles, and then rehydrate them later
with minimal overhead.

Now that we have our compiled bytecode and the module table, we're ready to run
our app in the browser, or any other JavaScript environment, like Node.js.

### Runtime

TODO

* * *

[Next: Precompiler Overview »](./02-precompiler-overview.md)
3 changes: 2 additions & 1 deletion package.json
Expand Up @@ -29,6 +29,7 @@
]
},
"dependencies": {
"babel-plugin-debug-macros": "^0.1.11",
"handlebars": "^4.0.6",
"simple-dom": "^0.3.0",
"simple-html-tokenizer": "^0.4.1"
Expand Down Expand Up @@ -68,4 +69,4 @@
"tslint": "^4.0.2",
"typescript": "^2.4.2"
}
}
}
1 change: 1 addition & 0 deletions packages/@glimmer/bundle-compiler/index.ts
@@ -0,0 +1 @@
export * from './lib/templates';