Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
15564e9
Parse and preserve parameter decorators
jtenner Mar 27, 2026
75d7c6a
Reject surviving parameter decorators after transforms
jtenner Mar 27, 2026
65527dd
Cover transform-time removal of parameter decorators
jtenner Mar 27, 2026
32f69b2
Clarify transform-only parameter decorator contract
jtenner Mar 27, 2026
21f724f
Document transform hook timing for parameter decorators
jtenner Mar 27, 2026
c474861
Fix lint for parameter decorator fixtures
jtenner Mar 27, 2026
ec618c9
chore: ignore AS parameter decorator fixture in eslint
jtenner Apr 5, 2026
4505b5c
Fix parameter decorator review feedback
jtenner Apr 8, 2026
c267d6e
chore: remove legacy eslint config
jtenner Apr 11, 2026
6f75b5e
refactor: consolidate parameter decorator validation
jtenner Apr 13, 2026
53dc57c
chore: remove unused AST imports
jtenner Apr 13, 2026
bcbce9a
style: move parameter range to the end
jtenner Apr 13, 2026
ba37601
style: include explicit this decorators in function types
jtenner Apr 13, 2026
b1fa8aa
refactor: reuse decorator serialization for parameters
jtenner Apr 13, 2026
2a7f4fb
refactor: rename tryParseParameterDecorators helper
jtenner Apr 14, 2026
e401a30
Refactor parameter decorator parsing
jtenner Apr 15, 2026
d1ce4c7
Update NOTICE
PaperPrototype Apr 15, 2026
2ca7c6a
Update NOTICE
PaperPrototype Apr 15, 2026
93f7b31
undo unecessary comment changes
PaperPrototype Apr 15, 2026
9757397
Undo changes to index-wasm.ts
PaperPrototype Apr 16, 2026
2177f77
removed currentSourceStatementDepth and currentSourceStatementHasPara…
PaperPrototype Apr 16, 2026
9b2dc22
Remove NodeWalker and ParameterDecoratorValidator and validateParamet…
PaperPrototype Apr 16, 2026
9f9f3bd
Remove NodeWalker from ast.ts
PaperPrototype Apr 16, 2026
6743001
remove validateParameterDecorators from compiler.ts
PaperPrototype Apr 16, 2026
efe656b
Update compiler.ts
PaperPrototype Apr 16, 2026
3e8b163
Update compiler.ts
PaperPrototype Apr 16, 2026
516b2ec
Some claude stuff
PaperPrototype May 1, 2026
6615b02
undo tab in compiler.ts
PaperPrototype May 1, 2026
0e68e6a
Create temporary TODO.md for myself to write things down
PaperPrototype May 1, 2026
4afd421
Update TODO.md
PaperPrototype May 1, 2026
056ef1f
Transformer hook notes in TODO.md
PaperPrototype May 1, 2026
bc7f01e
Remove validation from compiler
PaperPrototype May 1, 2026
bcaacc6
Write test transformer in typescript to import types from assemblyscr…
PaperPrototype May 1, 2026
4deaa00
Update TODO.md
PaperPrototype May 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions NOTICE
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ under the licensing terms detailed in LICENSE:
* Mopsgamer <79159094+Mopsgamer@users.noreply.github.com>
* EDM115 <github@edm115.dev>
* Weixie Cui <cuiweixie@gmail.com>
* Abdiel Lopez <a.paperprototype@gmail.com>

Portions of this software are derived from third-party works licensed under
the following terms:
Expand Down
19 changes: 19 additions & 0 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Working document of notes/tasks for trying to figure out how to correctly add decorators to AssemblyScript. Will be delete once the PR is complete.


- [ ] Ask claude to explain how the AssemblyScript parser works and try to add a garbage throwaway feature myself manually for learning purposes (eg. `mut` keyword for syntax like `mut myVariable = 10`).

- [ ] After implementing re-read all the stuff dcode and Max have been explaining and try to better understand it

