Compile ActionScript 3 source code ahead-of-time (AOT) into native machine code, producing native executables — while keeping the AS3 syntax you already know.
Design philosophy: the compiler frontend only handles
lexing → parsing → generating readable C, delegating optimization and machine-code generation entirely to mature C compilers — no reinventing the wheel.
as3compiler-aot-promo.mp4
ActionScript source (.as)
│ lexer.ts lexical analysis (tokens)
▼
│ parser.ts recursive-descent parsing (AST)
▼
│ codegen.ts semantics + C code generation
▼
C source (.c)
│ build orchestration src/build.ts
│ (build manifest JSON + -I/-L/-l/-D + --target)
▼
┌──────┴───────┐
│ native │ cc/clang -O2 -lm → native executable (Mach-O / ELF / PE)
│ wasm │ clang --target=wasm32-wasip1 → WASI .wasm
└──────────────┘
"Generating a single readable .c" is the default form (simple to build, easy to read), not a law against linking third-party libraries:
when implementing heavy lifting such as graphics (skia/cairo/SDL), the correct approach is to link mature libraries (-lskia) rather than hand-rolling rasterization.
See "Build & Linking" and AGENTS.md §2.9.
Install (gives the as-aot command): npm install -g as3compiler (or locally npm link).
If not installed, use node src/index.ts ... or npm run compile -- ... equivalently.
Requirements: Node ≥ 22.6 (native support for running .ts directly, no compilation step), and a system cc/clang;
--target wasm needs the WASI SDK (WASI_SDK_HOME pointing at the SDK root, or an LLVM clang with a wasm32 backend),
and running the output requires a runtime supporting the exception-handling proposal (wasmtime/wasmer, see docs/en/compile.md §2);
--target wasm --package web needs the Emscripten SDK (EMSDK_HOME pointing at the emsdk root, see docs/zh-cn/html5-web.md).
# Compile (default output: a file named after the input without extension, keeping the generated .c for reading)
as-aot examples/hello.as
# Compile and run
as-aot examples/fib.as --run
# Specify output name / specify the C compiler
as-aot examples/class.as -o build/class --cc clang --run
# Generate a macOS .app Xcode project (--package xcode-project: open/build/debug in Xcode, product is an .app)
as-aot src/Main.as --target native --package xcode-project -o build/Main
# Generate a browser HTML5-rendered page (--target wasm --package web, requires the Emscripten SDK)
as-aot examples/web/hello-web.as --manifest examples/web/hello-web.build.jsonFull compilation instructions (CLI arguments, build manifest JSON, multi-target backends, WASI toolchain) are in
compile.md.
Supports multiple targets and linking third-party libraries:
# WASI target (requires the WASI SDK or a clang with a wasm32 backend)
as-aot examples/hello.as --target wasm
# Link third-party libraries (skia/cairo/SDL etc.): include paths / library paths / libraries / macro definitions
as-aot app.as -I vendor/include -L vendor/lib -l skia -D USE_SKIA=1
# Build manifest (JSON, paths resolved relative to the manifest directory)
as-aot app.as --manifest examples/skia-link.build.example.json
# Export top-level functions for direct JS calls (default WASI command modules only export _start)
as-aot examples/wasm-native/fib.as --target wasm --export fib
# Declarative exports: mark functions/static methods with [WasmExport] metadata to auto-export and generate .exports.json
as-aot examples/wasm-native/export-meta.as --target wasm
# Generate a browser HTML5-rendered page (--target wasm --package web, requires the Emscripten SDK)
as-aot examples/web/hello-web.as --manifest examples/web/hello-web.build.json
# Only generate C and print the compile command, without actually compiling (verify the command without a toolchain)
as-aot examples/hello.as --target wasm --dryBuild manifest (JSON) fields: target (native/wasm), package (raw/xcode-project/android-project/web, default raw, see compile.md §6), c-compiler, opt, sources (additional C/C++ sources),
include-paths, link-libs, link-paths, defines, objects (precompiled .o), exports (C symbols exported to the .wasm export table), font-urls (font byte-stream URL list for --package web, injected into Skia at runtime), plus the xcode-project app metadata bundle-id/display-name/icon/deployment-target (see compile.md §6.4). CLI arguments override manifest fields of the same name.
Path-like fields resolve relative to the manifest file's directory. Example at
examples/skia-link.build.example.json.
| Category | Supported |
|---|---|
| Types | int, uint, Number, Boolean, String, void, Array, Vector.<T>, Function, RegExp, Class (class reference), Dictionary (object-reference-keyed associative table), * (untyped → dynamic any boxing), class names (object types), interface names |
| Literals | integer, float, hexadecimal (0xFF), string (single/double quotes), true/false, null, Infinity/NaN, array literal [...], object literal { ... }, regex literal /pattern/flags, Vector literal new <T>[...] |
| Expressions | arithmetic + - * / %, comparison == != < <= > >=, logical && || !, unary - + ! ~, bitwise & | ^ << >> >>>, increment/decrement ++ -- (prefix/postfix), ternary ?:, type checks is / casts as, coercion syntax Type(expr), typeof, delete, membership in, array indexing a[i], property access o.x, compound assignment += -= *= /= %= <<= >>= >>>= &= |= ^= |
| Statements | variable declarations (including multiple var a:T, b:T, c:*;), expression statements, if/else, while, do-while, for, for-in (array indices / dynamic object keys / Dictionary object-reference keys), for-each-in, switch/case/default, break, continue, labeled statements label: while(...){ break label; }, return, throw, try/catch/finally, blocks; arguments inside functions (a formal-parameter array generated on demand) |
| Functions | free functions with typed parameters and return types; optional and default parameters function f(a:int, b:int = 1); rest parameters function f(...args:Array); Function type and function values (anonymous function expressions, functions as arguments/return values); closures (anonymous functions capturing outer local variables, captured variables heap-allocated to escape) |
| Classes | fields + methods + parameterized constructors + new + this + inheritance extends + override (vtable dispatch) + super calls (including the implicit no-arg super() when there is no explicit super) + access modifiers public/private/protected/internal + static fields/methods + const constants + get/set accessors + final classes/methods + dynamic classes (dynamic class, undeclared properties route to a runtime slot table) + static methods as Function values (ClassName.method or bare identifiers) + dynamic class instantiation new (expr as Class)() (no-arg factory via class reference) |
| Interfaces | interface declaration + implements (interface vtable dispatch) + is/as interface type checks |
| Packages/Modules | multi-file compilation (multiple .as files merged into one compilation unit); package namespace isolation (C identifier prefix); import cross-file/cross-package short-name resolution; internal package-local visibility (rejected across packages) |
| Root classes | Object built-in root class: implicit base class of all classes, toString() returns the runtime class name; Error built-in error type: new Error(message), .message property; built-in error subclasses TypeError/RangeError/ArgumentError/SyntaxError; built-in Date class (millisecond timestamp + calendar accessors); flash.events event system Event/EventDispatcher/MouseEvent/KeyboardEvent/FocusEvent/TimerEvent/ProgressEvent/ErrorEvent/IOErrorEvent/DataEvent (addEventListener/dispatchEvent capture→target→bubble three phases, preventDefault/stopPropagation/stopImmediatePropagation; Event completes CANCEL/CLEAR/CLOSE/CONNECT/COPY/CUT/DEACTIVATE/EXIT_FRAME/FRAME_CONSTRUCTED/FULLSCREEN/ID3/INIT/MOUSE_LEAVE/OPEN/PASTE/RENDER/SCROLL/SELECT/SOUND_COMPLETE/TAB_*/UNLOAD constants, event subclasses (stage fifty-nine) TimerEvent (TIMER/TIMER_COMPLETE), ProgressEvent (PROGRESS + bytesLoaded/bytesTotal), ErrorEvent (ERROR + text), IOErrorEvent (IO_ERROR, inheriting text), DataEvent (DATA + data)); flash.display display list DisplayObject/InteractiveObject/DisplayObjectContainer/Stage/Sprite/Shape/Graphics/Bitmap/BitmapData (layering/coordinates/addChild (triggering addedToStage)/removeChild/hit testing/recursive rendering, as_render_object dispatching by is subtype chain, user Sprite subclasses may act as containers, cacheAsBitmap subtree bitmap caching (toggle invalidates and re-bakes); Stage stage properties stageWidth/stageHeight/fullScreenWidth/fullScreenHeight/displayState/quality/color/align/scaleMode/frameRate and boolean switches, constant classes StageAlign/StageScaleMode/StageQuality/StageDisplayState); flash.text text TextField/TextFormat (SkParagraph full layout: HarfBuzz shaping + UAX#14 breaking + real line height, multiline/wordWrap/scrollV/maxScrollV/numLines/textWidth/textHeight + background/backgroundColor background fill); flash.utils.Dictionary (new Dictionary(weakKeys): an associative table keyed by object reference, supporting in/delete/for-in, the weak-reference parameter accepted but not modeled, keys strongly held for the program lifetime); flash.utils.Timer (stage sixty): a repeating timer, inheriting EventDispatcher, constructor Timer(delay, repeatCount=0), delay setter validates negative/non-finite values throwing RangeError; repeatCount setter does not validate (int truncation, negatives preserved, NaN→0), currentCount/running read-only, start/stop/reset; dispatches TimerEvent.TIMER per interval, TimerEvent.TIMER_COMPLETE when repeatCount>0 is exhausted, repeatCount=0 repeats indefinitely; driven by the frame loop as_timer_tick (off-screen uses tickTimers() to pump manually, WASI degrades to second granularity); flash.geom 2D geometry (stage fifty-eight) Point/Rectangle/Matrix/ColorTransform/Transform (Point static distance/interpolate/polar and instance add/subtract/offset/normalize/setTo/copyFrom/clone/equals/toString; Rectangle read-only top/bottom/left/right and intersection/union/contains/containsPoint/containsRect/intersects/equals/inflate/offset/clone/setEmpty/isEmpty/toString; Matrix identity/translate/scale/rotate/concat/invert/transformPoint/deltaTransformPoint/createBox/createGradientBox; ColorTransform 8 channels + concat; Transform holds identity matrix/colorTransform, now wired to DisplayObject.transform (Matrix applied to the Skia canvas transform)); Vector3D/Matrix3D (stage seventy-nine, flash.geom 3D geometry, pure logic, no GPU): Vector3D x/y/z/w four components (geometric ops normalize/dotProduct/crossProduct/distance/angleBetween/length/lengthSquared use xyz only, w the homogeneous coordinate; add/subtract/scaleBy/negate four-component; equals(allFour), toString + static constants X_AXIS/Y_AXIS/Z_AXIS); Matrix3D 4×4 column-major (rawData a column-major 16-element Vector.<Number>, identity/append/prepend/invert (in-place, returning Boolean invertibility)/transpose/transformVector/transformVectors/deltaTransformVector/pointAt/interpolate/recompose/decompose/copyFrom/clone + translation/scale/rotation constructors + static identity/interpolate, decompose/recompose round-trip consistent); AGALTranslator (stage eighty, AGAL bytecode kernel, flash.display3D pure-static bridge, not an AS3 runtime API): translate(bytes:ByteArray, target:String):String translates AGAL1/2/3 bytecode into readable MSL ("msl") or GLSL ES ("glsl") source — parses the 7-byte header + variable-length instruction tokens (dest [num:16][mask:8][type:8], source [num:16][offset:8][swizzle:8][type:8][reltype:8][relsel:16], sampler [num:16][lod:8][0:8][samplerbits:32]), 28 base instructions with swizzle/write-mask expansion + AGAL2 control flow (ife/ine/ifg/ifl/els/eif) + derivatives (ddx/ddy) + tex sampling; register type is shader-relative encoding (vc=fc=1, vt=ft=2, op=oc=3, fs=vs=5) dispatched by vertex/fragment; basic validation (magic/version/opcode/truncation/instruction count) throws Error); Stage3D/Context3D (stage eighty-one, flash.display3D pure CPU state machine + resource classes, GPU upload/draw lands in stage eighty-two): Stage.stage3Ds returns a Vector.<Stage3D> (single slot), Stage3D.requestContext3D(mode) lazily creates a Context3D and synchronously dispatches Event.CONTEXT3D_CREATE, context3D/x/y/visible read/write; Context3D state machine configureBackBuffer/setBlendFactors/setDepthTest/setCulling/setProgram/setVertexBufferAt/setTextureAt/setProgramConstantsFromMatrix (transposed=false uploads the column-major matrix transposed to row-major, true copies verbatim)/createVertexBuffer/createIndexBuffer/createProgram/createTexture/drawTriangles/clear/present (the latter three record state only for now) + read-only driverInfo/profile; resource classes VertexBuffer3D/IndexBuffer3D (uploadFromVector records a CPU copy)/Program3D (upload records bytecode)/Texture/TextureBase (uploadFromBitmapData); 15 constant classes Context3DBlendFactor/Context3DBufferUsage/Context3DClearMask/Context3DCompareMode/Context3DFillMode/Context3DMipFilter/Context3DProfile/Context3DProgramType/Context3DRenderMode/Context3DStencilAction/Context3DTextureFilter/Context3DTextureFormat/Context3DTriangleFace/Context3DVertexBufferFormat/Context3DWrapMode); flash.filters filters (stage sixty-one) BitmapFilter (abstract base class, clone())/BlurFilter (blurX/blurY/quality + clone)/DropShadowFilter (distance/angle/color/alpha/blurX/blurY/strength/quality/inner/knockout/hideObject + clone)/GlowFilter (color/alpha/blurX/blurY/strength/quality/inner/knockout + clone) + constant class BitmapFilterQuality (LOW/MEDIUM/HIGH); DisplayObject.filters read/write (Array access); BitmapData.applyFilter (BlurFilter implemented via separable box blur, DropShadowFilter/GlowFilter rasterization deferred throwing errors); flash.display completion (stage sixty-two) MovieClip (currentFrame/totalFrames + play/stop/gotoAndPlay/gotoAndStop, frame-loop driven, currentFrame wraps to 1 when out of bounds, totalFrames writable to simulate multi-frame timelines)/SimpleButton (upState/overState/downState/hitTestState four state references)/Loader (content/contentLoaderInfo + load(url) async dispatch of INIT (sync)/COMPLETE (next frame tick))/LoaderInfo (bytesLoaded/bytesTotal/url + COMPLETE/INIT/OPEN/UNLOAD/PROGRESS/IO_ERROR constants); flash.net/flash.ui (stage sixty-three) URLRequest (url/method/data/contentType, method defaults to "GET")/URLLoader (data/dataFormat + load(request)/close, inheriting EventDispatcher, asynchronously (next frame tick) reading local files, dispatching COMPLETE on success and IO_ERROR on failure, data a GC-tracked string)/URLVariables (dynamic class, undeclared string-keyed properties stored in a runtime slot table, toString() serializes to a query string)/Keyboard (static key-code constant table AZ/NUMBER_0NUMBER_9/SPACE/ENTER/TAB/ESCAPE/BACKSPACE/DELETE/SHIFT/CONTROL/LEFT/RIGHT/UP/DOWN + read-only isAccessible)/Mouse (static hide/show + read-only cursor/supportsCursor/supportsNativeCursor); flash.filesystem (stage sixty-four) File (nativePath + read-only properties url (file:// form)/exists/isDirectory (getter, via stat()) + resolvePath/createDirectory/deleteFile/deleteDirectory + static read-only applicationStorageDirectory/applicationDirectory/desktopDirectory/documentsDirectory/userDirectory (the latter resolved at runtime via getenv("HOME")), POSIX stat/mkdir/remove)/FileStream (open/openAsync (async dispatch of ProgressEvent.PROGRESS + COMPLETE)/close/readUTFBytes/writeUTFBytes + read-only position/bytesAvailable, FILE* handle as opaque void* not GC-tracked)/FileMode (READ/WRITE/APPEND/UPDATE constants) |
| Builtins | trace(...); Array methods push/pop/shift/unshift/splice/slice/indexOf/join/concat, .length; String methods charAt/charCodeAt/indexOf/lastIndexOf/substring/substr/slice/split/match/search/replace/toUpperCase/toLowerCase/concat/localeCompare/valueOf/toLocaleLowerCase/toLocaleUpperCase/startsWith/endsWith, static String.fromCharCode, .length; Number/int/uint methods toFixed/toExponential/toPrecision/toString(radix)/valueOf, Number static constants MAX_VALUE/MIN_VALUE/NaN/POSITIVE_INFINITY/NEGATIVE_INFINITY, int/uint static constants MAX_VALUE/MIN_VALUE; Math constants PI/E/LN10/LN2/LOG10E/LOG2E/SQRT1_2/SQRT2 and methods abs/floor/ceil/round/sqrt/pow/min/max/random/sin/cos/tan/asin/acos/atan/atan2/exp/log; global functions parseInt/parseFloat/isNaN/isFinite; flash.utils.getTimer() (milliseconds since process startup, not a Unix timestamp, used for 60Hz frame-loop timing); flash.utils.setTimeout(closure, delay) / flash.utils.clearTimeout(id) (scheduling/canceling one-shot function callbacks, triggered by the frame loop after delay ms, returning a uint timer id; callbacks bound via function-value closure); flash.system.System memory statistics (totalMemory(uint)/totalMemoryNumber(Number)/freeMemory(Number)/privateMemory(Number) four purely static read-only properties: totalMemory is the runtime self-managed heap used bytes (returns 0 beyond 4 GiB), totalMemoryNumber same value unclamped, freeMemory is the arena allocated-but-unused gap, privateMemory is the real process resident memory (macOS mach_task_basic_info/Linux getrusage×1024/Windows GetProcessMemoryInfo/WASI degrades to totalMemory); flash.system.Capabilities (stage sixty-five): a final static read-only class (same pattern as System, no instantiable class) — version (a fixed AIR-compatible "<platform-prefix> 50,0,0,0", aligned with Adobe AIR's final major so Starling's version gate passes; the AS-AOT marker is carried by manufacturer), os (Mac OS/Windows/WASI/Linux via conditional compile), cpuArchitecture (ARM/x86), cpuAddressSize (32/64), supports64BitProcesses/supports32BitProcesses (derived from address size), playerType ("Desktop"), manufacturer ("AS-AOT"), isDebugger (false), touchscreenType ("none"), language (locale-derived ISO 639-1), screenResolutionX/screenResolutionY (SDL2 primary-display size, 0 in headless builds), screenDPI (72.0 fallback, no per-display DPI query), screenColor ("color"), pixelAspectRatio (1.0), hasAudio (true); type conversion String(x)/Number(x)/Boolean(x)/int(x)/uint(x); built-in constructor calls Array(...)/Object(...)/Vector.<T>(...) (also allowed without new, equivalent to new; Object(x) with an argument returns identity, boxing primitives as any); URI encoding/decoding encodeURI/decodeURI/encodeURIComponent/decodeURIComponent/escape/unescape; Boolean methods toString/valueOf; undefined global constant; object literals {} (string-keyed associative arrays); top-level JSON class stringify/parse (recursive serialization/parsing of objects, arrays, and primitives); exceptions throw/try/catch/finally (based on setjmp/longjmp); Date class (constructor overloads new Date()/new Date(ms)/new Date(year,month,day,...)/new Date(str), static Date.parse(str), accessors getTime/getFullYear/getMonth/getDate/getDay/getHours/getMinutes/getSeconds, formatting toDateString()/toUTCString()); RegExp class (regex literals /pattern/flags and new RegExp(pattern, flags): test/exec/source/global/ignoreCase/multiline/dotall/extended/lastIndex, supporting the five flags i/m/s/g/x; an embedded self-built ES3 backtracking regex VM: capturing/non-capturing groups, backreferences, lookahead, lazy/greedy quantifiers, character classes and \d\w\s escapes; String.match/search/replace regex variants: match returns the capture array or null, search returns the first match position, replace supports $1~$9/$&/$$ substitutions and function replacement repl:Function); Vector.<T> type-safe arrays push/pop/join/indexOf/slice/concat/splice/forEach/map/filter/sort/reverse, read/write .length, parameterized constructor new Vector.<T>(length, fixed), vector literal new <T>[...], indexed read/write with bounds checks; Array.sortOn(field, options) (sorting by object field, supporting Array.NUMERIC(16)/Array.DESCENDING(2) and `NUMERIC |
| Semantics | / is always Number (AS3 semantics); %'s zero divisor is defined per AS3 semantics (5 % 0 yields NaN, var r:int = 5 % 0 yields 0, INT_MIN % -1 yields 0), guarded by value via as_int_rem/as_uint_rem (C's integer % is UB on a zero divisor / INT_MIN % -1, and traps per spec under wasm); + auto-concatenates and boxes when encountering strings, and when a dynamic (*) operand is involved the runtime value decides (stage 89-34): emitBinary calls the as_add_v runtime helper (ES3 ToPrimitive — a String at runtime, or an object that stringifies, concatenates; otherwise the operands add), boxing the result as any. Measured: var a:Array=["x","y"]; a[0]+a[1] yields "xy" (was 0), var d:*="x"; d + 1 yields "x1" (was 1), null + 1 yields 1 (was a C compile error, (NULL + 1)). Known simplification: object/array boxes still render through as_v_str_val on the concatenation side, so [Array] + 1 gives [Array]1, not AS3's 1,21; trace/String(Number) output the IEEE-754 double's shortest 17-digit representation (trace(1/3) → 0.3333333333333333); Date.getTime() returns the real millisecond timestamp (gettimeofday); is/as perform runtime type checks via the vtable super chain; array elements are dynamic types (as_value boxing), heterogeneous elements handled automatically; exceptions jump via setjmp/longjmp to the nearest active catch, finally always executes; bitwise operations convert operands to 32-bit integers per AS3 semantics (>>> is unsigned right shift); dynamic index/property access on Object/any types goes through the reflection table (field offset + method table) onto real class instances, falling back to record slots on miss (obj[key]/obj.prop/obj[key] = v/obj.prop = v); conditional contexts (if/while/?:/&&/` |
| AS3 | C |
|---|---|
int |
int |
uint |
unsigned int |
Number |
double |
Boolean |
bool |
String |
char* (concatenation/boxing via as_str_* runtime helpers) |
Array |
as_array* (dynamic growth, heterogeneous elements boxed as as_value; element type is dynamic *) |
Vector.<T> |
as_vector_<T>* (monomorphized by element type T: contiguous element array T* data + length/capacity; out-of-bounds index throw RangeError; compile-time element type enforcement; push/pop/join/indexOf/slice/concat/splice/forEach/map/filter/sort/reverse, read/write .length, parameterized constructor new Vector.<T>(length, fixed), vector literal new <T>[...]) |
Function |
as_fn (a pointer to the closure record { thunk, env }; each function used as a value generates a thunk that unboxes the boxed argument list, calls the typed implementation, and boxes the result back into as_value; closures capturing outer locals additionally generate an environment struct + heap-allocated constructor, with env pointing at that environment) |
Object literal {} |
as_object* (string-keyed associative array, property values boxed as as_value) |
RegExp |
RegExp* ({ vtable; as_regex* compiled; source; flags; lastIndex; global/ignoreCase/multiline/dotall/extended }; compiled is the compiled artifact of the embedded backtracking VM, compiled at construction time, compile errors throw SyntaxError) |
Class Foo |
struct Foo + Foo*; first member is Foo_vtable* (vtable: super chain + method function pointers), fields laid out flat (parent-class fields first); methods compiled as Ret Foo_method(void* this, ...), invoked via obj->vtable->method(obj, ...); classes within namespaces use package-prefixed names (foo.bar.Baz → foo_bar_Baz) |
Interface I |
struct I (reference pair: void* obj + I_vtable* vt); method dispatch via ref.vt->method(ref.obj, ...); each implementing class generates an interface vtable instance Class_I_vt |
Error / exceptions |
struct Error (vtable + message); throw normalized to as_throw(Error*), exception state stored in the global as_exception, jump stack using setjmp/longjmp; TypeError/RangeError/ArgumentError are Error subclasses (same layout, independent vtables), catch (e:Type) uses as_is to match precisely along the super chain |
static members |
class-level fields/methods: file-scope globals Class_field / Class_method(...) (no this receiver) |
trace |
generates printf format strings by type; objects print as class names (as_obj_to_str reads the vtable's name) |
Before implementing new features, consult the authoritative specification to confirm real AS3 semantics, never reverse-engineering from memory or C behavior. The complete tiered list,
red-line comparisons, and decision divergences are in docs/en/as3-semantics.md. Core sources:
- ES4 draft specification (syntax/type system/coercion canon):
http://archives.ecma-international.org/2006/misc/es4lang-Jan06.pdf - AVM2 Overview (object model trait/slot/dispatch semantics, borrowing semantics only, not generating ABC):
http://hackipedia.org/raw/File%20formats/Containers/F4V,%20Flash%20Video/ActionScript%20Virtual%20Machine%202%20(AVM2)%20Overview%20by%20Adobe%20(2007-05).pdf - avmplus source (tie-breaker when the spec is ambiguous):
https://github.com/adobe/avmplus - AS3 language reference (standard library surface):
https://airsdk.dev/reference/actionscript/3.0/ - ECMA-262 3rd edition (source of numeric conversions
ToInt32/NaN/Infinity)
- Size optimization (stage seventy-eight): non-exported file-scope functions in the generated C (built-in class method bodies / thunks / free functions) are uniformly marked
static, soclang -O2builds a call graph frommainand dead-strips unreachable built-ins (hello.asbinary 165 KB → 34 KB);mainand[WasmExport]symbols stay global. Seedocs/zh-cn/size-optimization.md. - The regex engine is a self-built ES3 backtracking VM with byte/ASCII semantics (
\w/\d/\scover only ASCII, not UTF-8 Unicode-aware);exec/matchnon-global capture groups that don't participate returnundefined. - Under the WASI target,
Dateresolution degrades to second granularity (gettimeofdayis POSIX-specific; WASI degrades viatime(NULL), seeas_now_ms()). - Non-constant initializers for static fields (object literals
{},new X(), function valuesClassName.method, etc.), because C forbids non-constant static initialization, are executed atmain()entry in class declaration order (each evaluated in the declaring class's static context); instance field initializers execute aftersuper()within the constructor. throwis normalized toError(strings/primitives are wrapped asError);catch (e:Type)matches precisely along thesuperchain (TypeError/RangeError/ArgumentErrorimplemented, catchable by the basecatch (e:Error)).finallyexecutes on normal completion, thrown exceptions, and onreturn/break/continuejumping out of atryblock early (stage seventy-seven completes the early-exit stack unwind + finally re-run;try { return } finally {...}no longer skipsfinally).- Uncaught exceptions print to
stderrand exit with a non-zero code. - Bitwise operations convert operands to 32-bit integers (
int/uint), shift counts are taken modulo 32 per AS3 semantics;>>performs arithmetic right shift on signed integers. - Memory management (stage fifty-seven, precise GC): the runtime implements a self-built Mark-Sweep garbage collector —
gc_allocuses a segmented heap (1 MiB per segment) + free-list (first-fit + splitting + coalescing) + agc_headerthree-color bitfield before each object; the trigger point is theStage_dispatchFrameframe-boundary safe point (all frame callbacks have returned, leaving only permanent roots in the root set, so no shadow stack is needed), callinggc_step()to advance incrementally, plusSystem.gc()to manually triggergc_collect()for a stop-the-world collection. Root set = permanent roots (static fields, module variables,ASC_win_stage, ENTER_FRAME event registry, timers) + built-in root (as_exception). Marking dispatchesgc_scanby object type (array/object/Dictionary/closure/class instance/string), class instances mark via vtable→props reflection table along the super chain (tags 3/6/7 dispatchgc_mark_ptr/gc_mark_value); unreachable objects sweep back into the free-list for reuse. GC-2 has moved strings (concatenation, conversion,split/substring, etc.) into the GC heap (as_str_alloc), completely eliminating string arena leaks; only byte buffers (ByteArraygrow/compress/uncompress,BitmapData.pixels) still use arena/malloc; closure env andas_vectorhave moved into the GC heap (GCT_CUSTOMcallback marking +GCT_PTR_ARRAYbuffers for reference elements). GC-4 introduces incremental marking: a three-color state machine + explicit gray stack + Dijkstra write barriers (gc_write_barrier/gc_write_barrier_value), withgc_step()amortizing a fixed budget of mark/sweep slices per frame (gc_inc.budget, default 500), reducing single-frame pause from O(heap) to O(budget), eliminating the "heap growth → long freeze" linear degradation; new objects during marking start directly BLACK (gc_newside list, restored after sweep), write barriers injected into setters (as_array_set/as_object_set/as_dict_set/as_dyn_set), runtime constructors, and user direct field writes (o.field = v). GC-3 has completed WASI dual-target verification: under--target wasm(wasm32-wasip1), the four assertion examples of GC-1/2/4 (stage57/gc_strings/gc_incremental/gc_barrier) behave identically to native — reclamation back to zero, zero dangling in the object graph, bounded incremental-marking memory (as of stage 89-31 this acceptance could only be reproduced with examples whose exception machinery-O2eliminates: oncesetjmp/longjmplands on a reachable path the sysroot lacks__wasm_setjmp/__c_longjmpand the link fails; fixed in stage 89-35 — the wasm target now linkslibsetjmp.aand lowers to the standard EH proposal (try_table), and all four examples were re-run with results matching native; cross-target acceptance scripttemp/wasi-eh/check.sh, seedocs/en/compile.md§2).System.totalMemory/freeMemorycount GC heap used/total, and afterSystem.gc()returnstotalMemoryfalls back to baseline (examplesexamples/gc_strings.as: 100000 string concatenations reclaimed to zero;examples/gc_incremental.as: 800 frames of incremental reclamation with bounded memory;examples/gc_barrier.as: 10200 class-instance direct field writes, object chain complete with no dangling afterSystem.gc());privateMemoryis the real process RSS, comparable against Activity Monitor. flash.system.Capabilitiesis a static read-only subset:versionreturns an AIR-compatible"<platform-prefix> 50,0,0,0"(so Starling'sSystemUtilcan parse it),manufactureris"AS-AOT"(not Adobe),isDebuggeris alwaysfalse, andscreenResolutionX/screenResolutionY/screenDPIreport0/72in headless builds (no SDL2 window backend). Deferred (platform/backend dependent):languages,serverString,hasMP3/hasTLS/hasVideoEncoder/hasEmbeddedVideo/hasStreamingAudio/hasStreamingVideo,maxLevelIDC,hasAccessibility/hasIME/avHardwareDisable/localFileReadDisable/isEmbeddedInAcrobat, andhasMultiChannelAudio(type).- Namespaces are implemented as C identifier prefixes (
foo.bar.Baz→foo_bar_Baz), classes inpackageandimportresolve across files; short-name → fully-qualified-name uses a global unique mapping, same-name classes in different packages are not yet supported (later definitions override earlier ones);dynamic/abstractand other class modifiers are parsed and ignored. - Closures use snapshot capture: anonymous functions capture the current values of outer locals into a heap-allocated environment (surviving after the closure escapes), but modifications to captured variables inside the closure do not affect the outer local (full reference semantics/hoisting deferred). Multi-level nested closure capture is not yet supported.
- Function values (
Function) are invoked under the unifiedas_fnconvention; when a function used as a value has default or rest parameters, the full argument list must be passed at the call site (defaults/rest are only filled at the call site for directly typed calls). Arrayelements are dynamic types (as_valueboxing); multidimensional arrays are naturally supported via nestedas_v_objpointers, but out-of-bounds reads returnnull(AS3 should beundefined; this subset approximates withnull).- Object literals
{}are a simplified string-keyed associative array (not landing on theObjectroot class's dynamic properties, still mapped toas_object*);o.xreading a nonexistent key returnsnull. new Object()/Object()and object literals{}map identically toas_object*(recorddynamic associative arrays), not vtable class instances.Object(x)with an argument returns the original value identically for object types, boxing primitives (int/uint/Number/Boolean/String) asas_valuereturningany— this project does not modelNumber/String/Booleanwrapper classes, so they cannot masquerade asObjectpointers.is/ason interface types use compile-time static type checks (based on the implementing class'simplementslist), not crossing the runtime dynamic-dispatch boundary.is/ason primitive types (int/uint/Number/Boolean/String) are supported: static scalars are evaluated at compile time;*(anyboxing) is determined at runtime by theas_valuetag (butint/uint/Numberare not distinguished when boxed, all mapping to the number tag, as noted in the README).switchwith anint/uintdiscriminant generates a native Cswitch(supporting fall-through andbreak); other types (such asString) degrade to anif/elsestrict-equality chain (no fall-through).casevalues must be constants.var x;(no type, no initializer) defaults toint; a variable's type never changes once inferred. Assigning reference types (String/objects/arrays etc.) implicitly toint/uint/Number/Booleanscalars throws aCodegenError(no longer silently truncating to garbage); explicitint(x)/Number(x)etc. conversion functions must still be called explicitly.- The rendering backend (
Shape/Graphics/Bitmap/TextFieldreal rasterization) links Skia via the C++ glue layervendor/skia_glue.cc(extern "C"bridging the generated C). Skia is a C++17 library with no C API; this project uses Aseprite precompiled static libraries (m124) located atvendor/skia/(include/+lib/, with all transitive dependencies), enabled via the manifestdefines: ["ASC_USE_SKIA=1"](seedocs/en/skia.md§6.1); without Skia linked,Stage.render()degrades to a no-op,TextField.textWidthreturns 0 (pure C stub), and pure-C paths likeBitmapData.getPixel/setPixelwork as usual. The first backend is off-screen CPU raster (→ PNG);beginGradientFillcurrently supports only two-color linear gradients, with multi-stop/matrix/multiplebeginFillgroups as later sub-stages (seedocs/en/skia.md).TextFieldnow supports full layout (SkParagraph,modules/skparagraph): HarfBuzz shaping + UAX#14 breaking + real line height (getHeight()/getLongestLine()), withwordWrap/multiline/scrollV/maxScrollV/numLines/textWidth/textHeightall present, the layout cached on the object (re-laid out only when text/width/style change, auto-relaying out on width change);DisplayObject.cacheAsBitmapbakes the subtree to an off-screen surface and snapshots it into anSkImagedrawn once per frame (no more per-object recursive draw commands), toggle invalidates and re-bakes, pixel-identical to direct recursive rendering; incremental redraw (stage sixty-eight, auto cacheAsBitmap): sinceDisplayObjectx/y/rotation/scaleX/scaleY/alpha/visible are plain fields written directly (no setter), a push dirty flag is not used — instead the render entry recomputes a recursive content fingerprint of the subtree each frame (own transform + type-specific contentSkPathgeneration id /Bitmapimage /TextFieldtext+format + filter fields + recursive child fingerprints), and a subtree whose fingerprint is unchanged forASC_AUTO_BAKE_FRAMES(=3) consecutive frames is considered static and auto-baked into_cache_image(reusing the cacheAsBitmap path), so subsequent frames emit a singledrawImageinstead of re-walking the subtree; the instant the fingerprint moves the bake is dropped and re-baked. The fingerprint is pure integer/pointer FNV-1a mixing, far cheaper than Skia command generation — a render-side dirty detection rather than setter interception, matching cacheAsBitmap semantics (intra-subtree changes are not tracked until the fingerprint differs);clipclipping + viewport scrolling (scrollV = maxScrollVpins to bottom).autoSize/hscroll/selectable/HTML text and multipleTextFormatrange rich text remain unimplemented (exampleexamples/textflow.as). Windowed backend:Stage.showWindow(w, h, title)uploads off-screen surface pixels throughvendor/window_glue.cconto an SDL2 window and enters the event loop (defines: ["ASC_USE_WINDOW=1"], linking arm64libSDL2.a, seedocs/en/compile.md§6); mouse input is bridged back to the AS3 event system through function-pointer callbacks (Stage_dispatchMousehit test + bubble + re-render after events, exampleexamples/window_click.as); withoutASC_USE_WINDOWdefined,showWindowdegrades to a no-op. Retina high-DPI:defines: ["ASC_DISPLAY_HIGH=1"]enables SDLALLOW_HIGHDPIand creates a physical-pixel surface at the device scale factor (Stage.contentsScaleFactorreturns the measured factor), otherwise the surface is stretched 2x by the compositor causing blurry text; when the window is dragged onto a monitor with a different scale, the glue layer reads authoritative physical pixels viaSDL_GetWindowSizeInPixels, computes the factor from physical/logical size, and passes it down to the resize callback to rebuild the surface, skipping the resize when any dimension is ≤0 (otherwise the content enlarges/crops or the stage size zeroes). Frame rate follows the display refresh (VSync):SDL_RenderSetVSyncenables vertical sync, soSDL_RenderPresentblocks to the display refresh cycle and yields uniform frame intervals — a 50 Hz external display presents 50 fps, a 120 Hz main display 120 fps, with delta-time keeping animation speed correct; this is the only physically correct way to look smooth on a 50 Hz panel (chasing 120 fps with VSync off produces 120-vs-50 beat jitter and stutter). Window scaling control and mouse wheel (stage forty-five):scaleMode/alignmap to real canvas transforms (noScalekeeps content unscaled,showAll/noBorderuniform scaling,exactFitnon-uniform; align's eight values distribute remaining space), windowresizerebuilds the physical-pixel surface and re-renders viaSDL_WINDOWEVENT_SIZE_CHANGED(no more stretching distortion), undernoScalestageWidth/Heightfollow the real window size and dispatchEvent.RESIZE; mouse wheel viaSDL_MOUSEWHEEL→Stage_dispatchWheelauto-scrolls TextFields (adl-verified 1 delta = 1 line,scrollV -= delta, clamped to[1,maxScrollV]) and dispatches bubblingMouseEvent.MOUSE_WHEEL(exampleexamples/wheel.as). Frame loop and ENTER_FRAME (stage forty-six): the event loop callson_frame→Stage_dispatchFrameeach frame, broadcastingEvent.ENTER_FRAME(non-bubbling, dispatched to every registered listener via the global registry — including objects not on the display list, such as GreenSock's private staticShapedriving tweens; the event object is reused across objects with zero allocation), driving FPS counters and other frame-driven logic (exampleexamples/frame.as). Stagestage properties (stage forty-one) map to field storage + getters/setters:stageWidth/stageHeight/fullScreenWidth/fullScreenHeight/displayState/quality/color/align/scaleMode/frameRateand boolean switches are readable/writable/read-back;coloractually maps to the window background color (sk_canvas_clear),displayState = FULL_SCREENgoes full-screen viaSDL_SetWindowFullscreenwhenshowWindow,fullScreenWidth/Heightreturn the display resolution viaSDL_GetCurrentDisplayMode(logical points on macOS).frameRatenow maps to event-loop frame pacing (stage forty-seven): after setting, the frame loop drivesENTER_FRAMEevery1000/frameRatems, capped to the current display's refresh rate when the explicit rate is higher (avoiding 120-vs-50 beat jitter); when unset (<=0) it reads the display refresh rate (vsync cadence), falling back to 120Hz if unreadable;quality's actual anti-aliasing effect remains deferred;align/scaleModeand windowresizeare implemented (stage forty-five, see below).- The
--air-app <app.xml>command (stages forty-two~forty-four) parses the AIR application descriptor (src/air-app.tshand-written minimal XML parser), auto-generating bootstrap code + a build manifest (recursively scanningsrc/**/*.as+ linking Skia/SDL2), combined with--main-class(default inferred by scanningsrc/**/Main.as) to compile and link a windowed executable in one command; aligns with adl behavior:<resizable>false</resizable>→ASC_WINDOW_FIXED=1fixed-size window,<requestedDisplayResolution>high</requestedDisplayResolution>→ASC_DISPLAY_HIGH=1Retina native-resolution rendering, bootstrap code presetsstageWidth/stageHeightbeforenew Main()(sotracein the constructor returns window dimensions rather than0 0). - In
flash.geom(stage fifty-eight),Point/Rectangle/Matrix/ColorTransformare puredoublefield bundles (no GC pointers);TransformholdsMatrix/ColorTransformreferences and participates in GC marking.Transformis wired toDisplayObject.transform(stage sixty-seven): when rendering a display object,sk_canvas_concatappliestransform.matrixto the Skia canvas transform (a/c/tx/b/d/tymapped toSkMatrix; concat is safe even for identity); AS3 identifiers that collide with C keywords or libc/libm/POSIX symbols (class/method/field/local/parameter names) get a leading_prefixed in C identifiers — incrementally until collision-free (union→_union,index→_index) — while the AS3-side spelling is preserved unchanged in the runtime reflection tables (seedocs/zh-cn/c-naming.md, stage seventy-six). - In
flash.filters(stage sixty-one), filter classes are pure field bundles (BitmapFilterabstract base class +BlurFilter/DropShadowFilter/GlowFilter+BitmapFilterQualityconstant class),clone()returns a new instance copying all fields; thealphafield is quantized to 8-bit fixed-point per real AIR asfloor(alpha*255)/255(0.75→191/255≈0.74902);DisplayObject.filtersis an ordinaryArrayfor read/write, and therender()backend now appliesBlurFilter/DropShadowFilter/outer-glowGlowFilterto the display-object subtree via Skia image filters (blur/shadow/glow genuinely change on-screen pixels,sigma ≈ blurX/3approximating box-blur diameter; advanced parameters such asinner/knockout/hideObjectare not yet distinguished);BitmapData.applyFilterimplements onlyBlurFiltervia separable box blur (qualitycontrols box-blur rounds), whileDropShadowFilter/GlowFilterapplyFilterrasterization remains deferred (calling throwsError). flash.displaycompletion (stage sixty-two) is a minimal model with no timeline symbols and no async loading:MovieClip'stotalFramesis read-only in AIR (determined by the symbol timeline), but this subset has no symbol system, so it is modeled as a writable field (default 1, setter validates ≥1) to simulate multi-frame timelines;currentFramedefaults to 0 (real AIR's frameless MovieClip semantics), frame advancement is driven by theas_mc_*frame pool (off-screen usestickMovieClips()to pump manually, windowed builds are frame-loop driven), wrapping to 1 aftertotalFrames(looping timeline).SimpleButtononly models the four state references (upState/overState/downState/hitTestState), with mouse hover/press visual state switching and hit-test redirection still deferred (itself hit as a leaf by bounds).Loader.load(url)records the URL tocontentLoaderInfo, dispatching INIT synchronously and COMPLETE asynchronously (as_set_timeout(0)next frame tick, the same contract asURLLoader, so listeners registered afterload()still receive events);Loader.contentremainsnull(image decoding not implemented).flash.net/flash.ui(stage sixty-three) is a minimal model with no network/audio/video backends:URLLoader.load(request)treats the URL as a local filesystem path for whole-file reads (POSIXfopen/fread, also usable under WASI), but dispatches COMPLETE/IO_ERROR asynchronously (as_set_timeout(0)next frame tick, so listeners registered afterload()still receive events);dataholds the file text (as_str_allocGC-tracked string, no longermalloc); real async HTTP/Socket loading is not implemented.URLVariablesis implemented as adynamic class(undeclared string-keyed properties stored in a runtime slot table,toString()serializes to a query string);Socket,Sound/SoundChannel/Video,ContextMenuare all deferred (depending on network sockets, audio/video decoding, and native menu backends respectively).Keyboardmaps only the key-code constant subset (letters/digits/common control keys/arrow keys);Mouse.cursoris read-only ("auto", the real AIR default), the SDL cursor API is not wired, andhide/showonly toggle an internal visibility flag.flash.filesystem(stage sixty-four) is a POSIX file IO minimal model:Filetreats the path as an ordinary string bundle (nativePath+ read-onlyurl=file://prefix),exists/isDirectoryare read-only properties (getters,f.existsrather thanf.exists()) viastat(),createDirectoryviamkdir(supports recursive creation),deleteFile/deleteDirectoryviaremove()(empty directory); the static read-onlyapplicationStorageDirectoryreturns the application's writable per-app storage directory (~/Library/Application Support/as3aot/Local Storeon macOS,~/.as3aotelsewhere, created on demand — a bundled.applaunches with a non-writable CWD so.is not a safe write target),applicationDirectory(mapped to CWD"."),desktopDirectory/documentsDirectory/userDirectory(getenv("HOME")joined withDesktop/Documents/bare HOME, resolved at runtime).FileStreamholds aFILE*handle (opaquevoid*field, not GC-tracked),openmaps AIRFileMode(read/write/append/update) to Cfopenmodes,openAsyncdispatchesProgressEvent.PROGRESS(bytesLoaded=bytesTotal=file size) +Event.COMPLETEasynchronously on top of the same open;readUTFBytesreturns anas_str_allocGC-tracked buffer (no longermalloc).position/bytesAvailableare read-only (ftell/fseek).NativeWindow/Window(native multi-window) andSQLConnection/SQLStatement(SQLite linking) are all deferred.- Browser target (
--target wasm --package web, stage seventy-one): uses Emscripten to compile Skia + the C++ glue layer into browser artifacts (.wasm+.js+index.html), running CPU rasterization +emscripten_set_main_loopframe loop inside<canvas>; the window layer switches from SDL2 to canvas blit, and the font backend switches from CoreText toSkFontMgr_New_Custom_Data()+ runtimefetchinjection (font-urls). Known limitations: fonts load from the network at runtime (offline/intranet unavailable), translucent pixels have color drift when blitted to canvas as premultiplied alpha (opaque pixels are exact), and whole-CJK fonts load slowly on first render; seedocs/zh-cn/html5-web.md. Stage3D (stage eighty-nine / thirty): a program whose entire rendering goes throughContext3D(e.g.examples/air-starling-demo, 142 AS3 sources) renders correctly on web by linkingvendor/stage3d_webgl.ccinstead (WebGL2/GLES3 + AGAL→GLSL ES,ASC_RENDER_STAGE3D=1+ASC_S3D_GLSL=1) — thes3d_*signatures are identical to the native Metal backend, so the generated C needs no change (the GL-side Y-flip, BGRA swap, per-registeruniform vec4 vcN/fcN,DEPTH24_STENCIL8offscreen FBOs and the single WebGL2 context shared with Ganesh are all handled inside the glue); all 12 scenes were entered one by one and match the AIRadlreference (120 fps / 0 discards / 2.88 ms per frame). Stage3D presentation is now GPU-direct (stage eighty-nine / thirty-one):sk_gl_draw_texturewraps the render target's GL texture into aGrBackendTextureand draws it straight into the canvas (the counterpart of Metal'sas_skia_mtl_draw_texture), droppingrenderMsfrom 3.21 ms to 0.12–0.15 ms per frame (≈23×), pixel-identical to the readback path (readback is kept as the fallback whenASC_RENDER_GPUis undefined); seedocs/zh-cn/html5-web.md§3.3/§6. AGAL compare instructions (stage eighty-nine / thirty-two): the full-mask form ofsge/slt/seq/snetranslates on the GLSL side tomix(vec4(0.0), vec4(1.0), vec4(greaterThanEqual(a, b)))(mix(genType,genType,genType)is a legal ES 1.00 overload andvec4(bvec4)is allowed by §5.4.2; thebvecNoverload ofmixis ES 3.00-only). Both targets used to share onesprintfwith a ternary format string, and since the two formats consume a different number of varargs while the argument list is fixed, GLSL came out asv0(greaterThanEqual(v0, vec4))— clicking "Switch Filter" in the web Filters scene (Drop Shadow/Glow) failed fragment compilation (native was unaffected because its branch happens to use only the first four arguments). Each target now has its ownsprintf, and all four opcodes work on both ends (examples/stage80.asasserts the exact form on each side). Frame driver and pacing (stage eighty-nine / twenty-nine): the web frame is driven byrequestAnimationFrame, and one rAF callback is one vsync; the pacer therefore maps the target interval onto whole ticks (skip = round(1000/frameRate / rAF_period), i.e. present a frame everyskip-th vsync), with the rAF period measured as the median of consecutive callback intervals (there is no browser analogue ofSDL_GetDisplayMode, sosk_window_get_display_refresh()returns 0 on web). The previous timestamp-deadline pacer halved the rate under rAF timestamp jitter (measured on a 120 Hz panel: p50 8.30 / p95 9.30 ms for an 8.333 ms period) — rAF delivered 120 callbacks/s while only 66 fps were presented, discarding 54.5 callbacks/s (this is the reported "web FPS cannot reach 120"). After the fix, the 120 Hz panel measures 120 fps / 0 discards / 0.113 ms per frame (1.35% of the frame budget, GPU rasterization) with the demo's own corner readout showingFPS:120; the same build on CPU rasterization (putImageData) costs 6.66 ms/frame (80% of the budget) — GPU rasterization is necessary (the existing WebGL2/Ganesh backend suffices) while WebGPU offers no gain and is not even built in (the wasm Skia usesskia_use_webgpu=false/skia_enable_graphite=false). The hard ceiling is the refresh rate of the panel the window sits on (rAF follows the window's display; on a 50 Hz external panel no backend can exceed 50 fps). Frame-rate diagnostics on web use the compile-time define-D ASC_FRAME_STATS=1(compiled out by default), which publishesframes/loopcalls/skips/renderMs/rafPeriodMs/skiptowindow.__ascFrameStatsonce a second.
examples/hello.as— expressions, string concatenation,trace,if/elseexamples/fib.as— functions,while,for, recursion boundaryexamples/class.as— classes, fields, methods,new,thisexamples/features.as— division/modulo, comparison, logic, increment, string equalityexamples/syntax.as—uint, hexadecimal,do-while,switch, ternary,break/continue,Infinity/NaNexamples/ctor.as— parameterized constructors,newwith argumentsexamples/inherit.as— inheritanceextends, field/method inheritanceexamples/override.as—overridemethod overriding, polymorphic dynamic dispatchexamples/super.as— constructorsuper(...), methodsuper.method(...)examples/visibility.as— access modifierspublic/private/protectedexamples/isas.as—is/astype checks and castsexamples/array.as— array literals/indexing/push/pop/splice/for-in/multidimensional arrays/heterogeneous elementsexamples/object.as— object literals{}(associative arrays), property read/write, nested objectsexamples/any-add.as— dynamic+(one side*): runtime-decided concatenation vs. numeric add,nulloperands, and+=reusing the same path (stage 89-34)examples/any-dot-write.as— dot writes through a dynamically-typed (*) receiver, incl. compound writes: reflectable fields land on the real field, an unknown property on a sealed instance neither crashes nor corrupts, record slots are added, and an array's element buffer is not clobbered (assertion regression for the stage 89-33 crash fix)examples/stdlib.as—Stringmethods,Math, global functions, type conversion, readable objecttraceexamples/oop2.as—const/staticfields and methods/get/setaccessorsexamples/interface.as—interface/implements/interfaceis/as/interface method dispatchexamples/stage4.as— stage four synthesis: interfaces + static members + accessors +final+Object.toString+NumberdefaultNaNexamples/stage5.as— stage five synthesis: default parameters, rest parameters,Functionvalues and anonymous functionsexamples/stage6.as— stage six synthesis:package/importsyntax compatibility layer (namespace flattening)examples/stage7.as— stage seven synthesis:throw/try/catch/finally, built-inError, exception rethrow and nestingexamples/stage8.as— stage eight synthesis: bitwise& | ^ ~ << >> >>>and compound assignment, labeled statementsbreak label/continue labelexamples/stage9.as— stage nine synthesis:ErrorsubclassesTypeError/RangeError/ArgumentErrorprecisecatchand bubbling,Dateclass calendar accessorsexamples/stage12/— stage twelve synthesis (multi-file):package foocross-file class definitions,importthen instantiate and call, same-packageinternalvisibilityexamples/stage10.as— stage ten synthesis:Vector.<T>type-safe arrays (push/pop/indexed read/write/.length) and out-of-boundsRangeErrorexamples/stage11.as— stage eleven synthesis: closures (capturing outer locals, escaping, independent counting, parameterized closures)examples/stage13.as— stage thirteen synthesis:nullboxing semantics (string concatenation/comparison of object null), string arena allocation stress testexamples/stage14.as— stage fourteen synthesis:trace/String(Number)17-digit double shortest representation,Date.getTime()millisecond clockexamples/stage15.as— stage fifteen synthesis: module-levelconst/varvisible to free functions (file-level scope), untyped-variable truncation protection, module-level variable ordered initializationexamples/stage16.as— stage sixteen synthesis:String.replace(string variant, replaces first),Number.toFixed/toExponential/toPrecision(including exponent leading-zero stripping)examples/stage17.as— stage seventeen synthesis:Vector.<T>join/indexOf/.lengthassignment (shrinking)/parameterized constructornew Vector.<int>(3, true)examples/stage18.as— stage eighteen synthesis: primitive-typeis/as(static scalar compile-time evaluation +anyruntime tag determination)examples/stage19.as— stage nineteen synthesis:Array/Vectorjoin optimization (memcpy pointer advancement), intentionally simplified items verified (closure snapshots, array out-of-bounds null)examples/stage21.as— stage twenty-one synthesis:Number/int/uinttoString(radix)(base 2~36),valueOf,MAX_VALUE/MIN_VALUE/NaN/infinity static constantsexamples/stage22.as— stage twenty-two synthesis:Stringconcat/fromCharCode/localeCompare/valueOf/toLocaleLowerCase/toLocaleUpperCase/startsWith/endsWithexamples/stage23.as— stage twenty-three synthesis:Boolean.toString/valueOf,undefinedconstant, URI encoding/decodingencodeURI/decodeURI/encodeURIComponent/decodeURIComponent/escape/unescapeexamples/regexp.as— stages twenty-four~twenty-seven synthesis: regex literals/new RegExp,test/exec/capture groups/lastIndexglobal state machine,String.match/search/replaceregex variants ($1/$&/repl:Function), character classes/quantifiers/backreferences/lookahead,i/m/s/xflags, invalid regex throwingSyntaxErrorexamples/stage28.as— stage twenty-eight synthesis:Array()/Object()/Vector()constructor forms (new Array()/new Object()construction + function-style calls withoutnew,Object(x)with-argument identity/boxing semantics)examples/stage33.as— stage thirty-three synthesis:Event/EventDispatcherthree-phase event dispatch (capture→target→bubble,stopPropagation/stopImmediatePropagation/clone)examples/stage34.as— stage thirty-four synthesis: display listDisplayObject/DisplayObjectContainer/Stage/Sprite(addChild/removeChild/getChildAt/getChildByName/contains/setChildIndex/root/stage)examples/stage35.as— stage thirty-five synthesis: interaction events and hit testing (inside-out dispatch,mouseChildren=falseparent absorption,visible=falseskipping,MouseEvent/KeyboardEvent/FocusEvent)examples/stage36.as— stage thirty-six synthesis: minimal Skia closed loop (oneShape→Stage.renderoff-screen PNG, no-op without Skia)examples/stage37.as— stage thirty-seven synthesis:Shape/Graphics(beginFill/beginGradientFill/drawRect/drawCircle),Bitmap/BitmapData(getPixel/setPixel), nestedSprite+rotation/alpharecursive renderingexamples/stage38.as— stage thirty-eight synthesis:TextField/TextFormat(SkParagraph text rendering,textWidth/textHeight)examples/air-native/— multi-file synthesis (boot.as+src/demo/*.as): Air-style document classMain(Spritesubclass) + implicitsuper()/addedToStageevent-driven +Date/Array/ByteArray/JSONfour demos + stage fifty-eight~sixty-four test cases (Geometry/Events/Timer/FilterDisplay/NetUi/Filesix*Demos.run()assertions +FilterDisplayDemos.visualize()stage filter visualization) +TweenDemo(GreenSockTweenLitetween animation, drivingShapemovement via ENTER_FRAME in window mode), linking Skia to outputair-native.pngexamples/window.as— stage thirty-nine synthesis:Stage.showWindowwindowing (Shapeblue rectangle +TextFieldtext → off-screen Skia → SDL2 upload + event loop), usingexamples/window.build.example.jsonto link Skia + arm64 SDL2examples/window_click.as— stage forty: real mouse clicks in the window → AS3 event dispatch (mouseDown/mouseUp/click+ bubbling + click color-change re-render), usingexamples/window_click.build.example.jsonexamples/web/hello-web.as— stage seventy-one: browser HTML5 rendering (--target wasm --package web,Shapeblue rectangle +TextFieldtext → canvas blit +emscripten_set_main_loop, runtime font injection viafont-urls), usingexamples/web/hello-web.build.jsonexamples/textflow.as— stage thirty-eight: TextField layout assertions (numLines/maxScrollV/scrollVviewport math,wordWrapwrapping,appendText/textHeight), valid under pure C / Skia buildsexamples/wheel.as— stage forty-five: mouse wheel semantics (1 delta = 1 line,scrollV -= delta, clamped[1,maxScrollV], no dispatch on miss), pure C regressionexamples/frame.as— stage forty-six: ENTER_FRAME broadcast semantics (stage/child/grandchild depth-first dispatch), pure C regressionexamples/stage41.as— stage forty-one synthesis:Stagestage property read-back (stageWidth/stageHeight/color/quality/align/scaleMode/displayState/frameRate/boolean switches) + four constant classes, off-screen (included in regression)examples/stage67.as— stage sixty-seven:DisplayObject.cacheAsBitmapsubtree bitmap caching (default false, get/set readback, inheritance acrossShape/TextField/Stagesubclasses, toggle-invalidation safety), off-screen (included in regression)examples/cacheasbitmap.as—cacheAsBitmaprender verification (offscreen + manifest: a container subtree (rect + circle) rendered to a PNG once withcacheAsBitmapon and once off, byte-identical output; the bake is pixel-identical to direct recursive rendering), requiresexamples/cacheasbitmap.build.jsonto link Skiaexamples/autobake.as— incremental-redraw auto cacheAsBitmap verification (offscreen + manifest: a static subtree rendered across several frames triggers fingerprint-driven auto-baking, f4/f5 pixel-identical to the direct f1; moving the subtree invalidates and re-bakes, moved/f8 identical and distinct from f1), requiresexamples/autobake.build.jsonto link Skia (off-screen, included in regression)examples/stage81.as— stage eighty-one:Stage3D+Context3Dskeleton (Stage.stage3Dssingle slot +requestContext3Dlazy creation + synchronousCONTEXT3D_CREATEdispatch +Context3Dstate machine (blend/depth/cull/program/vertex streams/textures/matrix constants) full-path calls + 15 constant class value assertions), off-screen (included in regression)examples/stage80.as— stage eighty: AGAL bytecode kernel (hand-written vertex+fragment AGAL1 bytecode per the official AGALMiniAssembler encoding, asserting the MSL/GLSL translation contains the expected instructions, register mapping and swizzle/write-mask expansion, the full-mask form of the compare instructions (GLSLmix(vec4(0.0), vec4(1.0), vec4(greaterThanEqual(a, b)))/ MSLselect(float4(0.0), float4(1.0), a >= b)) and the partial-mask component-ternary form, and that an invalid opcode throws), off-screen (included in regression)examples/stage79.as— stage seventy-nine: Stage3D geometric prerequisites (Vector3Dfour-component/geometric ops/static axis constants +Matrix3Dcolumn-major matrix multiply/invert/transpose/transform-vector/rotation-scale-translation constructors +decompose/recomposeround-trip assertions), off-screen (included in regression)examples/stage66.as— stage sixty-six:Vector.<T>higher-order/sequence methods (slice/concat/splice/forEach/map/filter/sort/reverse) and thenew <T>[...]literal, off-screen (included in regression)examples/stage65.as— stage sixty-five:flash.system.Capabilitiesenvironment query (version(fixed AIR-compatible"50,0,0,0"),os/cpuArchitectureconditional-compile constants,cpuAddressSize+supports64BitProcesses/supports32BitProcesses, fixed desktop valuesplayerType/manufacturer/isDebugger/touchscreenType/screenColor/pixelAspectRatio/hasAudio, locale-derivedlanguage,screenResolutionX/screenResolutionY/screenDPI), off-screen (included in regression)examples/stage64.as— stage sixty-four:flash.filesystem(Filepath bundle +exists/isDirectory/resolvePath/createDirectory/deleteFile/deleteDirectory+ static directoriesapplicationDirectory/userDirectory/documentsDirectory/desktopDirectory,FileStreamopen/openAsync(asyncPROGRESS+COMPLETE)/close/readUTFBytes/writeUTFBytesread/write roundtrip + read-onlyposition/bytesAvailable,FileModeconstants), off-screen (included in regression)examples/stage63.as— stage sixty-three:flash.net/flash.ui(URLRequestvalue bundle + defaultmethod,URLLoaderasynchronous local file read (pumped viatickTimers()) success/failure dispatchingCOMPLETE/IO_ERROR,URLVariablesdynamic properties +toString()serialization,Keyboardkey-code constant subset +isAccessible,Mousestatichide/show+ read-onlycursor), off-screen (included in regression)examples/stage62.as— stage sixty-two:flash.displaycompletion (MovieClipframe timelineplay/stop/gotoAndPlay/gotoAndStop+ wrapping,SimpleButtonfour states,Loader/LoaderInfoasyncload(INIT sync/COMPLETE next frame) + constants), off-screen (included in regression)examples/stage61.as— stage sixty-one:flash.filtersfilters (BitmapFilter/BlurFilter/DropShadowFilter/GlowFilterdefaults/custom construction/clone/ischecks,BitmapFilterQualityconstants,DisplayObject.filtersread/write roundtrip,BitmapData.applyFilterbox blur and deferred rasterization throwing), off-screen (included in regression)examples/stage60.as— stage sixty:flash.utils.Timer(repeated triggering/TIMER_COMPLETE/stop/reset/delaysetter validation +repeatCountint truncation), off-screen (included in regression)examples/stage59.as— stage fifty-nine:flash.eventsevent subclasses (TimerEvent/ProgressEvent/ErrorEvent/IOErrorEvent/DataEvent+ fullEventconstant assertions), off-screen (included in regression)examples/stage58.as— stage fifty-eight:flash.geom2D geometry (Point/Rectangle/Matrix/ColorTransform/Transformassertions +DisplayObject.transformmatrix read-back and render wiring), off-screen (included in regression)examples/stage57.as— stage fifty-seven synthesis: precise GC leak assertion (after 50000 temporaryrecord+arrayobjects,System.gc()reclaims to zero,totalMemorydoes not grow linearly), off-screen (included in regression)examples/gc_strings.as— stage fifty-seven GC-2: strings in the GC heap leak assertion (after 100000 string concatenations,System.gc()reclaims to zero), off-screen (included in regression)examples/gc_incremental.as— stage fifty-seven GC-4: incremental marking bounded-memory assertion (800 frames each allocating temporaries,gc_step()reclaims slices per frame, heap does not grow linearly with frame count), off-screen (included in regression)examples/gc_barrier.as— stage fifty-seven GC-4: write-barrier stress assertion (200 class-instance chains, 10200 nodes after direct field writes, object graph complete with no dangling afterSystem.gc()), off-screen (included in regression)
Each example, after compilation, leaves a same-named .c file beside it so you can directly read the compiler-generated C code.
Run node test.ts (or npm test) to compile and run all examples under examples/, reporting pass/fail item by item, exiting with a non-zero code on any failure.
This project stands on the shoulders of the following projects and third-party libraries, for which we are grateful:
- TypePHP (official site) — an AOT compiler that compiles PHP into native binaries. The overall approach of "the compiler frontend only translates, delegating optimization and machine-code generation to mature C compilers — no reinventing the wheel", along with the build manifest (
project.yml) and multi-target backend design, is borrowed from it. - Ruffle (official site) — the open-source Rust reimplementation of Adobe Flash. Its
flash.events.*event flow,flash.display.*display list, and interactive hit-testing implementations for AS3/AVM2 are the authoritative reference for this project's GUI/event-system semantics. - Skia (official site) — the 2D graphics library and rendering backend. This project links Skia for rasterization via the C++ glue layer (using the Aseprite precompiled static libraries m124).
- SDL (official site) — the cross-platform window/input library, the windowed backend (
Stage.showWindow) uploads and runs its event loop via SDL2.