Introduction

The Jet engine is a multi-media platform targetting WebAssembly. It supports an ECMAScript 4 like language, a very modified version of ActionScript 3; and a Cascading Style Sheet 3 dialect.

Unlike Adobe Flex, Jet uses reactive user interface.

Features

Event model

When compared to standard ActionScript 3, the event model has been simplified due to type inference, and uses shorter notation for the event listener methods.

Documentation comments

Documentation comments support Markdown and referencing local images.

Package manager

A productive package manager is supported which facilitates creating new projects and building them. In addition, published packages are automatically queued for documentation generation in the online documentation host.

Reactivity

The ECMAScript for XML (E4X) feature of ActionScript is modified such that the markup syntax is used for implementing UI components, similiar to ReactJS, but tied to Jet Display List.

The E4X markup supports comments and XML namespaces.

Comparison to JavaScript

  • Vanilla JavaScript ∉ typed
  • Vanilla JavaScript ∉ proper custom events
  • JavaScript ∈ immediate classes
  • TypeScript ∉ custom event inheritance (requires ...BaseClassEvents)
  • TypeScript ∉ built-in reactivity

ActionScript language guide

The Jet engine uses ActionScript 3 as its main scripting language. The language supports many new features compared to its standard.

Packages

ActionScript packages are used for organizing definitions, using left-to-right hierarchic names.

package { public var x = 10 }

package me.diantha { public var y = 15 }

// (top-level).x
x

import me.diantha.*;

// me.diantha.y
y
me.diantha.y

Default scope

The language's default scope imports the top-level package, and it is the parent scope of the scope in each source file; therefore it is possible to override the name of top-level items, such as Number, Array and parseInt.

global

The global namespace equals the public namespace of the top-level package. It may be necessary when an item overrides a top-level item, as in:

package q.f {
    public const Number : global::Number = 10;
}

Types

Wildcard

The * type means untyped, and accepts all possible values in the language.

*

void

void // undefined

null

null // null

String

String is a UTF-16 encoded sequence of characters.

String

Boolean

Boolean // false or true

Number

IEEE 754 double-precision floating point.

Number

BigInt

Arbitrary range integer.

BigInt

float

IEEE 754 single-precision floating point.

float

decimal

IEEE 754 quadruple-precision floating point (binary128).

decimal

int

int // signed 32-bit integer

uint

uint // unsigned 32-bit integer

Tuple

Tuples, when untyped, have the length property available.

[T1, T2]

Array

The Array type represents a dynamic list of elements, optimized for when T is a number; for instance, [uint] will use a growable buffer optimized specifically for 32-bit unsigned integers.

[T]

Structural function

Structural function types inherit from Function.

function(T1, T2=, ...[T3]):E

Union

(Ta, Tb)

Map

Map.<K, V>

Usage instance:

const map = new Map.<String, Number>();
map.x = 10;

const fns = new Map.<String, Function>();
fns.m = function() { return 10 };
trace(fns.call("m"));

Note: Property access on a Map equals data access. Method call on a Map equals Map method use.

Structural object

Structural object types are compiled into efficient structures.

{
    /** x */
    x:Number,

    /** y */
    y?:Boolean,
}

...rest components may appear, where rest must be another structural object type; the resulting type may be a subtype of rest depending on whether properties do not collide.

type A = { x:Number };
type B = { y:Number, ...A };
type U = { ...W, ...B };
type W = { z:Number };

Objects

All types except void, null, uint, int, float, Number, decimal, BigInt and Boolean represent referenceable objects that may be null. The Object class is inherited by all types, except *, void, null and unions.

The Object class, unlike in standard ActionScript, is not untyped and there is no support for dynamic classes; therefore, it is preferable to use the * type when it is necessary to access prototype properties such as constructor and toString().

Type conversion

Optional conversion

An optional conversion will return the default value of T on conversion value.

v as T

Forced conversion

A forced conversion throws a TypeError on conversion failure.

T(v)

Type matching

Type test

v is T

Switch

switch type (v) {
    case (n:Number) {
        trace("number");
    }
    default {
        trace("any other");
    }
}

Classes

Inheritance

By default a class extends Object. Use the extends clause for extending a specific class:

class B extends A {}

Constructor

By default the constructor is based in the base class's constructor and will initialize the instance with default property values.

Define a constructor for a class using its name in a function definition:

class A {
    public function A() {}
}

Abstract classes

abstract class A {
    abstract function m():void;
}

Static classes

static class MyNamespace {
    public static const VALUE:Number = 10.5;
}

Final classes

final class A {}

class B extends A {} // ERROR!

Override

class A {
    function m() {}
}

class B extends A {
    override function m() { trace("B!") }
}

Final method

class A {
    final function m() {}
}

class B extends A {
    override function m() { trace("B!") } // ERROR!
}

Super

class A {
    function A() { trace("A!") }

    function m() { trace("A.m!") }
}

class B extends A {
    function B() { super(); trace("B!") }

    override function m() { super.m(); trace("B.m!") }
}

Namespaces

ActionScript 3 uses three-dimensional property names: a property consists of a namespace and a local name.

Private access

You can use ActionScript namespaces to privatize definitions across class fields and access them from any package as long as the used namespace is in scope:

// MyLibInternals.as
package my.lib {
    /**
     * @private
     */
    public namespace MyLibInternals = "http://my.lib/internals";
}

// Atom.as
package my.lib.atoms {
    import my.lib.*;

    public class Atom {
        /**
         * @private
         */
        MyLibInternals var x:Number = 10;
    }
}

