0
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-03-04 09:57:11 -05:00
deno/cli/js/40_lint.js

1998 lines
44 KiB
JavaScript
Raw Normal View History

2024-12-04 02:59:39 +01:00
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
2024-12-06 23:26:11 +01:00
// @ts-check
import exp from "constants";
2024-12-04 02:59:39 +01:00
import { core } from "ext:core/mod.js";
2024-12-06 23:26:11 +01:00
import { deno } from "../tsc/dts/typescript.d.ts";
2024-12-04 02:59:39 +01:00
const {
2024-12-03 13:45:31 +01:00
op_lint_get_rule,
op_lint_get_source,
op_lint_report,
2024-12-04 02:59:39 +01:00
} = core.ops;
2024-12-01 04:53:47 +01:00
2024-12-04 12:42:55 +01:00
/** @typedef {{ plugins: Array<{ name: string, rules: Record<string, Deno.LintRule}> }} LintState */
/** @type {LintState} */
const state = {
2024-12-04 12:42:55 +01:00
plugins: [],
};
2024-12-01 04:53:47 +01:00
export class Context {
id;
fileName;
2024-12-04 12:42:55 +01:00
#source = null;
2024-12-06 23:26:11 +01:00
/**
* @param {string} id
* @param {string} fileName
*/
2024-12-01 04:53:47 +01:00
constructor(id, fileName) {
this.id = id;
this.fileName = fileName;
}
2024-12-03 13:45:31 +01:00
source() {
2024-12-04 12:42:55 +01:00
if (this.#source === null) {
this.#source = op_lint_get_source();
}
return this.#source;
2024-12-03 13:45:31 +01:00
}
2024-12-02 02:27:30 +01:00
report(data) {
2024-12-03 03:11:26 +01:00
let start, end;
if (data.node) {
start = data.node.span.start - 1;
end = data.node.span.end - 1;
} else if (data.span) {
start = data.span.start - 1;
end = data.span.end - 1;
} else {
throw new Error(
"Either `node` or `span` must be provided when reporting an error",
);
}
2024-12-02 18:29:30 +01:00
op_lint_report(
this.id,
this.fileName,
data.message,
2024-12-03 03:11:26 +01:00
start,
end,
2024-12-02 18:29:30 +01:00
);
2024-12-01 04:53:47 +01:00
}
}
2024-12-04 04:30:32 +01:00
export function installPlugin(plugin) {
console.log("plugin", plugin);
if (typeof plugin !== "object") {
throw new Error("Linter plugin must be an object");
}
if (typeof plugin.name !== "string") {
throw new Error("Linter plugin name must be a string");
}
if (typeof plugin.rules !== "object") {
throw new Error("Linter plugin rules must be an object");
}
if (typeof state.plugins[plugin.name] !== "undefined") {
throw new Error(`Linter plugin ${plugin.name} has already been registered`);
}
state.plugins[plugin.name] = plugin.rules;
console.log("Installed plugin", plugin.name, plugin.rules);
}
2024-12-05 01:18:08 +01:00
// Keep in sync with Rust
2024-12-06 23:26:11 +01:00
/**
* @enum {number}
*/
const AstNodeId = {
2024-12-05 01:18:08 +01:00
Invalid: 0,
Program: 1,
Import: 2,
// Decls
Class: 12,
Fn: 13,
Var: 14,
// Statements
2024-12-06 23:26:11 +01:00
BlockStatement: 20,
2024-12-05 01:18:08 +01:00
Empty: 21,
2024-12-06 23:26:11 +01:00
DebuggerStatement: 22,
WithStatement: 23,
ReturnStatement: 24,
LabeledStatement: 25,
BreakStatement: 26,
ContinueStatement: 27,
IfStatement: 28,
SwitchStatement: 29,
2024-12-05 01:18:08 +01:00
SwitchCase: 30,
2024-12-06 23:26:11 +01:00
ThrowStatement: 31,
TryStatement: 32,
WhileStatement: 33,
DoWhileStatement: 34,
ForStatement: 35,
ForInStatement: 36,
ForOfStatement: 37,
2024-12-05 01:18:08 +01:00
Decl: 38,
2024-12-06 23:26:11 +01:00
ExpressionStatement: 39,
2024-12-05 01:18:08 +01:00
// Expressions
This: 40,
2024-12-06 23:26:11 +01:00
ArrayExpression: 41,
ObjectExpression: 42,
FunctionExpression: 43,
2024-12-05 01:18:08 +01:00
Unary: 44,
Update: 45,
2024-12-06 23:26:11 +01:00
BinaryExpression: 46,
AssignmentExpression: 47,
MemberExpression: 48,
2024-12-05 01:18:08 +01:00
SuperProp: 49,
2024-12-06 23:26:11 +01:00
ConditionalExpression: 50,
CallExpression: 51,
NewExpression: 52,
SequenceExpression: 53,
Identifier: 54,
2024-12-05 01:18:08 +01:00
Tpl: 55,
2024-12-06 23:26:11 +01:00
TaggedTemplateExpression: 56,
ArrowFunctionExpression: 57,
2024-12-05 01:18:08 +01:00
Yield: 59,
2024-12-06 23:26:11 +01:00
AwaitExpression: 61,
2024-12-05 01:18:08 +01:00
StringLiteral: 70,
2024-12-06 23:26:11 +01:00
BooleanLiteral: 71,
NullLiteral: 72,
NumericLiteral: 73,
2024-12-05 01:18:08 +01:00
BigInt: 74,
2024-12-06 23:26:11 +01:00
RegExpLiteral: 75,
2024-12-05 01:18:08 +01:00
// Custom
2024-12-05 02:24:26 +01:00
EmptyExpr: 82,
2024-12-05 01:18:08 +01:00
Spread: 83,
ObjProperty: 84,
VarDeclarator: 85,
2024-12-05 02:24:26 +01:00
CatchClause: 86,
2024-12-04 16:21:17 +01:00
};
2024-12-05 01:18:08 +01:00
const _ID = Symbol.for("__astId");
2024-12-06 23:26:11 +01:00
/** @implements {Deno.Program} */
2024-12-05 01:18:08 +01:00
class Program {
2024-12-06 23:26:11 +01:00
type = /** @type {const} */ ("Program");
range;
get body() {
return [];
2024-12-05 01:18:08 +01:00
}
2024-12-06 23:26:11 +01:00
#ctx;
/**
* @param {ParseContext} ctx
* @param {Deno.Range} range
* @param {Deno.Program["sourceType"]} sourceType
*/
constructor(ctx, range, sourceType) {
this.#ctx = ctx;
this.range = range;
this.sourceType = sourceType;
2024-12-05 01:18:08 +01:00
}
}
2024-12-06 23:26:11 +01:00
/** @implements {Deno.BlockStatement} */
class BlockStatement {
type = /** @type {const} */ ("BlockStatement");
body = [];
range;
2024-12-05 01:18:08 +01:00
2024-12-06 23:26:11 +01:00
#ctx;
2024-12-05 01:18:08 +01:00
2024-12-06 23:26:11 +01:00
/**
* @param {ParseContext} ctx
* @param {Deno.Range} range
*/
constructor(ctx, range) {
this.#ctx = ctx;
this.range = range;
2024-12-05 01:18:08 +01:00
}
}
2024-12-06 23:26:11 +01:00
/** @implements {Deno.BreakStatement} */
class BreakStatement {
type = /** @type {const} */ ("BreakStatement");
get label() {
if (this.#labelId === 0) return null;
return /** @type {Deno.Identifier} */ (createAstNode(
this.#ctx,
this.#labelId,
));
}
range;
#ctx;
#labelId;
/**
* @param {ParseContext} ctx
* @param {Deno.Range} range
* @param {number} labelId
*/
constructor(ctx, range, labelId) {
this.#ctx = ctx;
this.#labelId = labelId;
this.range = range;
2024-12-05 01:18:08 +01:00
}
}
2024-12-06 23:26:11 +01:00
/** @implements {Deno.ContinueStatement} */
class ContinueStatement {
type = /** @type {const} */ ("ContinueStatement");
range;
get label() {
return /** @type {Deno.Identifier} */ (createAstNode(
this.#ctx,
this.#labelId,
));
}
2024-12-05 01:18:08 +01:00
2024-12-06 23:26:11 +01:00
#ctx;
#labelId = 0;
/**
* @param {ParseContext} ctx
* @param {Deno.Range} range
* @param {number} labelId
*/
constructor(ctx, range, labelId) {
this.#ctx = ctx;
this.#labelId = labelId;
this.range = range;
2024-12-05 01:18:08 +01:00
}
}
2024-12-06 23:26:11 +01:00
/** @implements {Deno.DebuggerStatement} */
class DebuggerStatement {
type = /** @type {const} */ ("DebuggerStatement");
range;
#ctx;
/**
* @param {ParseContext} ctx
* @param {Deno.Range} range
*/
constructor(ctx, range) {
this.#ctx = ctx;
this.range = range;
2024-12-05 01:18:08 +01:00
}
}
2024-12-06 23:26:11 +01:00
/** @implements {Deno.DoWhileStatement} */
class DoWhileStatement {
type = /** @type {const} */ ("DoWhileStatement");
range;
get test() {
return /** @type {Deno.Expression} */ (createAstNode(
this.#ctx,
this.#exprId,
));
}
get body() {
return /** @type {Deno.Statement} */ (createAstNode(
this.#ctx,
this.#bodyId,
));
}
2024-12-05 01:18:08 +01:00
2024-12-06 23:26:11 +01:00
#ctx;
#exprId = 0;
#bodyId = 0;
/**
* @param {ParseContext} ctx
* @param {Deno.Range} range
* @param {number} exprId
* @param {number} bodyId
*/
constructor(ctx, range, exprId, bodyId) {
this.#ctx = ctx;
this.#exprId = exprId;
this.#bodyId = bodyId;
this.range = range;
2024-12-05 01:18:08 +01:00
}
}
2024-12-06 23:26:11 +01:00
/** @implements {Deno.ExpressionStatement} */
class ExpressionStatement {
type = /** @type {const} */ ("ExpressionStatement");
range;
get expression() {
return /** @type {Deno.Expression} */ (createAstNode(
this.#ctx,
this.#exprId,
));
}
2024-12-05 01:18:08 +01:00
2024-12-06 23:26:11 +01:00
#ctx;
#exprId = 0;
/**
* @param {ParseContext} ctx
* @param {Deno.Range} range
* @param {number} exprId
*/
constructor(ctx, range, exprId) {
this.#ctx = ctx;
this.#exprId = exprId;
this.range = range;
2024-12-05 01:18:08 +01:00
}
}
2024-12-06 23:26:11 +01:00
/** @implements {Deno.ForInStatement} */
2024-12-05 01:18:08 +01:00
class ForInStatement {
2024-12-06 23:26:11 +01:00
type = /** @type {const} */ ("ForInStatement");
range;
get left() {
return /** @type {Deno.Expression | Deno.VariableDeclaration} */ (createAstNode(
this.#ctx,
this.#leftId,
));
}
get right() {
return /** @type {Deno.Expression} */ (createAstNode(
this.#ctx,
this.#rightId,
));
}
get body() {
return /** @type {Deno.Statement} */ (createAstNode(
this.#ctx,
this.#bodyId,
));
}
2024-12-05 01:18:08 +01:00
2024-12-06 23:26:11 +01:00
#ctx;
#leftId = 0;
#rightId = 0;
#bodyId = 0;
/**
* @param {ParseContext} ctx
* @param {Deno.Range} range
* @param {number} leftId
* @param {number} rightId
* @param {number} bodyId
*/
constructor(ctx, range, leftId, rightId, bodyId) {
this.#ctx = ctx;
this.#leftId = leftId;
this.#rightId = rightId;
this.#bodyId = bodyId;
this.range = range;
2024-12-05 01:18:08 +01:00
}
}
2024-12-06 23:26:11 +01:00
/** @implements {Deno.ForOfStatement} */
2024-12-05 01:18:08 +01:00
class ForOfStatement {
2024-12-06 23:26:11 +01:00
type = /** @type {const} */ ("ForOfStatement");
range;
get left() {
return /** @type {Deno.Expression | Deno.VariableDeclaration | Deno.UsingDeclaration} */ (createAstNode(
this.#ctx,
this.#leftId,
));
}
get right() {
return /** @type {Deno.Expression} */ (createAstNode(
this.#ctx,
this.#rightId,
));
}
get body() {
return /** @type {Deno.Statement} */ (createAstNode(
this.#ctx,
this.#bodyId,
));
}
2024-12-05 01:18:08 +01:00
2024-12-06 23:26:11 +01:00
await;
#ctx;
#leftId = 0;
#rightId = 0;
#bodyId = 0;
/**
* @param {ParseContext} ctx
* @param {Deno.Range} range
* @param {boolean} isAwait
* @param {number} leftId
* @param {number} rightId
* @param {number} bodyId
*/
constructor(ctx, range, isAwait, leftId, rightId, bodyId) {
this.#ctx = ctx;
this.#leftId = leftId;
this.#rightId = rightId;
this.#bodyId = bodyId;
this.range = range;
this.await = isAwait;
2024-12-05 01:18:08 +01:00
}
}
2024-12-06 23:26:11 +01:00
/** @implements {Deno.ForStatement} */
class ForStatement {
type = /** @type {const} */ ("ForStatement");
range;
get init() {
if (this.#initId === 0) return null;
return /** @type {Deno.Expression | Deno.VariableDeclaration} */ (createAstNode(
this.#ctx,
this.#initId,
));
}
get test() {
if (this.#initId === 0) return null;
return /** @type {Deno.Expression} */ (createAstNode(
this.#ctx,
this.#testId,
));
}
get update() {
if (this.#updateId === 0) return null;
return /** @type {Deno.Expression} */ (createAstNode(
this.#ctx,
this.#updateId,
));
}
get body() {
return /** @type {Deno.Statement} */ (createAstNode(
this.#ctx,
this.#bodyId,
));
}
2024-12-05 01:18:08 +01:00
2024-12-06 23:26:11 +01:00
#ctx;
#initId = 0;
#testId = 0;
#updateId = 0;
#bodyId = 0;
/**
* @param {ParseContext} ctx
* @param {Deno.Range} range
* @param {number} initId
* @param {number} testId
* @param {number} updateId
* @param {number} bodyId
*/
constructor(ctx, range, initId, testId, updateId, bodyId) {
this.#ctx = ctx;
this.#initId = initId;
this.#testId = testId;
this.#updateId = updateId;
this.#bodyId = bodyId;
this.range = range;
2024-12-05 01:18:08 +01:00
}
}
2024-12-06 23:26:11 +01:00
/** @implements {Deno.IfStatement} */
class IfStatement {
type = /** @type {const} */ ("IfStatement");
range;
get test() {
return /** @type {Deno.Expression} */ (createAstNode(
this.#ctx,
this.#testId,
));
}
get consequent() {
return /** @type {Deno.Statement} */ (createAstNode(
this.#ctx,
this.#consequentId,
));
}
get alternate() {
if (this.#alternateId === 0) return null;
return /** @type {Deno.Statement} */ (createAstNode(
this.#ctx,
this.#alternateId,
));
}
2024-12-05 01:18:08 +01:00
2024-12-06 23:26:11 +01:00
#ctx;
#testId = 0;
#consequentId = 0;
#alternateId = 0;
/**
* @param {ParseContext} ctx
* @param {Deno.Range} range
* @param {number} testId
* @param {number} updateId
* @param {number} alternateId
*/
constructor(ctx, range, testId, updateId, alternateId) {
this.#ctx = ctx;
this.#testId = testId;
this.#consequentId = updateId;
this.#alternateId = alternateId;
this.range = range;
2024-12-05 01:18:08 +01:00
}
}
2024-12-06 23:26:11 +01:00
/** @implements {Deno.LabeledStatement} */
class LabeledStatement {
type = /** @type {const} */ ("LabeledStatement");
range;
get label() {
return /** @type {Deno.Identifier} */ (createAstNode(
this.#ctx,
this.#labelId,
));
}
get body() {
return /** @type {Deno.Statement} */ (createAstNode(
this.#ctx,
this.#bodyId,
));
}
2024-12-05 02:24:26 +01:00
2024-12-06 23:26:11 +01:00
#ctx;
#labelId = 0;
#bodyId = 0;
/**
* @param {ParseContext} ctx
* @param {Deno.Range} range
* @param {number} testId
* @param {number} bodyId
*/
constructor(ctx, range, testId, bodyId) {
this.#ctx = ctx;
this.#labelId = testId;
this.#bodyId = bodyId;
this.range = range;
2024-12-05 02:24:26 +01:00
}
}
2024-12-06 23:26:11 +01:00
/** @implements {Deno.ReturnStatement} */
class ReturnStatement {
type = /** @type {const} */ ("ReturnStatement");
range;
get argument() {
if (this.#exprId === 0) return null;
return /** @type {Deno.Expression} */ (createAstNode(
this.#ctx,
this.#exprId,
));
}
2024-12-05 02:24:26 +01:00
2024-12-06 23:26:11 +01:00
#ctx;
#exprId = 0;
/**
* @param {ParseContext} ctx
* @param {Deno.Range} range
* @param {number} argId
*/
constructor(ctx, range, argId) {
this.#ctx = ctx;
this.#exprId = argId;
this.range = range;
2024-12-05 02:24:26 +01:00
}
}
2024-12-06 23:26:11 +01:00
/** @implements {Deno.SwitchStatement} */
class SwitchStatement {
type = /** @type {const} */ ("SwitchStatement");
range;
get discriminant() {
return /** @type {Deno.Expression} */ (createAstNode(
this.#ctx,
this.#discriminantId,
));
}
get cases() {
return []; // FIXME
}
2024-12-05 01:18:08 +01:00
2024-12-06 23:26:11 +01:00
#ctx;
#discriminantId = 0;
/**
* @param {ParseContext} ctx
* @param {Deno.Range} range
* @param {number} discriminantId
*/
constructor(ctx, range, discriminantId) {
this.#ctx = ctx;
this.#discriminantId = discriminantId;
this.range = range;
2024-12-05 01:18:08 +01:00
}
}
2024-12-06 23:26:11 +01:00
/** @implements {Deno.ThrowStatement} */
class ThrowStatement {
type = /** @type {const} */ ("ThrowStatement");
range;
get argument() {
return /** @type {Deno.Expression} */ (createAstNode(
this.#ctx,
this.#argId,
));
}
2024-12-05 01:18:08 +01:00
2024-12-06 23:26:11 +01:00
#ctx;
#argId = 0;
/**
* @param {ParseContext} ctx
* @param {Deno.Range} range
* @param {number} argId
*/
constructor(ctx, range, argId) {
this.#ctx = ctx;
this.#argId = argId;
this.range = range;
2024-12-05 01:18:08 +01:00
}
}
2024-12-06 23:26:11 +01:00
/** @implements {Deno.TryStatement} */
class TryStatement {
type = /** @type {const} */ ("TryStatement");
range;
get block() {
return /** @type {Deno.BlockStatement} */ (createAstNode(
this.#ctx,
this.#blockId,
));
}
get finalizer() {
if (this.#finalizerId === 0) return null;
return /** @type {Deno.BlockStatement} */ (createAstNode(
this.#ctx,
this.#finalizerId,
));
}
get handler() {
if (this.#handlerId === 0) return null;
return /** @type {Deno.CatchClause} */ (createAstNode(
this.#ctx,
this.#handlerId,
));
}
2024-12-05 01:18:08 +01:00
2024-12-06 23:26:11 +01:00
#ctx;
#blockId = 0;
#finalizerId = 0;
#handlerId = 0;
/**
* @param {ParseContext} ctx
* @param {Deno.Range} range
* @param {number} blockId
* @param {number} finalizerId
* @param {number} handlerId
*/
constructor(ctx, range, blockId, finalizerId, handlerId) {
this.#ctx = ctx;
this.#blockId = blockId;
this.#finalizerId = finalizerId;
this.#handlerId = handlerId;
this.range = range;
2024-12-05 01:18:08 +01:00
}
}
2024-12-06 23:26:11 +01:00
/** @implements {Deno.WhileStatement} */
class WhileStatement {
type = /** @type {const} */ ("WhileStatement");
range;
get test() {
return /** @type {Deno.Expression} */ (createAstNode(
this.#ctx,
this.#testId,
));
}
get body() {
return /** @type {Deno.Statement} */ (createAstNode(
this.#ctx,
this.#bodyId,
));
}
2024-12-05 01:18:08 +01:00
2024-12-06 23:26:11 +01:00
#ctx;
#testId = 0;
#bodyId = 0;
/**
* @param {ParseContext} ctx
* @param {Deno.Range} range
* @param {number} testId
* @param {number} bodyId
*/
constructor(ctx, range, testId, bodyId) {
this.#ctx = ctx;
this.#testId = testId;
this.#bodyId = bodyId;
this.range = range;
2024-12-05 01:18:08 +01:00
}
}
2024-12-06 23:26:11 +01:00
/** @implements {Deno.WithStatement} */
class WithStatement {
type = /** @type {const} */ ("WithStatement");
range;
get body() {
return /** @type {Deno.Statement} */ (createAstNode(
this.#ctx,
this.#bodyId,
));
}
get object() {
return /** @type {Deno.Expression} */ (createAstNode(
this.#ctx,
this.#objectId,
));
}
2024-12-05 01:18:08 +01:00
2024-12-06 23:26:11 +01:00
#ctx;
#bodyId = 0;
#objectId = 0;
/**
* @param {ParseContext} ctx
* @param {Deno.Range} range
* @param {number} bodyId
* @param {number} objectId
*/
constructor(ctx, range, bodyId, objectId) {
this.#ctx = ctx;
this.#bodyId = bodyId;
this.#objectId = objectId;
this.range = range;
2024-12-05 01:18:08 +01:00
}
}
2024-12-06 23:26:11 +01:00
// Expressions
2024-12-05 01:18:08 +01:00
2024-12-06 23:26:11 +01:00
/** @implements {Deno.ArrayExpression} */
class ArrayExpression {
type = /** @type {const} */ ("ArrayExpression");
range;
get elements() {
return []; // FIXME
2024-12-05 01:18:08 +01:00
}
2024-12-06 23:26:11 +01:00
#ctx;
2024-12-05 01:18:08 +01:00
2024-12-06 23:26:11 +01:00
/**
* @param {ParseContext} ctx
* @param {Deno.Range} range
*/
constructor(ctx, range) {
this.#ctx = ctx;
this.range = range;
2024-12-05 01:18:08 +01:00
}
}
2024-12-06 23:26:11 +01:00
/** @implements {Deno.ArrowFunctionExpression} */
class ArrowFunctionExpression {
type = /** @type {const} */ ("ArrowFunctionExpression");
range;
async = false;
generator = false;
2024-12-05 01:18:08 +01:00
2024-12-06 23:26:11 +01:00
get body() {
return /** @type {Deno.BlockStatement| Deno.Expression} */ (createAstNode(
this.#ctx,
this.#bodyId,
));
2024-12-05 01:18:08 +01:00
}
2024-12-06 23:26:11 +01:00
get params() {
return []; // FIXME
}
2024-12-05 01:18:08 +01:00
2024-12-06 23:26:11 +01:00
get returnType() {
return null; // FIXME
2024-12-05 01:18:08 +01:00
}
2024-12-06 23:26:11 +01:00
get typeParameters() {
return null; // FIXME
}
2024-12-05 01:18:08 +01:00
2024-12-06 23:26:11 +01:00
#ctx;
#bodyId;
/**
* @param {ParseContext} ctx
* @param {Deno.Range} range
* @param {boolean} isAsync
* @param {boolean} isGenerator
* @param {number} bodyId
*/
constructor(ctx, range, isAsync, isGenerator, bodyId) {
this.#ctx = ctx;
this.#bodyId = bodyId;
this.asnyc = isAsync;
this.generator = isGenerator;
this.range = range;
2024-12-05 01:18:08 +01:00
}
}
2024-12-06 23:26:11 +01:00
/** @implements {Deno.AssignmentExpression} */
class AssignmentExpression {
type = /** @type {const} */ ("AssignmentExpression");
range;
get left() {
return /** @type {Deno.Expression} */ (createAstNode(
this.#ctx,
this.#leftId,
));
}
get right() {
return /** @type {Deno.Expression} */ (createAstNode(
this.#ctx,
this.#rightId,
));
}
2024-12-05 01:18:08 +01:00
2024-12-06 23:26:11 +01:00
operator;
#ctx;
#leftId = 0;
#rightId = 0;
/**
* @param {ParseContext} ctx
* @param {Deno.Range} range
* @param {number} flags
* @param {number} leftId
* @param {number} rightId
*/
constructor(ctx, range, flags, leftId, rightId) {
this.#ctx = ctx;
this.#leftId = leftId;
this.#rightId = rightId;
this.range = range;
this.operator = getAssignOperator(flags);
2024-12-05 01:18:08 +01:00
}
}
2024-12-06 23:26:11 +01:00
/**
* @param {number} n
* @returns {Deno.AssignmentExpression["operator"]}
*/
function getAssignOperator(n) {
switch (n) {
case 0:
return "&&=";
case 1:
return "&=";
case 2:
return "**=";
case 3:
return "*=";
case 4:
return "||=";
case 5:
return "|=";
case 6:
return "^=";
case 7:
return "=";
case 8:
return ">>=";
case 9:
return ">>>=";
case 10:
return "<<=";
case 11:
return "-=";
case 12:
return "%=";
case 13:
return "+=";
case 14:
return "??=";
case 15:
return "/=";
default:
throw new Error(`Unknown operator: ${n}`);
2024-12-05 01:18:08 +01:00
}
}
2024-12-06 23:26:11 +01:00
/** @implements {Deno.AwaitExpression} */
class AwaitExpression {
type = /** @type {const} */ ("AwaitExpression");
range;
get argument() {
return /** @type {Deno.Expression} */ (createAstNode(
this.#ctx,
this.#argId,
));
}
2024-12-05 01:18:08 +01:00
2024-12-06 23:26:11 +01:00
#ctx;
#argId = 0;
/**
* @param {ParseContext} ctx
* @param {Deno.Range} range
* @param {number} argId
*/
constructor(ctx, range, argId) {
this.#ctx = ctx;
this.#argId = argId;
this.range = range;
2024-12-05 01:18:08 +01:00
}
}
2024-12-06 23:26:11 +01:00
/** @implements {Deno.BinaryExpression} */
class BinaryExpression {
type = /** @type {const} */ ("BinaryExpression");
range;
get left() {
return /** @type {Deno.Expression | Deno.PrivateIdentifier} */ (createAstNode(
this.#ctx,
this.#leftId,
));
}
get right() {
return /** @type {Deno.Expression} */ (createAstNode(
this.#ctx,
this.#rightId,
));
}
2024-12-05 01:18:08 +01:00
2024-12-06 23:26:11 +01:00
operator;
#ctx;
#leftId;
#rightId;
/**
* @param {ParseContext} ctx
* @param {Deno.Range} range
* @param {number} flags
* @param {number} leftId
* @param {number} rightId
*/
constructor(ctx, range, flags, leftId, rightId) {
this.#ctx = ctx;
this.#leftId = leftId;
this.#rightId = rightId;
this.operator = getBinaryOperator(flags);
this.range = range;
2024-12-05 01:18:08 +01:00
}
}
2024-12-06 23:26:11 +01:00
/**
* @param {number} n
* @returns {Deno.BinaryExpression["operator"]}
*/
function getBinaryOperator(n) {
switch (n) {
case 0:
return "&";
case 1:
return "**";
case 2:
return "*";
case 3:
return "|";
case 4:
return "^";
case 5:
return "===";
case 6:
return "==";
case 7:
return "!==";
case 8:
return "!=";
case 9:
return ">=";
case 10:
return ">>>";
case 11:
return ">>";
case 12:
return ">";
case 13:
return "in";
case 14:
return "instanceof";
case 15:
return "<=";
case 16:
return "<<";
case 17:
return "<";
case 18:
return "-";
case 19:
return "%";
case 20:
return "+";
case 21:
return "/";
default:
throw new Error(`Unknown operator: ${n}`);
2024-12-05 01:18:08 +01:00
}
}
2024-12-06 23:26:11 +01:00
/** @implements {Deno.CallExpression} */
class CallExpression {
type = /** @type {const} */ ("CallExpression");
range;
get callee() {
return /** @type {Deno.Expression} */ (createAstNode(
this.#ctx,
this.#calleeId,
));
}
get arguments() {
return []; // FIXME
}
get typeArguments() {
return null; // FIXME
}
optional = false; // FIXME
#ctx;
#calleeId;
2024-12-05 01:18:08 +01:00
2024-12-06 23:26:11 +01:00
/**
* @param {ParseContext} ctx
* @param {Deno.Range} range
* @param {number} calleeId
*/
constructor(ctx, range, calleeId) {
this.#ctx = ctx;
this.#calleeId = calleeId;
this.range = range;
2024-12-05 01:18:08 +01:00
}
}
2024-12-06 23:26:11 +01:00
/** @implements {Deno.ChainExpression} */
class ChainExpression {
type = /** @type {const} */ ("ChainExpression");
range;
#ctx;
2024-12-05 01:18:08 +01:00
2024-12-06 23:26:11 +01:00
/**
* @param {ParseContext} ctx
* @param {Deno.Range} range
*/
constructor(ctx, range) {
this.#ctx = ctx;
this.range = range;
2024-12-05 01:18:08 +01:00
}
}
2024-12-06 23:26:11 +01:00
/** @implements {Deno.ConditionalExpression} */
class ConditionalExpression {
type = /** @type {const} */ ("ConditionalExpression");
range;
get test() {
return /** @type {Deno.Expression} */ (createAstNode(
this.#ctx,
this.#testId,
));
}
get consequent() {
return /** @type {Deno.Expression} */ (createAstNode(
this.#ctx,
this.#consequentId,
));
}
get alternate() {
return /** @type {Deno.Expression} */ (createAstNode(
this.#ctx,
this.#alternateId,
));
}
2024-12-05 01:18:08 +01:00
2024-12-06 23:26:11 +01:00
#ctx;
#testId;
#consequentId;
#alternateId;
/**
* @param {ParseContext} ctx
* @param {Deno.Range} range
* @param {number} testId
* @param {number} consequentId
* @param {number} alternateId
*/
constructor(ctx, range, testId, consequentId, alternateId) {
this.#ctx = ctx;
this.#testId = testId;
this.#consequentId = consequentId;
this.#alternateId = alternateId;
this.range = range;
2024-12-05 01:18:08 +01:00
}
}
2024-12-06 23:26:11 +01:00
/** @implements {Deno.FunctionExpression} */
class FunctionExpression {
type = /** @type {const} */ ("FunctionExpression");
range;
#ctx;
/**
* @param {ParseContext} ctx
* @param {Deno.Range} range
*/
constructor(ctx, range) {
this.#ctx = ctx;
this.range = range;
2024-12-05 01:18:08 +01:00
}
}
2024-12-06 23:26:11 +01:00
/** @implements {Deno.Identifier} */
class Identifier {
type = /** @type {const} */ ("Identifier");
range;
name = "";
2024-12-05 01:18:08 +01:00
2024-12-06 23:26:11 +01:00
#ctx;
/**
* @param {ParseContext} ctx
* @param {Deno.Range} range
* @param {number} flags
*/
constructor(ctx, range, flags) {
this.#ctx = ctx;
this.name = getString(ctx, flags);
this.range = range;
2024-12-05 01:18:08 +01:00
}
}
2024-12-06 23:26:11 +01:00
/** @implements {Deno.LogicalExpression} */
class LogicalExpression {
type = /** @type {const} */ ("LogicalExpression");
range;
get left() {
return /** @type {Deno.Expression} */ (createAstNode(
this.#ctx,
this.#leftId,
));
}
get right() {
return /** @type {Deno.Expression} */ (createAstNode(
this.#ctx,
this.#rightId,
));
}
2024-12-05 01:18:08 +01:00
2024-12-06 23:26:11 +01:00
#ctx;
#leftId;
#rightId;
/**
* @param {ParseContext} ctx
* @param {Deno.Range} range
* @param {number} flags
* @param {number} leftId
* @param {number} rightId
*/
constructor(ctx, range, flags, leftId, rightId) {
this.#ctx = ctx;
this.operator = getLogicalOperator(flags);
this.#leftId = leftId;
this.#rightId = rightId;
this.range = range;
2024-12-05 01:18:08 +01:00
}
}
2024-12-06 23:26:11 +01:00
/** @implements {Deno.MemberExpression} */
class MemberExpression {
type = /** @type {const} */ ("MemberExpression");
range;
get object() {
return /** @type {Deno.Expression} */ (createAstNode(
this.#ctx,
this.#objId,
));
}
get property() {
return /** @type {Deno.Expression | Deno.Identifier | Deno.PrivateIdentifier} */ (createAstNode(
this.#ctx,
this.#propId,
));
}
optional = false; // FIXME
computed = false; // FIXME
#ctx;
#objId;
#propId;
/**
* @param {ParseContext} ctx
* @param {Deno.Range} range
* @param {number} flags
* @param {number} objId
* @param {number} propId
*/
constructor(ctx, range, flags, objId, propId) {
this.#ctx = ctx;
this.computed = flags === 1;
this.#objId = objId;
this.#propId = propId;
this.range = range;
}
}
2024-12-05 01:18:08 +01:00
2024-12-06 23:26:11 +01:00
/**
* @param {number} n
* @returns {Deno.LogicalExpression["operator"]}
*/
function getLogicalOperator(n) {
switch (n) {
case 0:
return "&&";
case 1:
return "||";
case 2:
return "??";
default:
throw new Error(`Unknown operator: ${n}`);
2024-12-05 01:18:08 +01:00
}
}
2024-12-06 23:26:11 +01:00
// Literals
/** @implements {Deno.BooleanLiteral} */
2024-12-05 01:18:08 +01:00
class BooleanLiteral {
2024-12-06 23:26:11 +01:00
type = /** @type {const} */ ("BooleanLiteral");
range;
2024-12-05 01:18:08 +01:00
value = false;
2024-12-06 23:26:11 +01:00
#ctx;
/**
* @param {ParseContext} ctx
* @param {Deno.Range} range
* @param {number} flags
*/
constructor(ctx, range, flags) {
this.#ctx = ctx;
this.value = flags === 1;
this.range = range;
2024-12-05 01:18:08 +01:00
}
}
2024-12-06 23:26:11 +01:00
/** @implements {Deno.NullLiteral} */
2024-12-05 01:18:08 +01:00
class NullLiteral {
2024-12-06 23:26:11 +01:00
type = /** @type {const} */ ("NullLiteral");
range;
value = null;
2024-12-05 01:18:08 +01:00
2024-12-06 23:26:11 +01:00
#ctx;
/**
* @param {ParseContext} ctx
* @param {Deno.Range} range
*/
constructor(ctx, range) {
this.#ctx = ctx;
this.range = range;
2024-12-05 01:18:08 +01:00
}
}
2024-12-06 23:26:11 +01:00
/** @implements {Deno.NumericLiteral} */
2024-12-05 01:18:08 +01:00
class NumericLiteral {
2024-12-06 23:26:11 +01:00
type = /** @type {const} */ ("NumericLiteral");
range;
2024-12-05 01:18:08 +01:00
value = 0;
2024-12-06 23:26:11 +01:00
#ctx;
/**
* @param {ParseContext} ctx
* @param {Deno.Range} range
* @param {number} flags
*/
constructor(ctx, range, flags) {
this.#ctx = ctx;
this.range = range;
this.value = flags;
2024-12-05 01:18:08 +01:00
}
}
2024-12-06 23:26:11 +01:00
/** @implements {Deno.RegExpLiteral} */
2024-12-05 01:18:08 +01:00
class RegExpLiteral {
2024-12-06 23:26:11 +01:00
type = /** @type {const} */ ("RegExpLiteral");
range;
2024-12-05 01:18:08 +01:00
pattern = "";
flags = "";
2024-12-06 23:26:11 +01:00
#ctx;
/**
* @param {ParseContext} ctx
* @param {Deno.Range} range
* @param {number} patternId
* @param {number} flagsId
*/
constructor(ctx, range, patternId, flagsId) {
this.#ctx = ctx;
this.range = range;
this.pattern = getString(ctx, patternId);
this.flags = getString(ctx, flagsId);
2024-12-05 01:18:08 +01:00
}
}
2024-12-06 23:26:11 +01:00
/** @implements {Deno.StringLiteral} */
class StringLiteral {
type = /** @type {const} */ ("StringLiteral");
range;
value = "";
#ctx;
/**
* @param {ParseContext} ctx
* @param {Deno.Range} range
* @param {number} flags
*/
constructor(ctx, range, flags) {
this.#ctx = ctx;
this.range = range;
this.value = getString(ctx, flags);
2024-12-05 01:18:08 +01:00
}
}
2024-12-06 23:26:11 +01:00
const DECODER = new TextDecoder();
2024-12-05 02:24:26 +01:00
2024-12-06 23:26:11 +01:00
/**
* @typedef {{
* buf: Uint8Array,
* strTable: Map<number, string>,
* idTable: number[]
* }} ParseContext
*/
2024-12-05 02:24:26 +01:00
2024-12-06 23:26:11 +01:00
/**
* @param {Uint8Array} buf
* @param {number} i
* @returns {number}
*/
function readU32(buf, i) {
return (buf[i] << 24) + (buf[i + 1] << 16) + (buf[i + 2] << 8) +
buf[i + 3];
2024-12-05 02:24:26 +01:00
}
2024-12-06 23:26:11 +01:00
/**
* @param {ParseContext} ctx
* @param {number} id
* @returns {string}
*/
function getString(ctx, id) {
const name = ctx.strTable.get(id);
if (name === undefined) {
throw new Error(`Missing string id: ${id}`);
2024-12-05 01:18:08 +01:00
}
2024-12-06 23:26:11 +01:00
return name;
2024-12-05 01:18:08 +01:00
}
2024-12-06 23:26:11 +01:00
/**
* @param {ParseContext} ctx
* @param {number} id
* @returns {Deno.AstNode}
*/
function createAstNode(ctx, id) {
const { buf, idTable } = ctx;
const i = idTable[id];
/** @type {AstNodeId} */
const kind = buf[i];
const flags = buf[i];
const count = readU32(buf, i + 2);
const rangeStart = readU32(buf, i + 6);
const rangeEnd = readU32(buf, i + 10);
const range = /** @type {Deno.Range} */ ([rangeStart, rangeEnd]);
switch (kind) {
case AstNodeId.Program:
return new Program(ctx, range, flags === 1 ? "module" : "script");
case AstNodeId.Import:
// case AstNodeId.ImportDecl:
// case AstNodeId.ExportDecl:
// case AstNodeId.ExportNamed:
// case AstNodeId.ExportDefaultDecl:
// case AstNodeId.ExportDefaultExpr:
// case AstNodeId.ExportAll:
// Statements
case AstNodeId.BlockStatement:
throw new BlockStatement(ctx, range); // FIXME
case AstNodeId.BreakStatement:
throw new BreakStatement(ctx, range, 0); // FIXME
case AstNodeId.ContinueStatement:
throw new ContinueStatement(ctx, range, 0); // FIXME
case AstNodeId.DebuggerStatement:
throw new DebuggerStatement(ctx, range);
case AstNodeId.DoWhileStatement:
throw new DoWhileStatement(ctx, range, 0, 0); // FIXME
case AstNodeId.ExpressionStatement:
return new ExpressionStatement(ctx, range, 0); // FIXME
case AstNodeId.ForInStatement:
throw new ForInStatement(ctx, range, 0, 0, 0); // FIXME
case AstNodeId.ForOfStatement:
throw new ForOfStatement(ctx, range, false, 0, 0, 0); // FIXME
case AstNodeId.ForStatement:
throw new ForStatement(ctx, range, 0, 0, 0, 0); // FIXME
case AstNodeId.IfStatement:
throw new IfStatement(ctx, range, 0, 0, 0); // FIXME
case AstNodeId.LabeledStatement:
throw new LabeledStatement(ctx, range, 0, 0); // FIXME
case AstNodeId.ReturnStatement:
throw new ReturnStatement(ctx, range, 0); // FIXME
case AstNodeId.SwitchStatement:
throw new SwitchStatement(ctx, range, 0); // FIXME
case AstNodeId.ThrowStatement:
throw new ThrowStatement(ctx, range, 0); // FIXME
case AstNodeId.TryStatement:
throw new TryStatement(ctx, range, 0, 0, 0); // FIXME
case AstNodeId.WhileStatement:
throw new WhileStatement(ctx, range, 0, 0); // FIXME
case AstNodeId.WithStatement:
throw new WithStatement(ctx, range, 0, 0);
// Expressions
case AstNodeId.ArrayExpression:
throw new ArrayExpression(ctx, range); // FIXME
case AstNodeId.ArrowFunctionExpression:
throw new ArrowFunctionExpression(ctx, range, false, false, 0); // FIXME
case AstNodeId.AssignmentExpression:
throw new AssignmentExpression(ctx, range, flags, 0, 0); // FIXME
case AstNodeId.AwaitExpression:
throw new AwaitExpression(ctx, range, 0); // FIXME
case AstNodeId.BinaryExpression:
throw new BinaryExpression(ctx, range, flags, 0, 0); // FIXME
case AstNodeId.CallExpression:
throw new CallExpression(ctx, range, 0); // FIXME
case AstNodeId.ChainExpression:
throw new ChainExpression(ctx, range); // FIXME
case AstNodeId.ConditionalExpression:
throw new ConditionalExpression(ctx, range, 0, 0, 0); // FIXME
case AstNodeId.FunctionExpression:
throw new FunctionExpression(ctx, range); // FIXME
case AstNodeId.Identifier:
throw new Identifier(ctx, range, flags); // FIXME
case AstNodeId.LogicalExpression:
throw new LogicalExpression(ctx, range, flags, 0, 0); // FIXME
case AstNodeId.MemberExpression:
throw new MemberExpression(ctx, range, flags, 0, 0); // FIXME
case AstNodeId.MetaProperty:
throw new Error("TODO");
case AstNodeId.NewExpression:
throw new Error("TODO");
case AstNodeId.ObjectExpression:
throw new Error("TODO");
case AstNodeId.StaticBlock:
throw new Error("TODO");
case AstNodeId.SequenceExpression:
throw new Error("TODO");
case AstNodeId.Super:
throw new Error("TODO");
case AstNodeId.TaggedTemplateExpression:
throw new Error("TODO");
case AstNodeId.TemplateLiteral:
throw new Error("TODO");
// Literals
case AstNodeId.BooleanLiteral:
throw new BooleanLiteral(ctx, range, flags); // FIXME
case AstNodeId.NullLiteral:
throw new NullLiteral(ctx, range); // FIXME
case AstNodeId.NumericLiteral:
throw new NumericLiteral(ctx, range, flags); // FIXME
case AstNodeId.RegExpLiteral:
throw new RegExpLiteral(ctx, range, flags); // FIXME
case AstNodeId.StringLiteral:
throw new StringLiteral(ctx, range, flags); // FIXME
default:
throw new Error(`Unknown ast node ${kind}`);
2024-12-05 01:18:08 +01:00
}
}
2024-12-04 16:21:17 +01:00
/**
* @param {Uint8Array} ast
*/
function buildAstFromBinary(ast) {
2024-12-05 11:21:34 +01:00
console.log(ast);
// Extract string table
/** @type {Map<number, string>} */
const strTable = new Map();
let start = 0;
const stringCount = (ast[0] << 24) + (ast[1] << 16) + (ast[2] << 8) +
ast[3];
start += 4;
let id = 0;
while (id < stringCount) {
const len = (ast[start] << 24) + (ast[start + 1] << 16) +
(ast[start + 2] << 8) +
ast[start + 3];
start += 4;
const strBytes = ast.slice(start, start + len);
console.log({ strBytes });
start += len;
const s = DECODER.decode(strBytes);
strTable.set(id, s);
id++;
}
console.log({ stringCount, strTable });
if (strTable.size !== stringCount) {
throw new Error(
`Could not deserialize string table. Expected ${stringCount} items, but got ${strTable.size}`,
);
}
2024-12-05 01:18:08 +01:00
const counts = [];
2024-12-04 16:21:17 +01:00
const stack = [];
2024-12-05 11:21:34 +01:00
for (let i = start; i < ast.length; i += 14) {
2024-12-04 16:21:17 +01:00
const kind = ast[i];
2024-12-05 01:18:08 +01:00
const flags = ast[i + 1];
2024-12-05 11:21:34 +01:00
let count = (ast[i + 2] << 24) + (ast[i + 3] << 16) + (ast[i + 4] << 8) +
ast[i + 5];
const start = (ast[i + 6] << 24) + (ast[i + 7] << 16) + (ast[i + 8] << 8) +
ast[i + 9];
const end = (ast[i + 10] << 24) + (ast[i + 11] << 16) +
(ast[i + 12] << 8) +
ast[i + 13];
2024-12-05 01:18:08 +01:00
2024-12-05 14:34:22 +01:00
const span = [start, end];
2024-12-05 01:18:08 +01:00
let node = null;
switch (kind) {
2024-12-06 23:26:11 +01:00
case AstNodeId.Program:
2024-12-05 11:21:34 +01:00
node = new Program(span, flags === 1 ? "module" : "script");
2024-12-05 01:18:08 +01:00
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.Var:
2024-12-05 01:18:08 +01:00
node = new VariableDeclaration(span);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.VarDeclarator:
2024-12-05 01:18:08 +01:00
node = new VariableDeclarator(span);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.ExpressionStatement:
2024-12-05 01:18:08 +01:00
node = new ExpressionStatement(span);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.This:
2024-12-05 01:18:08 +01:00
node = new ThisExpression(span);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.ArrayExpression:
2024-12-05 01:18:08 +01:00
node = new ArrayExpression(span);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.ObjectExpression:
2024-12-05 01:18:08 +01:00
node = new ObjectExpression(span);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.AssignmentExpression:
2024-12-05 01:18:08 +01:00
node = new ObjectExpression(span);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.MemberExpression:
2024-12-05 01:18:08 +01:00
node = new MemberExpression(span);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.CallExpression:
2024-12-05 01:18:08 +01:00
node = new CallExpression(span);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.SequenceExpression:
2024-12-05 01:18:08 +01:00
node = new SequenceExpression(span);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.ObjProperty:
2024-12-05 01:18:08 +01:00
node = new ObjectProperty(span);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.ArrowFunctionExpression:
2024-12-05 01:18:08 +01:00
node = new ArrowFunctionExpression(span);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.BlockStatement:
2024-12-05 01:18:08 +01:00
node = new BlockStatement(span);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.StringLiteral:
2024-12-05 01:18:08 +01:00
node = new StringLiteral(span);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.Identifier:
2024-12-05 11:21:34 +01:00
node = new Identifier(span, strTable.get(count));
count = 0;
2024-12-05 01:18:08 +01:00
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.Fn:
2024-12-05 01:18:08 +01:00
node = new FunctionDeclaration(span);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.ReturnStatement:
2024-12-05 01:18:08 +01:00
node = new ReturnStatement(span);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.IfStatement:
2024-12-05 01:18:08 +01:00
node = new IfStatement(span);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.BinaryExpression:
2024-12-05 01:18:08 +01:00
node = new LogicalExpression(span);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.Unary:
2024-12-05 01:18:08 +01:00
node = new UnaryExpression(span);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.Update:
2024-12-05 01:18:08 +01:00
node = new UpdateExpression(span);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.ForStatement:
2024-12-05 01:18:08 +01:00
node = new ForStatement(span);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.BooleanLiteral:
2024-12-05 11:21:34 +01:00
node = new BooleanLiteral(span, flags === 1);
2024-12-05 01:18:08 +01:00
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.NullLiteral:
2024-12-05 01:18:08 +01:00
node = new NullLiteral(span);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.NumericLiteral:
2024-12-05 01:18:08 +01:00
node = new NumericLiteral(span);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.RegExpLiteral:
2024-12-05 01:18:08 +01:00
node = new RegExpLiteral(span);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.ForInStatement:
2024-12-05 01:18:08 +01:00
node = new ForInStatement(span);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.ForOfStatement:
2024-12-05 01:18:08 +01:00
node = new ForOfStatement(span);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.WhileStatement:
2024-12-05 01:18:08 +01:00
node = new WhileStatement(span);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.Yield:
2024-12-05 01:18:08 +01:00
node = new YieldExpression(span);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.ContinueStatement:
2024-12-05 01:18:08 +01:00
node = new ContinueStatement(span);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.BreakStatement:
2024-12-05 01:18:08 +01:00
node = new BreakStatement(span);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.ConditionalExpression:
2024-12-05 01:18:08 +01:00
node = new ConditionalExpression(span);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.SwitchStatement:
2024-12-05 01:18:08 +01:00
node = new SwitchStatement(span);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.SwitchCase:
2024-12-05 01:18:08 +01:00
node = new SwitchCase(span);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.LabeledStatement:
2024-12-05 01:18:08 +01:00
node = new LabeledStatement(span);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.DoWhileStatement:
2024-12-05 01:18:08 +01:00
node = new DoWhileStatement(span);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.Spread:
2024-12-05 01:18:08 +01:00
node = new SpreadElement(span);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.ThrowStatement:
2024-12-05 01:18:08 +01:00
node = new ThrowStatement(span);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.DebuggerStatement:
2024-12-05 01:18:08 +01:00
node = new DebuggerStatement(span);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.Tpl:
2024-12-05 01:18:08 +01:00
node = new TemplateLiteral(span);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.NewExpression:
2024-12-05 01:18:08 +01:00
node = new NewExpression(span);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.Class:
2024-12-05 01:18:08 +01:00
node = new ClassDeclaration(span);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.TryStatement:
2024-12-05 02:24:26 +01:00
node = new TryStatement(span);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.CatchClause:
2024-12-05 02:24:26 +01:00
node = new CatchClause(span);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.TaggedTemplateExpression:
2024-12-05 02:24:26 +01:00
node = new TaggedTemplateExpression(span);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.FunctionExpression:
2024-12-05 02:24:26 +01:00
node = new FunctionExpression(span);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.Empty:
2024-12-05 02:24:26 +01:00
// Ignore empty statements
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.EmptyExpr:
2024-12-05 02:24:26 +01:00
// Nothing, AST defaults to null
break;
2024-12-05 01:18:08 +01:00
default:
throw new Error(`Unknown node: ${kind}`);
}
// append node
if (stack.length > 0) {
const last = stack[stack.length - 1];
const id = last[_ID];
const lastCount = counts[counts.length - 1];
2024-12-05 02:24:26 +01:00
// console.log({ last, node });
2024-12-05 01:18:08 +01:00
switch (id) {
2024-12-06 23:26:11 +01:00
case AstNodeId.Program:
case AstNodeId.BlockStatement:
2024-12-05 01:18:08 +01:00
last.body.push(node);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.ExpressionStatement:
2024-12-05 01:18:08 +01:00
last.expression = node;
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.ObjProperty:
2024-12-05 01:18:08 +01:00
if (lastCount > 1) {
last.value = node;
} else {
last.key = node;
}
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.MemberExpression:
2024-12-05 01:18:08 +01:00
if (lastCount > 1) {
last.property = node;
} else {
last.object = node;
}
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.CallExpression:
2024-12-05 01:18:08 +01:00
if (lastCount > 1) {
last.arguments.push(node);
} else {
last.callee = node;
}
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.SequenceExpression:
2024-12-05 01:18:08 +01:00
last.expressions.push(node);
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.ArrowFunctionExpression:
2024-12-05 01:18:08 +01:00
// FIXME
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.ReturnStatement:
case AstNodeId.Spread:
case AstNodeId.ThrowStatement:
case AstNodeId.Unary:
case AstNodeId.Update:
case AstNodeId.Yield:
2024-12-05 01:18:08 +01:00
last.argument = node;
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.IfStatement:
case AstNodeId.ConditionalExpression:
2024-12-05 01:18:08 +01:00
if (lastCount === 3) {
last.alternate = node;
} else if (lastCount === 2) {
last.consequent = node;
} else {
last.test = node;
}
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.BinaryExpression:
2024-12-05 01:18:08 +01:00
if (lastCount === 2) {
last.right = node;
} else {
last.left = node;
}
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.ForStatement:
2024-12-05 01:18:08 +01:00
if (lastCount === 4) {
last.body = node;
} else if (lastCount === 3) {
last.update = node;
} else if (lastCount === 2) {
last.test = node;
} else if (lastCount === 1) {
last.init = node;
}
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.ForInStatement:
case AstNodeId.ForOfStatement:
2024-12-05 01:18:08 +01:00
if (lastCount === 3) {
last.body = node;
} else if (lastCount === 2) {
last.right = node;
} else {
last.left = node;
}
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.DoWhileStatement:
case AstNodeId.WhileStatement:
2024-12-05 01:18:08 +01:00
if (lastCount === 2) {
last.body = node;
} else {
last.test = node;
}
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.BreakStatement:
case AstNodeId.ContinueStatement:
2024-12-05 01:18:08 +01:00
last.label = node;
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.SwitchStatement:
2024-12-05 01:18:08 +01:00
if (lastCount > 1) {
last.cases.push(node);
} else {
last.discriminant = node;
}
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.SwitchCase:
2024-12-05 01:18:08 +01:00
if (lastCount > 1) {
last.consequent = node;
} else {
last.test = node;
}
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.LabeledStatement:
2024-12-05 01:18:08 +01:00
last.body = node;
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.VarDeclarator:
2024-12-05 01:18:08 +01:00
if (lastCount > 1) {
last.init = node;
} else {
last.id = node;
}
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.NewExpression:
2024-12-05 01:18:08 +01:00
// FIXME
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.Class:
2024-12-05 01:18:08 +01:00
// FIXME
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.TryStatement:
2024-12-05 02:24:26 +01:00
// FIXME
break;
2024-12-06 23:26:11 +01:00
case AstNodeId.CatchClause:
2024-12-05 02:24:26 +01:00
// FIXME
break;
2024-12-05 01:18:08 +01:00
// Can't happen
2024-12-06 23:26:11 +01:00
case AstNodeId.Identifier:
case AstNodeId.StringLiteral:
case AstNodeId.BigInt:
case AstNodeId.BooleanLiteral:
case AstNodeId.NullLiteral:
case AstNodeId.NumericLiteral:
case AstNodeId.RegExpLiteral:
case AstNodeId.This:
case AstNodeId.DebuggerStatement:
2024-12-05 01:18:08 +01:00
break;
}
2024-12-05 02:24:26 +01:00
// console.log("APPENDED");
// console.log(last);
// console.log("======");
2024-12-05 01:18:08 +01:00
// Decrease count
2024-12-05 02:24:26 +01:00
const newCount = lastCount - 1;
counts[counts.length - 1] = newCount;
2024-12-05 01:18:08 +01:00
}
if (count > 0) {
stack.push(node);
counts.push(count);
2024-12-05 02:24:26 +01:00
} else if (stack.length > 0) {
let lastCount = counts[counts.length - 1];
while (stack.length > 1 && lastCount === 0) {
// console.log({ counts, s: stack.map((x) => x.type) });
const l = stack.pop();
// console.log("POP", l);
lastCount = counts.pop();
}
2024-12-04 16:21:17 +01:00
}
}
2024-12-05 01:18:08 +01:00
2024-12-05 02:24:26 +01:00
// console.log(JSON.stringify(stack, null, 2));
2024-12-05 01:18:08 +01:00
return stack[0];
2024-12-04 16:21:17 +01:00
}
2024-12-05 11:21:34 +01:00
export function runPluginsForFile(fileName, serializedAst) {
const ast = buildAstFromBinary(serializedAst);
2024-12-05 14:34:22 +01:00
console.log(JSON.stringify(ast, null, 2));
2024-12-04 12:42:55 +01:00
/** @type {Record<string, (node: any) => void} */
const mergedVisitor = {};
const destroyFns = [];
// Instantiate and merge visitors. This allows us to only traverse
// the AST once instead of per plugin.
for (let i = 0; i < state.plugins; i++) {
const plugin = state.plugins[i];
for (const name of Object.keys(plugin)) {
const rule = plugin.rules[name];
const id = `${plugin.name}/${ruleName}`;
const ctx = new Context(id, fileName);
const visitor = rule.create(ctx);
for (const name in visitor) {
const prev = mergedVisitor[name];
mergedVisitor[name] = (node) => {
if (typeof prev === "function") {
prev(node);
}
try {
visitor[name](node);
} catch (err) {
throw new Error(`Visitor "${name}" of plugin "${id}" errored`, {
cause: err,
});
}
};
}
mergedVisitor.push({ ctx, visitor, rule });
if (typeof rule.destroy === "function") {
destroyFns.push(() => {
try {
rule.destroy(ctx);
} catch (err) {
throw new Error(`Destroy hook of "${id}" errored`, { cause: err });
}
});
}
}
2024-12-04 12:42:55 +01:00
}
2024-12-04 12:42:55 +01:00
// Traverse ast with all visitors at the same time to avoid traversing
// multiple times.
traverse(ast, mergedVisitor, null);
2024-12-04 12:42:55 +01:00
// Optional: Destroy rules
for (let i = 0; i < destroyFns.length; i++) {
destroyFns[i]();
}
}
2024-12-04 12:42:55 +01:00
/**
* @param {Record<string, any>} ast
* @param {*} visitor
* @param {any | null} parent
* @returns {void}
*/
function traverse(ast, visitor, parent) {
2024-12-03 02:51:29 +01:00
if (!ast || typeof ast !== "object") {
return;
2024-12-01 05:14:46 +01:00
}
2024-12-03 02:51:29 +01:00
// Get node type, accounting for SWC's type property naming
const nodeType = ast.type || (ast.nodeType ? ast.nodeType : null);
// Skip if not a valid AST node
if (!nodeType) {
return;
2024-12-01 05:14:46 +01:00
}
2024-12-03 02:51:29 +01:00
ast.parent = parent;
2024-12-04 12:42:55 +01:00
2024-12-03 02:51:29 +01:00
// Call visitor if it exists for this node type
2024-12-04 12:42:55 +01:00
visitor[nodeType]?.(ast);
2024-12-03 02:51:29 +01:00
// Traverse child nodes
for (const key in ast) {
2024-12-03 03:11:26 +01:00
if (key === "parent" || key === "type") {
2024-12-03 02:51:29 +01:00
continue;
2024-12-01 05:14:46 +01:00
}
2024-12-03 02:51:29 +01:00
const child = ast[key];
if (Array.isArray(child)) {
2024-12-04 12:42:55 +01:00
for (let i = 0; i < child.length; i++) {
const item = child[i];
traverse(item, visitor, ast);
}
} else if (child !== null && typeof child === "object") {
2024-12-03 02:51:29 +01:00
traverse(child, visitor, ast);
2024-12-01 05:14:46 +01:00
}
}
2024-12-01 04:53:47 +01:00
}