1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-01-21 04:52:26 -05:00

get selector integration working

This commit is contained in:
Marvin Hagemeister 2024-12-17 12:04:12 +01:00
parent d9fd959820
commit 6e70ba2c77
6 changed files with 194 additions and 69 deletions

View file

@ -31,39 +31,13 @@ const PropFlags = {
Undefined: 5, Undefined: 5,
}; };
/** /** @typedef {import("./40_lint_types.d.ts").AstContext} AstContext */
* @typedef {{ /** @typedef {import("./40_lint_types.d.ts").VisitorFn} VisitorFn */
* buf: Uint8Array, /** @typedef {import("./40_lint_types.d.ts").MatcherFn} MatcherFn */
* strTable: Map<number, string>, /** @typedef {import("./40_lint_types.d.ts").TransformFn} TransformerFn */
* strTableOffset: number, /** @typedef {import("./40_lint_types.d.ts").CompiledVisitor} CompiledVisitor */
* rootId: number, /** @typedef {import("./40_lint_types.d.ts").LintState} LintState */
* nodes: Map<number, Node>, /** @typedef {import("./40_lint_types.d.ts").MatchContext} MatchContext */
* strByType: number[],
* strByProp: number[]
* typeByStr: Map<string, number>,
* propByStr: Map<string, number>,
* }} AstContext
*/
/**
* @typedef {{
* plugins: Deno.LintPlugin[],
* installedPlugins: Set<string>,
* }} LintState
*/
/**
* @typedef {import("./40_lint_types.d.ts").VisitorFn} VisitorFn
*/
/**
* @typedef {import("./40_lint_types.d.ts").MatcherFn} MatcherFn
*/
/**
* @typedef {import("./40_lint_types.d.ts").TransformFn} TransformerFn
*/
/**
* @typedef {import("./40_lint_types.d.ts").CompiledVisitor} CompiledVisitor
*/
/** @type {LintState} */ /** @type {LintState} */
const state = { const state = {
@ -201,7 +175,7 @@ function readValue(ctx, offset, search) {
const type = buf[offset]; const type = buf[offset];
if (search === AST_PROP_TYPE) { if (search === AST_PROP_TYPE) {
return getString(ctx, ctx.strByType[type]); return getString(ctx.strTable, ctx.strByType[type]);
} else if (search === AST_PROP_RANGE) { } else if (search === AST_PROP_RANGE) {
const start = readU32(buf, offset + 1 + 4); const start = readU32(buf, offset + 1 + 4);
const end = readU32(buf, offset + 1 + 4 + 4); const end = readU32(buf, offset + 1 + 4 + 4);
@ -233,7 +207,7 @@ function readValue(ctx, offset, search) {
return buf[offset] === 1; return buf[offset] === 1;
} else if (kind === PropFlags.String) { } else if (kind === PropFlags.String) {
const v = readU32(buf, offset); const v = readU32(buf, offset);
return getString(ctx, v); return getString(ctx.strTable, v);
} else if (kind === PropFlags.Null) { } else if (kind === PropFlags.Null) {
return null; return null;
} else if (kind === PropFlags.Undefined) { } else if (kind === PropFlags.Undefined) {
@ -264,7 +238,7 @@ function toJsValue(ctx, offset) {
for (let i = 0; i < count; i++) { for (let i = 0; i < count; i++) {
const prop = buf[offset++]; const prop = buf[offset++];
const kind = buf[offset++]; const kind = buf[offset++];
const name = getString(ctx, ctx.strByProp[prop]); const name = getString(ctx.strTable, ctx.strByProp[prop]);
if (kind === PropFlags.Ref) { if (kind === PropFlags.Ref) {
const v = readU32(buf, offset); const v = readU32(buf, offset);
@ -287,7 +261,7 @@ function toJsValue(ctx, offset) {
} else if (kind === PropFlags.String) { } else if (kind === PropFlags.String) {
const v = readU32(buf, offset); const v = readU32(buf, offset);
offset += 4; offset += 4;
node[name] = getString(ctx, v); node[name] = getString(ctx.strTable, v);
} else if (kind === PropFlags.Null) { } else if (kind === PropFlags.Null) {
node[name] = null; node[name] = null;
} else if (kind === PropFlags.Undefined) { } else if (kind === PropFlags.Undefined) {
@ -339,7 +313,7 @@ function setNodeGetters(ctx) {
if (id === 0 || appliedGetters.has(i)) continue; if (id === 0 || appliedGetters.has(i)) continue;
appliedGetters.add(i); appliedGetters.add(i);
const name = getString(ctx, id); const name = getString(ctx.strTable, id);
Object.defineProperty(Node.prototype, name, { Object.defineProperty(Node.prototype, name, {
get() { get() {
@ -362,12 +336,12 @@ function readU32(buf, i) {
} }
/** /**
* @param {AstContext} ctx * @param {AstContext["strTable"]} strTable
* @param {number} id * @param {number} id
* @returns {string} * @returns {string}
*/ */
function getString(ctx, id) { function getString(strTable, id) {
const name = ctx.strTable.get(id); const name = strTable.get(id);
if (name === undefined) { if (name === undefined) {
throw new Error(`Missing string id: ${id}`); throw new Error(`Missing string id: ${id}`);
} }
@ -375,6 +349,128 @@ function getString(ctx, id) {
return name; return name;
} }
/** @implements {MatchContext} */
class MatchCtx {
/**
* @param {AstContext["buf"]} buf
* @param {AstContext["strTable"]} strTable
*/
constructor(buf, strTable) {
this.buf = buf;
this.strTable = strTable;
}
/**
* @param {number} offset
* @returns {number}
*/
getParent(offset) {
return readU32(this.buf, offset + 1);
}
/**
* @param {number} offset
* @returns {number}
*/
getType(offset) {
return this.buf[offset];
}
/**
* @param {number} offset
* @param {number[]} propIds
* @param {number} idx
* @returns {unknown}
*/
getAttrPathValue(offset, propIds, idx) {
const { buf } = this;
offset = findPropOffset(buf, offset, propIds[idx]);
// console.log("attr value", offset, propIds, idx);
if (offset === -1) return undefined;
const prop = buf[offset++];
const kind = buf[offset++];
if (kind === PropFlags.Ref) {
const value = readU32(buf, offset);
// Checks need to end with a value, not a node
if (idx === propIds.length - 1) return undefined;
return this.getAttrPathValue(value, propIds, idx + 1);
} else if (kind === PropFlags.RefArr) {
// FIXME
const count = readU32(buf, offset);
offset += 4;
}
// Cannot traverse into primitives further
if (idx < propIds.length - 1) return undefined;
if (kind === PropFlags.String) {
const s = readU32(buf, offset);
return getString(this.strTable, s);
} else if (kind === PropFlags.Bool) {
return buf[offset] === 1;
} else if (kind === PropFlags.Null) {
return null;
} else if (kind === PropFlags.Undefined) {
return undefined;
}
return undefined;
}
/**
* @param {number} offset
* @param {number[]} propIds
* @param {number} idx
* @returns {boolean}
*/
hasAttrPath(offset, propIds, idx) {
const { buf } = this;
offset = findPropOffset(buf, offset, propIds[idx]);
// console.log("attr path", offset, propIds, idx);
if (offset === -1) return false;
if (idx === propIds.length - 1) return true;
const prop = buf[offset++];
const kind = buf[offset++];
if (kind === PropFlags.Ref) {
const value = readU32(buf, offset);
return this.hasAttrPath(value, propIds, idx + 1);
} else if (kind === PropFlags.RefArr) {
const count = readU32(buf, offset);
offset += 4;
// FIXME
}
// Primitives cannot be traversed further. This means we
// didn't found the attribute.
if (idx < propIds.length - 1) return false;
return true;
}
getFirstChild() {
// FIXME
return 0;
}
getLastChild() {
// FIXME
return 0;
}
getSiblingBefore() {
// FIXME
return 0;
}
getSiblings() {
// FIXME
return [];
}
}
/** /**
* @param {Uint8Array} buf * @param {Uint8Array} buf
* @param {AstContext} buf * @param {AstContext} buf
@ -455,6 +551,7 @@ function createAstContext(buf) {
strByType, strByType,
typeByStr, typeByStr,
propByStr, propByStr,
matcher: new MatchCtx(buf, strTable),
}; };
setNodeGetters(ctx); setNodeGetters(ctx);
@ -499,6 +596,7 @@ export function runPluginsForFile(fileName, serializedAst) {
for (let key in visitor) { for (let key in visitor) {
const fn = visitor[key]; const fn = visitor[key];
if (fn === undefined) continue;
let isExit = false; let isExit = false;
if (key.endsWith(":exit")) { if (key.endsWith(":exit")) {
@ -563,7 +661,7 @@ export function runPluginsForFile(fileName, serializedAst) {
}; };
/** @type {TransformerFn} */ /** @type {TransformerFn} */
const toAttr = (str) => { const toAttr = (str) => {
const id = ctx.typeByStr.get(str); const id = ctx.propByStr.get(str);
if (id === undefined) throw new Error(`Unknown elem: ${str}`); if (id === undefined) throw new Error(`Unknown elem: ${str}`);
return id; return id;
}; };
@ -571,8 +669,10 @@ export function runPluginsForFile(fileName, serializedAst) {
/** @type {CompiledVisitor[]} */ /** @type {CompiledVisitor[]} */
const visitors = []; const visitors = [];
for (const [sel, info] of bySelector.entries()) { for (const [sel, info] of bySelector.entries()) {
const compiled = parseSelector(sel, toElem, toAttr); // Selectors are already split here.
const matcher = compileSelector(compiled); // TODO: Avoid array allocation (not sure if that matters)
const parsed = parseSelector(sel, toElem, toAttr)[0];
const matcher = compileSelector(parsed);
visitors.push({ visitors.push({
info, info,
@ -616,8 +716,7 @@ function traverse(ctx, visitors, offset) {
continue; continue;
} }
// FIXME: add matcher context methods if (v.matcher(ctx.matcher, offset)) {
if (v.matcher(ctx, offset)) {
const node = /** @type {*} */ (getNode(ctx, offset)); const node = /** @type {*} */ (getNode(ctx, offset));
v.info.enter(node); v.info.enter(node);
@ -684,14 +783,14 @@ function _dump(ctx) {
for (let i = 0; i < strByType.length; i++) { for (let i = 0; i < strByType.length; i++) {
const v = strByType[i]; const v = strByType[i];
// @ts-ignore dump fn // @ts-ignore dump fn
if (v > 0) console.log(" > type:", i, getString(ctx, v), v); if (v > 0) console.log(" > type:", i, getString(ctx.strTable, v), v);
} }
// @ts-ignore dump fn // @ts-ignore dump fn
console.log(); console.log();
for (let i = 0; i < strByProp.length; i++) { for (let i = 0; i < strByProp.length; i++) {
const v = strByProp[i]; const v = strByProp[i];
// @ts-ignore dump fn // @ts-ignore dump fn
if (v > 0) console.log(" > prop:", i, getString(ctx, v), v); if (v > 0) console.log(" > prop:", i, getString(ctx.strTable, v), v);
} }
// @ts-ignore dump fn // @ts-ignore dump fn
console.log(); console.log();
@ -700,7 +799,7 @@ function _dump(ctx) {
while (offset < strTableOffset) { while (offset < strTableOffset) {
const type = buf[offset]; const type = buf[offset];
const name = getString(ctx, ctx.strByType[type]); const name = getString(ctx.strTable, ctx.strByType[type]);
// @ts-ignore dump fn // @ts-ignore dump fn
console.log(`${name}, offset: ${offset}, type: ${type}`); console.log(`${name}, offset: ${offset}, type: ${type}`);
offset += 1; offset += 1;
@ -724,7 +823,7 @@ function _dump(ctx) {
for (let i = 0; i < count; i++) { for (let i = 0; i < count; i++) {
const prop = buf[offset++]; const prop = buf[offset++];
const kind = buf[offset++]; const kind = buf[offset++];
const name = getString(ctx, ctx.strByProp[prop]); const name = getString(ctx.strTable, ctx.strByProp[prop]);
let kindName = "unknown"; let kindName = "unknown";
for (const k in PropFlags) { for (const k in PropFlags) {
@ -760,7 +859,9 @@ function _dump(ctx) {
const v = readU32(buf, offset); const v = readU32(buf, offset);
offset += 4; offset += 4;
// @ts-ignore dump fn // @ts-ignore dump fn
console.log(` ${name}: ${getString(ctx, v)} (${kindName}, ${prop})`); console.log(
` ${name}: ${getString(ctx.strTable, v)} (${kindName}, ${prop})`,
);
} else if (kind === PropFlags.Null) { } else if (kind === PropFlags.Null) {
// @ts-ignore dump fn // @ts-ignore dump fn
console.log(` ${name}: null (${kindName}, ${prop})`); console.log(` ${name}: null (${kindName}, ${prop})`);

View file

@ -4,7 +4,7 @@
/** @typedef {import("./40_lint_types.d.ts").LintState} LintState */ /** @typedef {import("./40_lint_types.d.ts").LintState} LintState */
/** @typedef {import("./40_lint_types.d.ts").AstContext} AstContext */ /** @typedef {import("./40_lint_types.d.ts").AstContext} AstContext */
/** @typedef {import("./40_lint_types.d.ts").MatchCtx} MatchCtx */ /** @typedef {import("./40_lint_types.d.ts").MatchContext} MatchCtx */
/** @typedef {import("./40_lint_types.d.ts").AttrExists} AttrExists */ /** @typedef {import("./40_lint_types.d.ts").AttrExists} AttrExists */
/** @typedef {import("./40_lint_types.d.ts").AttrBin} AttrBin */ /** @typedef {import("./40_lint_types.d.ts").AttrBin} AttrBin */
/** @typedef {import("./40_lint_types.d.ts").AttrSelector} AttrSelector */ /** @typedef {import("./40_lint_types.d.ts").AttrSelector} AttrSelector */
@ -685,7 +685,9 @@ function popSelector(result, stack) {
} }
} }
const TRUE_FN = () => true; const TRUE_FN = () => {
return true;
};
/** /**
* @param {Selector} selector * @param {Selector} selector
@ -742,6 +744,10 @@ export function compileSelector(selector) {
case PSEUDO_NOT: case PSEUDO_NOT:
fn = matchNot(node.selectors, fn); fn = matchNot(node.selectors, fn);
break; break;
default:
// @ts-ignore error handling
console.log(node);
throw new Error(`Unknown selector node`);
} }
} }
@ -943,7 +949,7 @@ function matchElem(part, next) {
*/ */
function matchAttrExists(attr, next) { function matchAttrExists(attr, next) {
return (ctx, id) => { return (ctx, id) => {
return ctx.hasAttrPath(id, attr.prop) ? next(ctx, id) : false; return ctx.hasAttrPath(id, attr.prop, 0) ? next(ctx, id) : false;
}; };
} }
@ -954,8 +960,8 @@ function matchAttrExists(attr, next) {
*/ */
function matchAttrBin(attr, next) { function matchAttrBin(attr, next) {
return (ctx, id) => { return (ctx, id) => {
if (!ctx.hasAttrPath(id, attr.prop)) return false; if (!ctx.hasAttrPath(id, attr.prop, 0)) return false;
const value = ctx.getAttrPathValue(id, attr.prop); const value = ctx.getAttrPathValue(id, attr.prop, 0);
if (!matchAttrValue(attr, value)) return false; if (!matchAttrValue(attr, value)) return false;
return next(ctx, id); return next(ctx, id);
}; };

View file

@ -1,11 +1,21 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
export interface NodeFacade {
readonly type: string;
readonly range: [number, number];
}
export interface AstContext { export interface AstContext {
buf: Uint8Array; buf: Uint8Array;
strTable: Map<number, string>; strTable: Map<number, string>;
idTable: number[]; strTableOffset: number;
rootId: number; rootId: number;
stack: number[]; nodes: Map<number, NodeFacade>;
strByType: number[];
strByProp: number[];
typeByStr: Map<string, number>;
propByStr: Map<string, number>;
matcher: MatchContext;
} }
export interface LintState { export interface LintState {
@ -89,19 +99,19 @@ export interface ILexer {
token: SelToken; token: SelToken;
} }
export interface MatchCtx { export interface MatchContext {
getFirstChild(id: number): number; getFirstChild(id: number): number;
getLastChild(id: number): number; getLastChild(id: number): number;
getSiblingBefore(parentId: number, sib: number): number; getSiblingBefore(parentId: number, sib: number): number;
getSiblings(id: number): number[]; getSiblings(id: number): number[];
getParent(id: number): number; getParent(id: number): number;
getType(id: number): number; getType(id: number): number;
hasAttrPath(id: number, propIds: number[]): boolean; hasAttrPath(id: number, propIds: number[], idx: number): boolean;
getAttrPathValue(id: number, propIds: number[]): unknown; getAttrPathValue(id: number, propIds: number[], idx: number): unknown;
} }
export type NextFn = (ctx: MatchCtx, id: number) => boolean; export type NextFn = (ctx: MatchContext, id: number) => boolean;
export type MatcherFn = (ctx: MatchCtx, id: number) => boolean; export type MatcherFn = (ctx: MatchContext, id: number) => boolean;
export type TransformFn = (value: string) => number; export type TransformFn = (value: string) => number;
export type VisitorFn = (node: Deno.AstNode) => void; export type VisitorFn = (node: Deno.AstNode) => void;

View file

@ -169,7 +169,7 @@ fn serialize_stmt(
) -> NodeRef { ) -> NodeRef {
match stmt { match stmt {
Stmt::Block(node) => { Stmt::Block(node) => {
let pos = ctx.header(AstNode::Block, parent, &node.span, 1); let pos = ctx.header(AstNode::BlockStatement, parent, &node.span, 1);
let body_pos = ctx.ref_vec_field(AstProp::Body, node.stmts.len()); let body_pos = ctx.ref_vec_field(AstProp::Body, node.stmts.len());
let children = node let children = node
@ -1437,7 +1437,12 @@ fn serialize_decl(
id id
} }
Decl::Fn(node) => { Decl::Fn(node) => {
let pos = ctx.header(AstNode::Fn, parent, &node.function.span, 8); let pos = ctx.header(
AstNode::FunctionDeclaration,
parent,
&node.function.span,
8,
);
let declare_pos = ctx.ref_field(AstProp::Declare); let declare_pos = ctx.ref_field(AstProp::Declare);
let async_pos = ctx.ref_field(AstProp::Async); let async_pos = ctx.ref_field(AstProp::Async);
let gen_pos = ctx.ref_field(AstProp::Generator); let gen_pos = ctx.ref_field(AstProp::Generator);
@ -1479,7 +1484,7 @@ fn serialize_decl(
pos pos
} }
Decl::Var(node) => { Decl::Var(node) => {
let id = ctx.header(AstNode::Var, parent, &node.span, 3); let id = ctx.header(AstNode::VariableDeclaration, parent, &node.span, 3);
let declare_pos = ctx.bool_field(AstProp::Declare); let declare_pos = ctx.bool_field(AstProp::Declare);
let kind_pos = ctx.str_field(AstProp::Kind); let kind_pos = ctx.str_field(AstProp::Kind);
let decls_pos = let decls_pos =

View file

@ -25,8 +25,8 @@ pub enum AstNode {
// Decls // Decls
ClassDeclaration, ClassDeclaration,
Fn, FunctionDeclaration,
Var, VariableDeclaration,
Using, Using,
TSInterface, TSInterface,
TsTypeAlias, TsTypeAlias,
@ -34,7 +34,7 @@ pub enum AstNode {
TsModule, TsModule,
// Statements // Statements
Block, BlockStatement,
Empty, Empty,
Debugger, Debugger,
With, With,

View file

@ -7159,6 +7159,9 @@ declare namespace Deno {
TSTypeAnnotation?(node: TSTypeAnnotation): void; TSTypeAnnotation?(node: TSTypeAnnotation): void;
TSTypeReference?(node: TSTypeReference): void; TSTypeReference?(node: TSTypeReference): void;
TSUnknownKeyword?(node: TSUnknownKeyword): void; TSUnknownKeyword?(node: TSUnknownKeyword): void;
// deno-lint-ignore no-explicit-any
[selector: string]: ((node: any) => void) | undefined;
} }
export interface LintRule { export interface LintRule {