// consumer.as
import my.lib.*;
import my.lib.atoms.*;

const atom = new Atom();
trace(atom.MyLibInternals::x);

Events

The native EventEmitter class is designated for dispatching and listening to events, and is the one used for implementing the hierarchic event model in the display list API.

In addition, the IEventEmitter interface may be implemented instead of extending the EventEmitter class.

An event emitter

/**
 * On play event.
 */
[Event(name="play", type="Event")]
/**
 * My player class.
 */
class Player extends EventEmitter {

    public function aMethod() {
        this.emit(new Event("play"));
    }
}

Player usage:

player.on("play", function() { trace("played") });

Note: Unlike in Flash Player, the convention of using static constants for identifying event types is discarded.

An event class

Event constructors must always take the event type as the first argument; any other arguments may follow.

class SomeEvent extends Event {
    // constructor
    public function SomeEvent(type: String) {
        super(type);
    }
}

Emitting

The EventEmitter#emit() method is defined as follows:

public function emit.<E extends this.Event::object>(e:E) : Boolean {
    // code
}

When the emit() method is used, it will force a new E(...) expression to be a correct Event object construction, by ensuring the first argument identifies a determined event type according to E.

Listening

The EventEmitter#on() method is roughly defined as follows:

public function on.<E extends this.Event::type>(
    type: E.name,
    listener: function(E.type):void,
) : void {
    // code
}

Environment variables

Environment variables may be read from the project's .env file using the import.meta.env.VAR_NAME expression:

import.meta.env.FOO

Embed

The Embed() expression may be used for embedding static media into the program.

const text : String = Embed("data.txt");

Note: In Flash Player, an [Embed] meta-data was used in definitions in order to embed static files; this is not the case with Jet engine.

MIME type

Through type inference, the Embed() expression results in either String (UTF-8 interpretation) or ByteArray (octet stream).

Conditional compilation

NAMESPACE::CONSTANT may match a set configuration constant.

NAMESPACE::CONSTANT {
    //
}

NAMESPACE::CONSTANT var x

Inline constants:

trace(NAMESPACE::CONSTANT)

Asynchronous code

function myMethod(): Promise.<Boolean> {
    return await other();
}

ECMAScript for XML

ECMAScript for XML (E4X) comprises XML markup and XML data manipulation facilities.

Markup

XML markup is directly used in ActionScript 3 for rendering reactive UI components.

package me.diantha.portfolio {
    public function Portfolio() {
        return (
            <>
                <j:VGroup xmlns:j="jet.components.**" gap={5}>
                    <!-- some comment -->
                    <j:Label>
                        <markdown>
                            Hi **there**.
                        </markdown>
                    </j:Label>
                </j:VGroup>
            </>
        );
    }
}

XML namespaces are used for importing packages solely within the markup, either non-recursive (q.f.*) or recursive (q.f.**). It is additionally allowed to use lexical names and fully qualified names like <jet.components.HGroup> as long as the component name is in scope.

Note: Unlike the E4X standard 2nd edition, markup does not result in XML or XMLList objects, but rather ReactNode; and there is support for a attributes without an attribute value, which equals a={true}.

Markdown

Use <markdown> tags for translating Markdown to HyperText Markup Language (HTML) text.

Note: Tags nested with <markdown> must comply with XHTML tags; for instance, use <br/> instead of <br>.

It is additionally allowed to interpolate HTML code inside a <markdown> tag using braces:

<markdown>
    Hi, {personName}
</markdown>

Note: Interpolated parts must be in the HTML language (not XHTML or Markdown).

ASDoc

Documentation comments use the format /** */, with lines optionally beginning with *.

Supported tags

@copy

Copies documentation comment from another definition. Use a #x component to refer to an instance property.

@copy A
@copy A.w
@copy A#x
@copy #x

@deprecated

@deprecated [Description]

@internal

Internal comment for an item (not included in the generated documentation).

@internal Comment.

@inheritDoc

Inherits documentation from base class or base class's item.

@inheritDoc

@see

Where item maybe an item reference with optional #x instance property, or just an instance property #x.

@see item [Display text]

@param

@param paramName Description

@private

Hides an item from the generated documentation.

@private

@return

@return Description

@throws

@throws ClassName [Description]

Statements

For..in

Iterate keys:

for (var key in o) {
    //
}

Iterate values:

for each (var value in o) {
    //
}

Import

All of a pacakge:

import q.f.*;

Specific property:

import q.f.x;

Alias:

import x1 = q.f.x;

Alias a package:

import f = q.f.*;

f::x

Alias a package recursively:

package q.f.w {
    public var x = 10;
}

import f = q.f.**;
f::x

Switch

The switch statement, as opposed to the standard, does not support fallthrough in cases.

switch (0) {
    case 0: {
        trace("zero");
    }
    case 1: {
        trace("one");
    }
}

The above prints "zero", not "zero" then "one".

Switch type

switch type (v) {
    case (d:Date) {
        //
    }
    default {
        //
    }
}

Try

try {
    //
} catch (e: SecurityError) {
    //
} catch (e: RangeError) {
    //
} finally {
    //
}

Expressions

String literal

Triple string literal

Triple string literals span multiple lines and are indentation-aware.

const text =
    """
    a
    """

assert(text == "a")

Display List

The Display List is a hierarchy of two-dimensional display objects.

Points and scale factor

A point is a 1/72nd of an inch.

Scale factor

In the display list, all the point units are affected by the global scale factor (ScaleFactor#scale).

scaleFactor.on("change", function() {
    //
});

scaleFactor.scale = 10;