* Improve some logging and assertions.
* Initialize the prototype map (it was always empty before).
* Actually memoize the prototype objects in this map.
* During class initialization, ensure the base class is also initialized.
* Trigger class initialization anytime a property or function is loaded
from that class. Similarly, trigger module initialization on loads too.
* Treat For/ForIn/ForOfStatements as legal parents for local variables.
This ensures for loops at the top-level in a module have their variables
treated as locals, rather than module properties. Add a test also.
* Track source locations for more nodes.
* Mark auto-generated constructors as public so that they may be called.
* Fix the naming of class init methods; it should be ".init", not ".ctor".
* Clone the loop test cases into both function and module top-level variants.
One guiding principle for what makes it into the MuIL AST is that
the gap between source language and AST should not be too great; the
projection of switch statements from MuJS into MuIL clearly violated
that principle, particularly considering that the logic wasn't even
right due to the incorrect emulation of conditional breaks.
Instead of digging deeper into the hole, I've encoded switch logic
in the AST, and implemented support in the evaluator.
These asserts need to use Object.hasOwnProperty, rather than simply
looking up the property; otherwise, if we have a function whose name
happens to match a built-in ECMAScript property name -- like
toString -- then the assert will fire!
This change generalizes the support for default modules (added as
part of marapongo/mu#57), to use an "alias" table. This is just a
table of alternative module names to the real defining module name.
The default module is now just represented as a mapping from the
special default module token, ".default", to the actual defining
module. This generalization was necessary to support Node.js-style
alternative names for modules, like "lib" mapping to "lib/index".
This change adds runtime verification of the super test's expected
behavior. This has proven useful in testing out some of the recent
runtime/interpreter logic around super, etc. Most likely we will
start testing all of the MuJS tests as real MuIL/etc. tests soon.
This change introduces a MuJS standard library. This will allow us
to support an increasing amount of ECMAScript functionality without
needing to do it in the runtime or the compiler.
The basic idea is as follows.
In the TypeScript compiler, all uses of ECMAScript standard types and
functions get bound to the TypeScript library headers. These headers
are in the node_modules/typescript/lib/ directory, and there is one
per ECMAScript "standard" (lib.d.ts for ECMAScript 5, lib.es2015.d.ts
for ECMAScript 6, lib.es5016.d.ts for ECMAScript 7, and so on). Note
that these libraries are "header only", since the functionality for
these are of course supplied by whatever runtime (V8, Chakra, etc)
actually executes the resulting JavaScript code.
In our case, we have no such luxury. So instead, we intercept these
bindings and transform them into bindings to a MuJS standard library.
This library is just a MuIL library written in MuJS code. Only the
set of operations expressable in basic MuIL can be used to implement
these. In select few cases, we will need runtime intrinsics, but those
will not be specific to MuJS and will go into the basic Mu library and
shared among all MetaMu compilers.
At this point, the MuJS standard library only contains the ECMAScript
Error type, so that we can represent throwing errors in MuJS/MuIL.
This changes the way we discover the kind of export in the case of
an export declaration. This might be exporting a module member, as in
let x = 42;
export { x as y };
or it could be exporting an entire module previously imported, as in
import * as other from "other";
export { other };
In each case, we must emit a proper fully qualified token.
Our logic for detecting these cases was a bit busted previously. We
now rely on real symbolic information rather than doing hokey name lookups.
This change redoes the way module exports are represented. The old
mechanism -- although laudible for its attempt at consistency -- was
wrong. For example, consider this case:
let v = 42;
export { v };
The old code would silently add *two* members, both with the name "v",
one of which would be dropped since the entries in the map collided.
It would be easy enough just to detect collisions, and update the
above to mark "v" as public, when the export was encountered. That
doesn't work either, as the following two examples demonstrate:
let v = 42;
export { v as w };
let x = w; // error!
This demonstrates:
* Exporting "v" with a different name, "w" to consumers of the
module. In particular, it should not be possible for module
consumers to access the member through the name "v".
* An inability to access the exported name "w" from within the
module itself. This is solely for external consumption.
Because of this, we will use an export table approach. The exports
live alongside the members, and we are smart about when to consult
the export table, versus the member table, during name binding.
This change plucks array element types out of more AST nodes, tightening
up the MuIL that MuJS produces. It also adds a few cases where we should
be emitting "object", and permits more dynamic conversions (which are
technically unsafe, but necessary for the sort of duck typing we anticipate).
This change fixes a few things about property initializers:
* Instance properties on classes need an object (`this`) for initialization.
Previously, this wasn't being passed, which leads to a verification error.
* Static properties on classes should go into the `.init` function, not the
`.ctor` function.
* Synthesized constructors need to invoke `super()` when there is a base
class. It's legal for this call to be missing from the source program
when the base's constructor has no args.
* All initializers happen immediately after such a call to `super()`, to
achieve the intended initialization order. Therefore, for an existing
constructor, we must search for the insertion point manually.
* Also add a superclass case to the existing property tests.
This change fixes up some cases that weren't quite right, when it
comes to load location expressions. This includes functions and
local variables. Furthermore, the loadIdentifierExpression codepath
should be routed through the general purpose load location logic,
so that it can detect the various cases.
After this change, the ec2instance example fully verifies.
Per marapongo/mu#57, this change marks the "index" module as the default.
We don't yet discover and crack open `package.json` files which may override
this by specifying a "main" function.
This change also generates entrypoint functions for all MuJS modules. To
emulate the way Node.js works -- in that every module is still a valid script
target -- we are generating entrypoints for everything; this is in contrast to
only generating them for "blueprint" modules, or somehow marking `main`
somehow. See the notes in marapongo/mu#57 for additional thoughts on this.
This change transforms element access expressions much like we do
property access expressions, in that we recognize several static load
situations. (The code is rearranged to share more logic.) The code
also now recognizes situations where we must devolve into a dynamic
load (e.g., due to a string literal, dynamic LHS, etc).
This change fixes a few issues:
* Emit ObjectLiteral types as `dynamic`. We can do better here, including
spilling the "anonymous" types, for cases where cross-module exports/imports
are leveraged. For the time being, however, we will erase them to dynamic.
This is a heck of a lot better than emitting garbage `__object` type tokens.
* Implement TypeScript TypeAssertionExpression lowering into casts.
* Recognize static class property references. Previously, we tried to load
the class as a location, which resulted in nonsense, unverifiable MuIL. Now
we properly detect that the target is a property symbol with the static
modifier, and emit the token without an object reference (as expected).
* Accompany the above with a bunch of tests that stress these paths.
We weren't properly emitting fully qualified tokens for properties
and "block scoped variables" (of which, module properties qualify).
This change fixes that, bringing the ec2instance verification error
count down from 22 to 7.
This change introduces support for dynamic object literals. If the
type of a literal is dynamic, then the property initializers for that
literal are naked strings; if the type of a literal is anything else,
however, we expect that the property names are full blown member tokens.
This change simplifies certain types while lowering to MuIL, fixing
a number of assertion failures that cropped up after my tightening of
the type checking (namely, asserting rather than silently logging).
This comes in the following forms:
1) StringLiteral, NumberLiteral, and BooleanLiteral types can simply
become String, Number, and Boolean, respectively. This loses some
static type-checking (e.g., ensuring that values are within range),
however at least the physical runtime type will match.
2) EnumLiteral types can simply adopt their base types, with similar
downsides and caveats to the other literal types (range widening).
3) Any union types that contain Undefined or Null types can be simplified
to omit those types. If, after filtering them out, we are left with
a single type (admitting the recursive case of multiple XLiterals that
simplified down to the exact same primitive type), we will eliminate
the union altogether and just use the underlying simple type. This
handles the case of optional interface properties, e.g. `T?`, which are
internally represented in TypeScript as `T|undefined` union types.
There are two follow up work items for later on:
* marapongo/mu#64 tracks support for non-nullability. At the moment we
are tossing away any `|undefined` or `|null` unions, including `T?`,
because MuIL currently permits nullability everywhere, but we want to
consider honoring these annotations (eventually).
* marapongo/mu#82 tracks support for union types. This is how we intended
to support true enum types, so tossing away this information is a bit
unfortunate. But this is a feature for another day.
This change prepares to mimick the TypeScript behavior of `any` which,
it turns out, is closer to Python and Ruby duck typing than what we have
here. It also introduces `object` to mean "the base of all types".
MuJS has been updated to map `any` to `dynamic`.
At this point, the conversion logic now distinguishes between NoConversion,
ImplicitConversion, and AutoCastConversion cases, the latter being a case
in which `dynamic` is the source and so a duck-type-style cast must occur.
Right now we do not honor the AutoCastConversion case anywhere, so we still
have not achieved the duck-typing semantics of the source language.
This is part of fixing marapongo/mu#79.
Turns out we hadn't been transforming interface heritage clauses --
extends and implements -- like we were classes. This change fixes
that. As a result, we're down to 14 verification errors for AWS.
This change also accepts MuPackages during MuJS dependency resolution.
The old logic wasn't quite right, because it would skip over MuPackage
files, and keep probing upwards, possibly binding to the wrong, pre-
compilation, Mufile. Note that we must still probe for both Mupackages
and Mufiles, because of intra-package dependencies.
After this change, the AWS MuPackage is down to 28 verification errors.
To prepare a MuPackage for consumption by another MuPackage, we need
to save the definition files output by the TypeScript compiler. In
theory, we could regenerate these definitions from the MuPackage itself;
in fact, when we tackle cross-language, this will be necessary. For
now, however, it's much simpler to simply save the definition outputs
alongside the Mupack.json package metadata.
Note that we do not save "code" outputs. The MuIL format encodes all
computations and so the only thing we need for dependent packages is
the header files to compile against; all "link" and runtime dependencies
are satisfied by the MuIL format and Mu toolchain.
There are a variety of ways to specify a MuJS output location, including
the command line --out (-o for short) flag. If that isn't present, we simply
dump the Mupack.json file in the current working directory. However, in order
to prepare Mupackage files for packaging, distribution, and reuse, we actually
want to store the Mupack.json file alongside all the other package assets (like
TypeScript header files). So it makes sense to recognize the outDir, if present,
in the tsconfig.json file, and use it as the default if not otherwise specified.
This changes transformIdentifierExpression to call through to the
general purpose function that understands how to transform any Symbol
reference into a fully qualified token suitable for serialization.
This change recognizes identifier expressions that mention the null and
undefined constants, transforming them into null literals. Note that
marapongo/mu#70 tracks mimicking the subtle differences between
undefined and null in ECMAScript.
In the event that we're loading a property straight from a module,
we needn't actually load the module at all; instead, we refer to it
using a fully qualified token.
In many cases, property access expressions interact with types. For
example, the expression `o.j.bar()` likely accesses variable `o`, then
its property `j`, then `j`'s method `bar`. In other cases, however,
property access elements are actually modules. For example, in:
import * as o from "o";
o.j.bar();
This change recognizes this situation and emits code correctly.