From 2da8eee932f6d1075722c51c7f7282bacfc1db7b Mon Sep 17 00:00:00 2001 From: Marvin Hagemeister Date: Mon, 6 Jan 2025 19:04:42 +0100 Subject: [PATCH] WIP --- cli/js/40_lint.js | 158 ++++++++++++++-------------- cli/tools/lint/ast_buffer/buffer.rs | 134 ++++++++++++----------- cli/tools/lint/ast_buffer/swc.rs | 4 +- cli/tools/lint/mod.rs | 4 +- 4 files changed, 147 insertions(+), 153 deletions(-) diff --git a/cli/js/40_lint.js b/cli/js/40_lint.js index d8fdb903d7..854596847c 100644 --- a/cli/js/40_lint.js +++ b/cli/js/40_lint.js @@ -25,6 +25,10 @@ const AST_GROUP_TYPE = 1; /// /// const NODE_SIZE = 1 + 4 + 4 + 4 + 4; +const PROP_OFFSET = 1; +const CHILD_OFFSET = 1 + 4; +const NEXT_OFFSET = 1 + 4 + 4; +const PARENT_OFFSET = 1 + 4 + 4 + 4; // Span size in buffer: u32 + u32 const SPAN_SIZE = 4 + 4; @@ -285,16 +289,16 @@ export function installPlugin(plugin, exclude) { /** * @param {AstContext} ctx * @param {number} idx - * @returns + * @returns {FacadeNode | null} */ function getNode(ctx, idx) { if (idx === AST_IDX_INVALID) return null; const cached = ctx.nodes.get(idx); - if (cached !== undefined) return cached; + if (cached !== undefined) return /** @type {*} */ (cached); const node = new FacadeNode(ctx, idx); - ctx.nodes.set(idx, /** @type {*} */ (cached)); - return node; + ctx.nodes.set(idx, /** @type {*} */ (node)); + return /** @type {*} */ (node); } /** @@ -482,6 +486,15 @@ function toJsValue(ctx, idx) { return node; } +/** + * @param {AstContext["buf"]} buf + * @param {number} idx + * @returns {number} + */ +function readType(buf, idx) { + return buf[idx * NODE_SIZE]; +} + /** * @param {AstContext} ctx * @param {number} idx @@ -501,10 +514,8 @@ function readSpan(ctx, idx) { * @param {number} idx * @returns {number} */ -function readParent(buf, idx) { - let offset = idx * NODE_SIZE; - // type + prop offset + child + next - offset += 1 + 4 + 4 + 4; +function readPropOffset(buf, idx) { + const offset = (idx * NODE_SIZE) + PROP_OFFSET; return readU32(buf, offset); } @@ -513,10 +524,27 @@ function readParent(buf, idx) { * @param {number} idx * @returns {number} */ -function readPropOffset(buf, idx) { - let offset = idx * NODE_SIZE; - // type + prop offset + child + next - offset += 1; +function readChild(buf, idx) { + const offset = (idx * NODE_SIZE) * CHILD_OFFSET; + return readU32(buf, offset); +} +/** + * @param {AstContext["buf"]} buf + * @param {number} idx + * @returns {number} + */ +function readNext(buf, idx) { + const offset = (idx * NODE_SIZE) * NEXT_OFFSET; + return readU32(buf, offset); +} + +/** + * @param {AstContext["buf"]} buf + * @param {number} idx + * @returns {number} + */ +function readParent(buf, idx) { + const offset = (idx * NODE_SIZE) * PARENT_OFFSET; return readU32(buf, offset); } @@ -576,6 +604,7 @@ function readProperty(ctx, offset) { const value = readU32(buf, offset); return getNode(ctx, value); } else if (kind === PropFlags.RefArr) { + // FIXME: This is broken atm const len = readU32(buf, offset); offset += 4; @@ -618,9 +647,8 @@ function readProperty(ctx, offset) { function readValue(ctx, idx, search) { const { buf } = ctx; - const type = buf[idx * NODE_SIZE]; - if (search === AST_PROP_TYPE) { + const type = readType(buf, idx); return getString(ctx.strTable, ctx.strByType[type]); } else if (search === AST_PROP_RANGE) { return readSpan(ctx, idx); @@ -814,9 +842,8 @@ class MatchCtx { * @returns {number} */ getFirstChild(idx) { - let offset = idx * NODE_SIZE; - offset += 1; - return readU32(this.buf, offset); + const siblings = this.getSiblings(idx); + return siblings[0] ?? -1; } /** @@ -824,23 +851,8 @@ class MatchCtx { * @returns {number} */ getLastChild(idx) { - let offset = idx * NODE_SIZE; - offset += 1; - - let childId = readU32(this.buf, offset); - if (childId <= AST_IDX_INVALID) return -1; - - while (childId > AST_IDX_INVALID) { - let offset = childId * NODE_SIZE; - offset += 1 + 4; - - const nextId = readU32(this.buf, offset); - if (nextId <= AST_IDX_INVALID) break; - - childId = nextId; - } - - return childId; + const siblings = this.getSiblings(idx); + return siblings.at(-1) ?? -1; } /** @@ -848,32 +860,22 @@ class MatchCtx { * @returns {number[]} */ getSiblings(idx) { - // TODO(@marvinhagemeister): We can rewrite this to not - // use array allocation now. const { buf } = this; + const parent = readParent(buf, idx); - let offset = idx * NODE_SIZE; - offset += 1 + 4 + 4; - const parentIdx = readU32(buf, offset); - - const pOffset = parentIdx * NODE_SIZE; - const pType = buf[pOffset + 1]; - - /** @type {number[]} */ - const out = []; - - if (pType !== AST_GROUP_TYPE) { - return out; + // Only RefArrays have siblings + const parentType = readType(buf, parent); + if (parentType !== AST_GROUP_TYPE) { + return []; } - let childId = readU32(buf, pOffset + 1); - while (childId > AST_IDX_INVALID) { - out.push(childId); + const child = readChild(buf, parent); + const out = [child]; - const nextId = readU32(buf, pOffset + 1 + 4); - if (nextId <= AST_IDX_INVALID) break; - - childId = nextId; + let next = readNext(buf, child); + while (next > AST_IDX_INVALID) { + out.push(next); + next = readNext(buf, next); } return out; @@ -965,8 +967,6 @@ function createAstContext(buf) { setNodeGetters(ctx); - console.log(ctx); - // DEV ONLY: Enable this to inspect the buffer message _dump(ctx); @@ -1119,9 +1119,7 @@ function traverse(ctx, visitors, idx, cancellationToken) { if (cancellationToken.isCancellationRequested()) return; const { buf } = ctx; - - let offset = idx * NODE_SIZE; - const nodeType = buf[offset++]; + const nodeType = readType(ctx.buf, idx); /** @type {VisitorFn[] | null} */ let exits = null; @@ -1149,16 +1147,12 @@ function traverse(ctx, visitors, idx, cancellationToken) { } try { - const childIdx = readU32(buf, offset); - offset += 4; - + const childIdx = readChild(buf, idx); if (childIdx > AST_IDX_INVALID) { traverse(ctx, visitors, childIdx, cancellationToken); } - const nextIdx = readU32(buf, offset); - offset += 4; - + const nextIdx = readNext(buf, idx); if (nextIdx > AST_IDX_INVALID) { traverse(ctx, visitors, nextIdx, cancellationToken); } @@ -1204,23 +1198,27 @@ function _dump(ctx) { let idx = 0; while (idx < (strTableOffset / NODE_SIZE)) { - const offset = idx * NODE_SIZE; - - const type = buf[offset]; - const name = getString(ctx.strTable, ctx.strByType[type]); - // @ts-ignore dump fn - // deno-lint-ignore no-console - console.log(`${name}, idx: ${idx}, offset: ${offset}, type: ${type}`); - + const type = readType(buf, idx); + const child = readChild(buf, idx); + const next = readNext(buf, idx); const parent = readParent(buf, idx); - // @ts-ignore dump fn - // deno-lint-ignore no-console - console.log(` parent: ${parent}`); - const range = readSpan(ctx, idx); + + const name = type === AST_IDX_INVALID + ? "" + : type === AST_GROUP_TYPE + ? "" + : getString(ctx.strTable, ctx.strByType[type]); // @ts-ignore dump fn // deno-lint-ignore no-console - console.log(` range: ${JSON.stringify(range)}`); + console.log(`${name}, idx: ${idx}, type: ${type}`); + + // @ts-ignore dump fn + // deno-lint-ignore no-console + console.log(` child: ${child}, next: ${next}, parent: ${parent}`); + // @ts-ignore dump fn + // deno-lint-ignore no-console + console.log(` range: ${range[0]}, ${range[1]}`); let propOffset = readPropOffset(buf, idx); const count = buf[propOffset++]; @@ -1241,7 +1239,7 @@ function _dump(ctx) { } } - const v = readU32(buf, offset); + const v = readU32(buf, propOffset); propOffset += 4; if (kind === PropFlags.Ref) { diff --git a/cli/tools/lint/ast_buffer/buffer.rs b/cli/tools/lint/ast_buffer/buffer.rs index 1b5d5fa3d6..d312082ced 100644 --- a/cli/tools/lint/ast_buffer/buffer.rs +++ b/cli/tools/lint/ast_buffer/buffer.rs @@ -70,29 +70,11 @@ fn append_u32(result: &mut Vec, value: u32) { result.push(v4); } -#[inline] -fn write_u32(result: &mut [u8], value: u32, offset: usize) { - let v1: u8 = ((value & MASK_U32_1) >> 24) as u8; - let v2: u8 = ((value & MASK_U32_2) >> 16) as u8; - let v3: u8 = ((value & MASK_U32_3) >> 8) as u8; - let v4: u8 = (value & MASK_U32_4) as u8; - - result[offset] = v1; - result[offset + 1] = v2; - result[offset + 2] = v3; - result[offset + 3] = v4; -} - fn append_usize(result: &mut Vec, value: usize) { let raw = u32::try_from(value).unwrap(); append_u32(result, raw); } -fn write_usize(result: &mut [u8], value: usize, offset: usize) { - let raw = u32::try_from(value).unwrap(); - write_u32(result, raw, offset); -} - #[derive(Debug)] pub struct StringTable { id: usize, @@ -142,14 +124,27 @@ pub trait AstBufSerializer { fn serialize(&mut self) -> Vec; } +/// +/// +/// +/// +/// +#[derive(Debug)] +struct Node { + kind: u8, + prop_offset: u32, + child: u32, + next: u32, + parent: u32, +} + #[derive(Debug)] pub struct SerializeCtx { id: u32, start_buf: NodeRef, - /// Node buffer for traversal - buf: Vec, + nodes: Vec, field_buf: Vec, /// Vec of spans @@ -168,13 +163,6 @@ pub struct SerializeCtx { prev_sibling_node: Option, } -/// -/// -/// -/// -/// -const NODE_SIZE: u32 = 1 + 4 + 4 + 4 + 4; - /// This is the internal context used to allocate and fill the buffer. The point /// is to be able to write absolute offsets directly in place. /// @@ -190,7 +178,7 @@ impl SerializeCtx { id: 0, spans: vec![], start_buf: NodeRef(0), - buf: vec![], + nodes: vec![], field_buf: vec![], str_table: StringTable::new(), kind_name_map: vec![0; kind_size], @@ -206,7 +194,7 @@ impl SerializeCtx { ctx.append_node(0, &DUMMY_SP); ctx.kind_name_map[0] = empty_str; ctx.kind_name_map[1] = empty_str; - ctx.start_buf = NodeRef(ctx.buf.len() as u32); + ctx.start_buf = NodeRef(ctx.nodes.len() as u32); // Insert default props that are always present let type_str = ctx.str_table.insert("type"); @@ -251,11 +239,9 @@ impl SerializeCtx { } fn update_parent_link(&mut self, parent_id: u32, ref_id: u32) { - let offset = ref_id * NODE_SIZE; - - // Update parent id of written ref - let parent_offset = offset + 1 + 4 + 4; - write_u32(&mut self.buf, parent_id, parent_offset.try_into().unwrap()); + eprintln!("set parent {} for {}", parent_id, ref_id); + let node = self.nodes.get_mut(ref_id as usize).expect("Invalid id"); + node.parent = parent_id; } fn update_ref_links(&mut self, parent_id: u32, ref_id: u32) { @@ -263,44 +249,45 @@ impl SerializeCtx { // Update next pointer of previous sibling if let Some(prev_id) = self.prev_sibling_node { - let prev_offset = prev_id * NODE_SIZE; - - let prev_next = prev_offset + 1 + 4; - write_u32(&mut self.buf, ref_id, prev_next.try_into().unwrap()); + eprintln!("set next {} for {}", ref_id, prev_id); + let node = self.nodes.get_mut(prev_id as usize).unwrap(); + node.next = ref_id; } else { // Update parent child pointer - let parent_offset = parent_id * NODE_SIZE; - - let child_offset = parent_offset + 1; - write_u32(&mut self.buf, ref_id, child_offset.try_into().unwrap()); + eprintln!("set child {} for {}", ref_id, parent_id); + let node = self.nodes.get_mut(parent_id as usize).unwrap(); + node.child = ref_id; } self.prev_sibling_node = Some(ref_id) } - fn append_inner( + fn append_inner( &mut self, - kind: u8, - field_offset: usize, + kind: K, + prop_offset: usize, span_lo: u32, span_hi: u32, - ) { - // type - self.buf.push(kind); + ) where + K: Into + Display + Clone, + { + let kind_u8: u8 = kind.clone().into(); - if let Some(v) = self.kind_name_map.get::(kind.into()) { + self.nodes.push(Node { + kind: kind_u8, + prop_offset: prop_offset as u32, + child: 0, + next: 0, + parent: 0, + }); + + if let Some(v) = self.kind_name_map.get::(kind_u8.into()) { if *v == 0 { let s_id = self.str_table.insert(&format!("{kind}")); - self.kind_name_map[kind as usize] = s_id; + self.kind_name_map[kind_u8 as usize] = s_id; } } - // field offset + child idx + next idx + parent idx - append_usize(&mut self.buf, field_offset); - append_usize(&mut self.buf, 0); - append_usize(&mut self.buf, 0); - append_usize(&mut self.buf, 0); - // write spans self.spans.push(span_lo); self.spans.push(span_hi); @@ -316,9 +303,7 @@ impl SerializeCtx { self.field_offset = self.field_buf.len(); self.field_buf.push(0); - // type - let kind_u8 = kind.clone().into(); - self.append_inner(kind_u8, self.field_offset, span.lo.0, span.hi.0); + self.append_inner(kind, self.field_offset, span.lo.0, span.hi.0); PendingRef(id) } @@ -411,10 +396,14 @@ impl SerializeCtx { where P: Into + Display + Clone, { + eprintln!("prop: {} set parent {} for {:#?}", prop, parent.0, value); + self.field_header(prop, PropFlags::Ref); append_u32(&mut self.field_buf, value.0); - self.update_ref_links(parent.0, value.0); + if parent.0 > 0 { + self.update_ref_links(parent.0, value.0); + } } /// Helper for writing optional node offsets @@ -426,12 +415,11 @@ impl SerializeCtx { ) where P: Into + Display + Clone, { - let ref_value = if let Some(v) = value { v.0 } else { 0 }; - - self.field_header(prop, PropFlags::Ref); - append_u32(&mut self.field_buf, ref_value); - - self.update_ref_links(parent.0, ref_value); + if let Some(v) = value { + self.write_ref(prop, parent, v); + } else { + self.write_null(prop); + }; } /// Write a vec of node offsets into the property. The necessary space @@ -447,10 +435,8 @@ impl SerializeCtx { self.field_header(prop, PropFlags::RefArr); let group_id = self.get_id(); - append_u32(&mut self.field_buf, group_id); - // TODO(@marvinhagemeister) This is wrong self.append_inner(GROUP_KIND, 0, 0, 0); self.update_parent_link(parent.0, group_id); @@ -466,16 +452,26 @@ impl SerializeCtx { /// /// <- node kind id maps to string id /// <- node property id maps to string id + /// <- List of spans, rarely needed + /// /// /// /// pub fn serialize(&mut self) -> Vec { let mut buf: Vec = vec![]; + eprintln!("BUFFER {:#?}", self.nodes); + // The buffer starts with the serialized AST first, because that // contains absolute offsets. By butting this at the start of the // message we don't have to waste time updating any offsets. - buf.append(&mut self.buf); + for node in &self.nodes { + buf.push(node.kind); + append_u32(&mut buf, node.prop_offset); + append_u32(&mut buf, node.child); + append_u32(&mut buf, node.next); + append_u32(&mut buf, node.parent); + } // Next follows the string table. We'll keep track of the offset // in the message of where the string table begins diff --git a/cli/tools/lint/ast_buffer/swc.rs b/cli/tools/lint/ast_buffer/swc.rs index 368aa547ef..d6d44195db 100644 --- a/cli/tools/lint/ast_buffer/swc.rs +++ b/cli/tools/lint/ast_buffer/swc.rs @@ -943,7 +943,7 @@ fn serialize_member_expr( ctx.write_member_expr(&node.span, optional, computed, obj, prop) } -fn serialize_class_member( +fn _serialize_class_member( ctx: &mut TsEsTreeBuilder, member: &ClassMember, ) -> NodeRef { @@ -1314,7 +1314,7 @@ fn serialize_ts_index_sig( // pos } -fn accessibility_to_str(accessibility: Accessibility) -> String { +fn _accessibility_to_str(accessibility: Accessibility) -> String { match accessibility { Accessibility::Public => "public".to_string(), Accessibility::Protected => "protected".to_string(), diff --git a/cli/tools/lint/mod.rs b/cli/tools/lint/mod.rs index 5bf7c40880..177f31d936 100644 --- a/cli/tools/lint/mod.rs +++ b/cli/tools/lint/mod.rs @@ -304,9 +304,9 @@ impl WorkspaceLinter { fn logger_printer(msg: &str, is_err: bool) { if is_err { - eprintln!("{}", msg); + eprintln!("{}", msg.trim_end()); } else { - println!("{}", msg); + println!("{}", msg.trim_end()); } }