- [ ] Need to add a transform hook for valdiating after parse but before compiliation.

- [ ] Need to add a test case for having a transform validate decorators.

NOTES:
- AST can be extended without breaking changes, this is perfectly fine and anything that is added will just be ignored by the compiler but at least transformer plugins can then make use of it. No need to validate and throw errors unless it is an AST parse error.
- Need to add a transformer hook so transformers can add their own validation. This way AssemblyScript can delegate validation to transformers instead of having to handle it internally, which doesn't make sense because AS shouldn't be in charge of worring about that in the first place.

- "Transformer" refers the to transformers in /tests/transform

- Rather than have a method validate the decorators, they should just be part of the AST. Once the AST can directly handle the decorators then validating decorators syntax will happen automatically. This also means we need actual AST nodes in ast.ts for the different parts of decorators rather than just inlining the decorators.
- Correction to the above. The method validation was only 1 part of the problem, the compiler of assemblyscript is for outputting WASM binaries not validating valid AST syntax. The compiler.ts will not need to be touched AT ALL for this PR.
4 changes: 4 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ export default defineConfig([

// FIXME: Tagged template literal tests with invalid escapes
"tests/compiler/templateliteral.ts",

// Decorators on `this` are not allowed typically in TypeScript, but this
// fixture exercises that AS-only syntax and is validated by transform tests.
"tests/transform/parameter-decorators.ts",
]),

js.configs.recommended,
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@
"test:browser": "node --enable-source-maps tests/browser",
"test:asconfig": "cd tests/asconfig && npm run test",
"test:transform": "npm run test:transform:esm && npm run test:transform:cjs",
"test:transform:esm": "node bin/asc tests/compiler/empty --transform ./tests/transform/index.js --noEmit && node bin/asc tests/compiler/empty --transform ./tests/transform/simple.js --noEmit",
"test:transform:cjs": "node bin/asc tests/compiler/empty --transform ./tests/transform/cjs/index.js --noEmit && node bin/asc tests/compiler/empty --transform ./tests/transform/cjs/simple.js --noEmit",
"test:transform:esm": "node bin/asc tests/compiler/empty --transform ./tests/transform/index.js --noEmit && node bin/asc tests/compiler/empty --transform ./tests/transform/simple.js --noEmit && node --experimental-strip-types --no-warnings bin/asc tests/transform/parameter-decorators.ts --transform ./tests/transform/remove-parameter-decorators.ts --noEmit",
"test:transform:cjs": "node bin/asc tests/compiler/empty --transform ./tests/transform/cjs/index.js --noEmit && node bin/asc tests/compiler/empty --transform ./tests/transform/cjs/simple.js --noEmit && node bin/asc tests/transform/parameter-decorators.ts --transform ./tests/transform/cjs/remove-parameter-decorators.js --noEmit",
"test:cli": "node tests/cli/options.js",
"asbuild": "npm run asbuild:debug && npm run asbuild:release",
"asbuild:debug": "node bin/asc --config src/asconfig.json --target debug",
Expand Down
13 changes: 10 additions & 3 deletions src/ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,10 +155,11 @@ export abstract class Node {
parameters: ParameterNode[],
returnType: TypeNode,
explicitThisType: NamedTypeNode | null,
explicitThisDecorators: DecoratorNode[] | null,
isNullable: bool,
range: Range
): FunctionTypeNode {
return new FunctionTypeNode(parameters, returnType, explicitThisType, isNullable, range);
return new FunctionTypeNode(parameters, returnType, explicitThisType, explicitThisDecorators, isNullable, range);
}

static createOmittedType(
Expand All @@ -181,9 +182,10 @@ export abstract class Node {
name: IdentifierExpression,
type: TypeNode,
initializer: Expression | null,
decorators: DecoratorNode[] | null,
range: Range
): ParameterNode {
return new ParameterNode(parameterKind, name, type, initializer, range);
return new ParameterNode(parameterKind, name, type, initializer, decorators, range);
}

// special
Expand Down Expand Up @@ -919,6 +921,8 @@ export class FunctionTypeNode extends TypeNode {
public returnType: TypeNode,
/** Explicitly provided this type, if any. */
public explicitThisType: NamedTypeNode | null, // can't be a function
/** Decorators on an explicit `this` parameter, if any. Preserved as transform-only syntax. */
public explicitThisDecorators: DecoratorNode[] | null,
/** Whether nullable or not. */
isNullable: bool,
/** Source range. */
Expand Down Expand Up @@ -965,12 +969,13 @@ export class ParameterNode extends Node {
public type: TypeNode,
/** Initializer expression, if any. */
public initializer: Expression | null,
/** Decorators, if any. Preserved as transform-only syntax so transforms can rewrite or remove them before validation. */
public decorators: DecoratorNode[] | null,
/** Source range. */
range: Range
) {
super(NodeKind.Parameter, range);
}

/** Implicit field declaration, if applicable. */
implicitFieldDeclaration: FieldDeclaration | null = null;
/** Common flags indicating specific traits. */
Expand Down Expand Up @@ -1664,6 +1669,8 @@ export class Source extends Node {
debugInfoIndex: i32 = -1;
/** Re-exported sources. */
exportPaths: string[] | null = null;
/** Function types with parameter or this-parameter decorators, revisited after transforms for validation. */
decoratedFunctionTypes: FunctionTypeNode[] | null = null;

/** Checks if this source represents native code. */
get isNative(): bool {
Expand Down
3 changes: 1 addition & 2 deletions src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -536,8 +536,7 @@ export class Compiler extends DiagnosticEmitter {

// initialize lookup maps, built-ins, imports, exports, etc.
this.program.initialize();



// Binaryen treats all function references as being leaked to the outside world when
// the module isn't marked as closed-world (see WebAssembly/binaryen#7135). Therefore,
// we should mark the module as closed-world when we're definitely sure it is.
Expand Down
20 changes: 17 additions & 3 deletions src/extra/ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,7 @@ export class ASTBuilder {
sb.push(isNullable ? "((" : "(");
let explicitThisType = node.explicitThisType;
if (explicitThisType) {
this.serializeParameterDecorators(node.explicitThisDecorators);
sb.push("this: ");
this.visitTypeNode(explicitThisType);
}
Expand Down Expand Up @@ -1153,6 +1154,7 @@ export class ASTBuilder {
let numParameters = parameters.length;
let explicitThisType = signature.explicitThisType;
if (explicitThisType) {
this.serializeParameterDecorators(signature.explicitThisDecorators);
sb.push("this: ");
this.visitTypeNode(explicitThisType);
}
Expand Down Expand Up @@ -1541,7 +1543,7 @@ export class ASTBuilder {

// other

serializeDecorator(node: DecoratorNode): void {
serializeDecorator(node: DecoratorNode, isInline: bool = false): void {
let sb = this.sb;
sb.push("@");
this.visitNode(node.name);
Expand All @@ -1556,16 +1558,28 @@ export class ASTBuilder {
this.visitNode(args[i]);
}
}
sb.push(")\n");
sb.push(")");
}
if (isInline) {
sb.push(" ");
} else {
sb.push("\n");
indent(sb, this.indentLevel);
}
}

serializeParameterDecorators(decorators: DecoratorNode[] | null): void {
if (decorators) {
for (let i = 0, k = decorators.length; i < k; ++i) {
this.serializeDecorator(decorators[i], true);
}
}
indent(sb, this.indentLevel);
}

serializeParameter(node: ParameterNode): void {
let sb = this.sb;
let kind = node.parameterKind;
this.serializeParameterDecorators(node.decorators);
let implicitFieldDeclaration = node.implicitFieldDeclaration;
if (implicitFieldDeclaration) {
this.serializeAccessModifiers(implicitFieldDeclaration);
Expand Down
Loading