From f6957e9a323c63d70ecb4b0639d38f4dab826d80 Mon Sep 17 00:00:00 2001 From: Joe Savona Date: Thu, 6 Jul 2023 09:24:48 +0900 Subject: [PATCH] [rust] Partial handling of identifiers Fundamentally this PR is about lowering identifiers during construction of HIR. For now i'm punting on context variables and assuming all variables are either global, module-scoped, or locals. The implementation involves a few pieces: * `estree::Identifer` gets extended with optional binding information. The idea is that _some_ name resolution mechanism will populate this. Eventually our own, but we can also borrow data from another source... * `estree-swc` now configures SWC's (possibly broken?) name resolution mechanism and sets the above binding data when translating identifiers from swc into our estree format. * hir `Builder` tracks identifiers based on `(name, BindingId)` pairs, and assigns a unique `hir::Identifier` instance for each pair. `Identifiers` are clone-able (shared). * Tangential: i updated the printer to handle more instruction variants, including the now-ported LoadGlobal instr. I don't love this but it's a start. Long-term we definitely should have our own name resolution mechanism which we run on the estree prior to lowering to HIR. --- compiler/forget/crates/build-hir/src/build.rs | 24 +++- .../forget/crates/build-hir/src/builder.rs | 62 ++++++++- compiler/forget/crates/estree-swc/src/lib.rs | 130 +++++++++++------- compiler/forget/crates/estree/src/lib.rs | 29 ++++ ...e__tests__fixtures@for-statement.json.snap | 9 ++ .../estree__tests__fixtures@import.json.snap | 1 + .../estree__tests__fixtures@simple.json.snap | 4 + .../estree__tests__fixtures@test.json.snap | 1 + .../fixtures/tests/fixtures/identifiers.js | 11 ++ .../crates/fixtures/tests/fixtures_test.rs | 2 + ...ixtures_test__fixtures@identifiers.js.snap | 27 ++++ ...=> fixtures_test__fixtures@simple.js.snap} | 18 +-- compiler/forget/crates/hir/src/instruction.rs | 7 +- compiler/forget/crates/hir/src/print.rs | 9 ++ 14 files changed, 269 insertions(+), 65 deletions(-) create mode 100644 compiler/forget/crates/fixtures/tests/fixtures/identifiers.js create mode 100644 compiler/forget/crates/fixtures/tests/snapshots/fixtures_test__fixtures@identifiers.js.snap rename compiler/forget/crates/fixtures/tests/snapshots/{fixtures_test__fixtures.snap => fixtures_test__fixtures@simple.js.snap} (50%) diff --git a/compiler/forget/crates/build-hir/src/build.rs b/compiler/forget/crates/build-hir/src/build.rs index a0f19d46f6..541def2006 100644 --- a/compiler/forget/crates/build-hir/src/build.rs +++ b/compiler/forget/crates/build-hir/src/build.rs @@ -2,10 +2,10 @@ use bumpalo::collections::{CollectIn, String}; use estree::{ExpressionLike, FunctionDeclaration, Literal, LiteralValue, Statement}; use hir::{ ArrayElement, BlockKind, Environment, Function, GotoKind, Identifier, InstructionValue, - LoadLocal, Place, PrimitiveValue, TerminalValue, + LoadGlobal, LoadLocal, Place, PrimitiveValue, TerminalValue, }; -use crate::builder::Builder; +use crate::builder::{Binding, Builder}; /// Converts a React function in ESTree format into HIR. Returns the HIR /// if it was constructed sucessfully, otherwise a list of diagnostics @@ -61,7 +61,7 @@ fn lower_statement<'a>( } } Statement::BreakStatement(stmt) => { - let block = builder.resolve_break(stmt.label)?; + let block = builder.resolve_break(stmt.label.as_ref())?; builder.terminate( TerminalValue::GotoTerminal(hir::GotoTerminal { block, @@ -71,7 +71,7 @@ fn lower_statement<'a>( ); } Statement::ContinueStatement(stmt) => { - let block = builder.resolve_continue(stmt.label)?; + let block = builder.resolve_continue(stmt.label.as_ref())?; builder.terminate( TerminalValue::GotoTerminal(hir::GotoTerminal { block, @@ -127,6 +127,22 @@ fn lower_expression<'a>( expr: ExpressionLike, ) -> InstructionValue<'a> { match expr { + ExpressionLike::Identifier(expr) => { + // TODO: handle unbound variables + let binding = builder.resolve_binding(&expr).unwrap(); + match binding { + Binding::Local(identifier) => { + let place = Place { + effect: None, + identifier, + }; + InstructionValue::LoadLocal(LoadLocal { place }) + } + Binding::Module(..) | Binding::Global => InstructionValue::LoadGlobal(LoadGlobal { + name: String::from_str_in(&expr.name, &env.allocator), + }), + } + } ExpressionLike::Literal(expr) => InstructionValue::Primitive(hir::Primitive { value: lower_primitive(env, builder, *expr), }), diff --git a/compiler/forget/crates/build-hir/src/builder.rs b/compiler/forget/crates/build-hir/src/builder.rs index 05abb1ab14..687f21c86f 100644 --- a/compiler/forget/crates/build-hir/src/builder.rs +++ b/compiler/forget/crates/build-hir/src/builder.rs @@ -1,9 +1,13 @@ use bumpalo::collections::Vec; -use estree::Identifier; -use std::{cell::RefCell, collections::HashSet, rc::Rc}; +use estree::BindingId; +use std::{ + cell::RefCell, + collections::{HashMap, HashSet}, + rc::Rc, +}; use hir::{ - BasicBlock, BlockId, BlockKind, Environment, GotoKind, IdentifierData, Instruction, + BasicBlock, BlockId, BlockKind, Environment, GotoKind, Identifier, IdentifierData, Instruction, InstructionIdGenerator, InstructionValue, Place, Terminal, TerminalValue, Type, HIR, }; use indexmap::IndexMap; @@ -28,6 +32,8 @@ pub struct Builder<'a> { wip: WipBlock<'a>, id_gen: InstructionIdGenerator, + + bindings: HashMap<(bumpalo::collections::String<'a>, BindingId), Identifier<'a>>, } impl<'a> Builder<'a> { @@ -44,6 +50,7 @@ impl<'a> Builder<'a> { entry, wip: current, id_gen: InstructionIdGenerator::new(), + bindings: Default::default(), } } @@ -131,7 +138,10 @@ impl<'a> Builder<'a> { /// Resolves the target for the given break label (if present), or returns the default /// break target given the current context. Returns a diagnostic if the label is /// provided but cannot be resolved. - pub(crate) fn resolve_break(&self, _label: Option) -> Result { + pub(crate) fn resolve_break( + &self, + _label: Option<&estree::Identifier>, + ) -> Result { todo!() } @@ -140,10 +150,52 @@ impl<'a> Builder<'a> { /// provided but cannot be resolved. pub(crate) fn resolve_continue( &self, - _label: Option, + _label: Option<&estree::Identifier>, ) -> Result { todo!() } + + pub(crate) fn resolve_binding( + &mut self, + identifier: &estree::Identifier, + ) -> Option> { + identifier.binding.as_ref().map(|binding| match binding { + estree::Binding::Global => Binding::Global, + estree::Binding::Local(id) => { + Binding::Local(self.resolve_binding_identifier(&identifier.name, *id)) + } + estree::Binding::Module(id) => { + Binding::Module(self.resolve_binding_identifier(&identifier.name, *id)) + } + }) + } + + fn resolve_binding_identifier(&mut self, name: &str, binding_id: BindingId) -> Identifier<'a> { + let key_name = bumpalo::collections::String::from_str_in(name, &self.environment.allocator); + if let Some(identifier) = self.bindings.get(&(key_name.clone(), binding_id)) { + identifier.clone() + } else { + let id = self.environment.next_identifier_id(); + let identifier = Identifier { + id, + name: Some(key_name.clone()), + data: Rc::new(RefCell::new(IdentifierData { + mutable_range: Default::default(), + scope: None, + type_: Type::Var(self.environment.next_type_var_id()), + })), + }; + self.bindings + .insert((key_name, binding_id), identifier.clone()); + identifier + } + } +} + +pub(crate) enum Binding<'a> { + Local(Identifier<'a>), + Module(Identifier<'a>), + Global, } /// Modifies the HIR to put the blocks in reverse postorder, with predecessors before diff --git a/compiler/forget/crates/estree-swc/src/lib.rs b/compiler/forget/crates/estree-swc/src/lib.rs index 688790838b..5802ace112 100644 --- a/compiler/forget/crates/estree-swc/src/lib.rs +++ b/compiler/forget/crates/estree-swc/src/lib.rs @@ -1,9 +1,10 @@ use std::{io::stderr, num::NonZeroU32, sync::Arc}; +use estree::{Binding, BindingId}; use swc::Compiler; use swc_core::common::errors::Handler; use swc_core::common::source_map::Pos; -use swc_core::common::{FileName, FilePathMapping, Mark, SourceMap, Span, GLOBALS}; +use swc_core::common::{FileName, FilePathMapping, Mark, SourceMap, Span, SyntaxContext, GLOBALS}; use swc_core::ecma::ast::{ AssignOp, BinaryOp, BlockStmt, Decl, EsVersion, Expr, Ident, Lit, ModuleItem, Pat, PatOrExpr, Program, Stmt, UnaryOp, VarDeclKind, VarDeclOrExpr, @@ -33,24 +34,37 @@ pub fn parse(source: &str, file: &str) -> Result estree::Program { +#[derive(Debug)] +struct Context { + unresolved_mark: Mark, + top_level_mark: Mark, +} + +fn convert_program(cx: &Context, program: &Program) -> estree::Program { let mut program_items: Vec; match program { Program::Module(program) => { let body = &program.body; program_items = Vec::with_capacity(body.len()); for item in body { - program_items.push(convert_module_item(item)); + program_items.push(convert_module_item(cx, item)); } } Program::Script(program) => { @@ -58,7 +72,7 @@ fn convert_program(program: &Program) -> estree::Program { program_items = Vec::with_capacity(body.len()); for item in body { program_items.push(estree::ModuleItem::Statement(Box::new(convert_statement( - item, + cx, item, )))); } } @@ -76,10 +90,12 @@ fn convert_program(program: &Program) -> estree::Program { } } -fn convert_module_item(item: &ModuleItem) -> estree::ModuleItem { +fn convert_module_item(cx: &Context, item: &ModuleItem) -> estree::ModuleItem { match item { - ModuleItem::Stmt(item) => estree::ModuleItem::Statement(Box::new(convert_statement(item))), - _ => todo!(), + ModuleItem::Stmt(item) => { + estree::ModuleItem::Statement(Box::new(convert_statement(cx, item))) + } + _ => todo!("Convert {:#?}", item), } } @@ -98,10 +114,10 @@ fn convert_decl_kind(kind: &VarDeclKind) -> estree::VariableDeclarationKind { } } -fn convert_block_statement(stmt: &BlockStmt) -> estree::BlockStatement { +fn convert_block_statement(cx: &Context, stmt: &BlockStmt) -> estree::BlockStatement { let mut body: Vec = Vec::with_capacity(stmt.stmts.len()); for stmt in &stmt.stmts { - body.push(convert_statement(stmt)); + body.push(convert_statement(cx, stmt)); } estree::BlockStatement { body, @@ -110,13 +126,14 @@ fn convert_block_statement(stmt: &BlockStmt) -> estree::BlockStatement { } } -fn convert_statement(stmt: &Stmt) -> estree::Statement { +fn convert_statement(cx: &Context, stmt: &Stmt) -> estree::Statement { match stmt { Stmt::Decl(Decl::Fn(item)) => { let name = item.ident.sym.to_string(); estree::Statement::FunctionDeclaration(Box::new(estree::FunctionDeclaration { id: Some(estree::Identifier { name, + binding: convert_binding(cx, item.ident.span.ctxt), loc: None, range: convert_span(&item.ident.span), }), @@ -124,10 +141,10 @@ fn convert_statement(stmt: &Stmt) -> estree::Statement { .function .params .iter() - .map(|param| convert_pattern(¶m.pat)) + .map(|param| convert_pattern(cx, ¶m.pat)) .collect(), body: item.function.body.as_ref().map(|body| { - estree::Statement::BlockStatement(Box::new(convert_block_statement(body))) + estree::Statement::BlockStatement(Box::new(convert_block_statement(cx, body))) }), is_async: item.function.is_async, is_generator: item.function.is_generator, @@ -136,16 +153,22 @@ fn convert_statement(stmt: &Stmt) -> estree::Statement { })) } Stmt::Block(item) => { - estree::Statement::BlockStatement(Box::new(convert_block_statement(item))) + estree::Statement::BlockStatement(Box::new(convert_block_statement(cx, item))) } Stmt::Break(item) => estree::Statement::BreakStatement(Box::new(estree::BreakStatement { - label: item.label.as_ref().map(|label| convert_identifier(label)), + label: item + .label + .as_ref() + .map(|label| convert_identifier(cx, label)), loc: None, range: convert_span(&item.span), })), Stmt::Continue(item) => { estree::Statement::ContinueStatement(Box::new(estree::ContinueStatement { - label: item.label.as_ref().map(|label| convert_identifier(label)), + label: item + .label + .as_ref() + .map(|label| convert_identifier(cx, label)), loc: None, range: convert_span(&item.span), })) @@ -158,8 +181,8 @@ fn convert_statement(stmt: &Stmt) -> estree::Statement { } Stmt::DoWhile(item) => { estree::Statement::DoWhileStatement(Box::new(estree::DoWhileStatement { - body: convert_statement(&item.body), - test: convert_expression(&item.test), + body: convert_statement(cx, &item.body), + test: convert_expression(cx, &item.test), loc: None, range: convert_span(&item.span), })) @@ -169,7 +192,7 @@ fn convert_statement(stmt: &Stmt) -> estree::Statement { range: convert_span(&item.span), })), Stmt::Expr(item) => { - let expression = convert_expression(&item.expr); + let expression = convert_expression(cx, &item.expr); estree::Statement::ExpressionStatement(Box::new(estree::ExpressionStatement { expression, directive: None, @@ -180,7 +203,7 @@ fn convert_statement(stmt: &Stmt) -> estree::Statement { Stmt::For(item) => estree::Statement::ForStatement(Box::new(estree::ForStatement { init: item.init.as_ref().map(|init| match init { VarDeclOrExpr::Expr(init) => { - estree::ForInit::Expression(Box::new(convert_expression(init))) + estree::ForInit::Expression(Box::new(convert_expression(cx, init))) } VarDeclOrExpr::VarDecl(init) => { assert_eq!(init.decls.len(), 1); @@ -188,8 +211,8 @@ fn convert_statement(stmt: &Stmt) -> estree::Statement { estree::ForInit::VariableDeclaration(Box::new(estree::VariableDeclaration { kind: convert_decl_kind(&init.kind), declarations: vec![estree::VariableDeclarator { - id: convert_pattern(&decl.name), - init: decl.init.as_ref().map(|init| convert_expression(init)), + id: convert_pattern(cx, &decl.name), + init: decl.init.as_ref().map(|init| convert_expression(cx, init)), loc: None, range: convert_span(&decl.span), }], @@ -198,31 +221,31 @@ fn convert_statement(stmt: &Stmt) -> estree::Statement { })) } }), - test: item.test.as_ref().map(|test| convert_expression(test)), + test: item.test.as_ref().map(|test| convert_expression(cx, test)), update: item .update .as_ref() - .map(|update| convert_expression(update)), - body: convert_statement(&item.body), + .map(|update| convert_expression(cx, update)), + body: convert_statement(cx, &item.body), loc: None, range: convert_span(&item.span), })), Stmt::Return(item) => { estree::Statement::ReturnStatement(Box::new(estree::ReturnStatement { - argument: item.arg.as_ref().map(|arg| convert_expression(arg)), + argument: item.arg.as_ref().map(|arg| convert_expression(cx, arg)), loc: None, range: convert_span(&item.span), })) } Stmt::Throw(item) => estree::Statement::ThrowStatement(Box::new(estree::ThrowStatement { - argument: convert_expression(&item.arg), + argument: convert_expression(cx, &item.arg), loc: None, range: convert_span(&item.span), })), Stmt::If(item) => estree::Statement::IfStatement(Box::new(estree::IfStatement { - test: convert_expression(&item.test), - consequent: convert_statement(&item.cons), - alternate: item.alt.as_ref().map(|alt| convert_statement(alt)), + test: convert_expression(cx, &item.test), + consequent: convert_statement(cx, &item.cons), + alternate: item.alt.as_ref().map(|alt| convert_statement(cx, alt)), loc: None, range: convert_span(&item.span), })), @@ -230,9 +253,11 @@ fn convert_statement(stmt: &Stmt) -> estree::Statement { } } -fn convert_expression(expr: &Expr) -> estree::ExpressionLike { +fn convert_expression(cx: &Context, expr: &Expr) -> estree::ExpressionLike { match expr { - Expr::Ident(expr) => estree::ExpressionLike::Identifier(Box::new(convert_identifier(expr))), + Expr::Ident(expr) => { + estree::ExpressionLike::Identifier(Box::new(convert_identifier(cx, expr))) + } Expr::Array(expr) => { estree::ExpressionLike::ArrayExpression(Box::new(estree::ArrayExpression { elements: expr @@ -244,12 +269,12 @@ fn convert_expression(expr: &Expr) -> estree::ExpressionLike { match value.spread { Some(spread) => estree::ExpressionLike::SpreadElement(Box::new( estree::SpreadElement { - argument: convert_expression(&value.expr), + argument: convert_expression(cx, &value.expr), loc: None, range: convert_span(&spread), }, )), - None => convert_expression(&value.expr), + None => convert_expression(cx, &value.expr), } }) .collect(), @@ -259,7 +284,7 @@ fn convert_expression(expr: &Expr) -> estree::ExpressionLike { } Expr::Await(expr) => { estree::ExpressionLike::AwaitExpression(Box::new(estree::AwaitExpression { - argument: convert_expression(&expr.arg), + argument: convert_expression(cx, &expr.arg), loc: None, range: convert_span(&expr.span), })) @@ -268,7 +293,7 @@ fn convert_expression(expr: &Expr) -> estree::ExpressionLike { estree::ExpressionLike::UnaryExpression(Box::new(estree::UnaryExpression { operator: convert_unary_operator(expr.op), is_prefix: false, - argument: convert_expression(&expr.arg), + argument: convert_expression(cx, &expr.arg), loc: None, range: convert_span(&expr.span), })) @@ -277,8 +302,8 @@ fn convert_expression(expr: &Expr) -> estree::ExpressionLike { Operator::Binary(op) => { estree::ExpressionLike::BinaryExpression(Box::new(estree::BinaryExpression { operator: op, - left: convert_expression(&expr.left), - right: convert_expression(&expr.right), + left: convert_expression(cx, &expr.left), + right: convert_expression(cx, &expr.right), loc: None, range: convert_span(&expr.span), })) @@ -286,8 +311,8 @@ fn convert_expression(expr: &Expr) -> estree::ExpressionLike { Operator::Logical(op) => { estree::ExpressionLike::LogicalExpression(Box::new(estree::LogicalExpression { operator: op, - left: convert_expression(&expr.left), - right: convert_expression(&expr.right), + left: convert_expression(cx, &expr.left), + right: convert_expression(cx, &expr.right), loc: None, range: convert_span(&expr.span), })) @@ -321,7 +346,7 @@ fn convert_expression(expr: &Expr) -> estree::ExpressionLike { estree::ExpressionLike::AssignmentExpression(Box::new(estree::AssignmentExpression { operator: convert_assignment_operator(expr.op), left: convert_assignment_target(&expr.left), - right: convert_expression(&expr.right), + right: convert_expression(cx, &expr.right), loc: None, range: convert_span(&expr.span), })) @@ -389,10 +414,11 @@ fn convert_binary_operator(op: BinaryOp) -> Operator { } } -fn convert_pattern(pat: &Pat) -> estree::Pattern { +fn convert_pattern(cx: &Context, pat: &Pat) -> estree::Pattern { match pat { Pat::Ident(pat) => estree::Pattern::Identifier(Box::new(estree::Identifier { name: pat.id.sym.to_string(), + binding: convert_binding(cx, pat.id.span.ctxt), loc: None, range: convert_span(&pat.span), })), @@ -400,10 +426,22 @@ fn convert_pattern(pat: &Pat) -> estree::Pattern { } } -fn convert_identifier(identifier: &Ident) -> estree::Identifier { +fn convert_binding(context: &Context, binding_cx: SyntaxContext) -> Option { + let id = BindingId::new(NonZeroU32::new(binding_cx.as_u32()).unwrap()); + if binding_cx.as_u32() == context.top_level_mark.as_u32() { + Some(Binding::Global) + } else if binding_cx.as_u32() == context.unresolved_mark.as_u32() { + Some(Binding::Module(id)) + } else { + Some(Binding::Local(id)) + } +} + +fn convert_identifier(cx: &Context, identifier: &Ident) -> estree::Identifier { let name = identifier.sym.as_ref().to_string(); estree::Identifier { name, + binding: convert_binding(cx, identifier.span.ctxt), loc: None, range: convert_span(&identifier.span), } diff --git a/compiler/forget/crates/estree/src/lib.rs b/compiler/forget/crates/estree/src/lib.rs index 39bc165782..3e8d59618c 100644 --- a/compiler/forget/crates/estree/src/lib.rs +++ b/compiler/forget/crates/estree/src/lib.rs @@ -1020,11 +1020,24 @@ pub struct AssignmentPattern { #[derive(Serialize, Deserialize, Debug)] pub struct Identifier { pub name: String, + + #[serde(default)] + pub binding: Option, + pub loc: Option, + #[serde(default)] pub range: Option, } +#[derive(Serialize, Deserialize, Debug)] +#[serde(tag = "type")] +pub enum Binding { + Local(BindingId), + Module(BindingId), + Global, +} + #[derive(Serialize, Deserialize, Debug)] pub struct MemberExpression { pub object: ExpressionLike, @@ -1240,6 +1253,22 @@ pub struct JSXText { pub range: Option, } +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone, Copy)] +#[serde(transparent)] +pub struct BindingId(NonZeroU32); + +impl BindingId { + pub fn new(id: NonZeroU32) -> Self { + Self(id) + } +} + +impl From for u32 { + fn from(value: BindingId) -> Self { + value.0.into() + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/compiler/forget/crates/estree/src/snapshots/estree__tests__fixtures@for-statement.json.snap b/compiler/forget/crates/estree/src/snapshots/estree__tests__fixtures@for-statement.json.snap index cf56c136ff..8a7f225260 100644 --- a/compiler/forget/crates/estree/src/snapshots/estree__tests__fixtures@for-statement.json.snap +++ b/compiler/forget/crates/estree/src/snapshots/estree__tests__fixtures@for-statement.json.snap @@ -541,6 +541,7 @@ Output: "type": "FunctionDeclaration", "id": { "name": "Component", + "binding": null, "loc": { "source": null, "start": { @@ -561,6 +562,7 @@ Output: { "type": "Identifier", "name": "props", + "binding": null, "loc": { "source": null, "start": { @@ -590,6 +592,7 @@ Output: "id": { "type": "Identifier", "name": "x", + "binding": null, "loc": { "source": null, "start": { @@ -669,6 +672,7 @@ Output: "id": { "type": "Identifier", "name": "i", + "binding": null, "loc": { "source": null, "start": { @@ -745,6 +749,7 @@ Output: "left": { "type": "Identifier", "name": "i", + "binding": null, "loc": { "source": null, "start": { @@ -803,6 +808,7 @@ Output: "argument": { "type": "Identifier", "name": "i", + "binding": null, "loc": { "source": null, "start": { @@ -847,6 +853,7 @@ Output: "left": { "type": "Identifier", "name": "x", + "binding": null, "loc": { "source": null, "start": { @@ -866,6 +873,7 @@ Output: "right": { "type": "Identifier", "name": "i", + "binding": null, "loc": { "source": null, "start": { @@ -953,6 +961,7 @@ Output: "argument": { "type": "Identifier", "name": "x", + "binding": null, "loc": { "source": null, "start": { diff --git a/compiler/forget/crates/estree/src/snapshots/estree__tests__fixtures@import.json.snap b/compiler/forget/crates/estree/src/snapshots/estree__tests__fixtures@import.json.snap index bffb940c37..4bfb9ebbd5 100644 --- a/compiler/forget/crates/estree/src/snapshots/estree__tests__fixtures@import.json.snap +++ b/compiler/forget/crates/estree/src/snapshots/estree__tests__fixtures@import.json.snap @@ -119,6 +119,7 @@ Output: "type": "ImportDefaultSpecifier", "local": { "name": "React", + "binding": null, "loc": { "source": null, "start": { diff --git a/compiler/forget/crates/estree/src/snapshots/estree__tests__fixtures@simple.json.snap b/compiler/forget/crates/estree/src/snapshots/estree__tests__fixtures@simple.json.snap index d3b89d67ae..85fb57cd23 100644 --- a/compiler/forget/crates/estree/src/snapshots/estree__tests__fixtures@simple.json.snap +++ b/compiler/forget/crates/estree/src/snapshots/estree__tests__fixtures@simple.json.snap @@ -202,6 +202,7 @@ Output: "type": "FunctionDeclaration", "id": { "name": "Component", + "binding": null, "loc": { "source": null, "start": { @@ -222,6 +223,7 @@ Output: { "type": "Identifier", "name": "props", + "binding": null, "loc": { "source": null, "start": { @@ -251,6 +253,7 @@ Output: "object": { "type": "Identifier", "name": "props", + "binding": null, "loc": { "source": null, "start": { @@ -270,6 +273,7 @@ Output: "property": { "type": "Identifier", "name": "value", + "binding": null, "loc": { "source": null, "start": { diff --git a/compiler/forget/crates/estree/src/snapshots/estree__tests__fixtures@test.json.snap b/compiler/forget/crates/estree/src/snapshots/estree__tests__fixtures@test.json.snap index 1c4361aa79..d3b5edb7ef 100644 --- a/compiler/forget/crates/estree/src/snapshots/estree__tests__fixtures@test.json.snap +++ b/compiler/forget/crates/estree/src/snapshots/estree__tests__fixtures@test.json.snap @@ -364,6 +364,7 @@ Output: "type": "FunctionDeclaration", "id": { "name": "foo", + "binding": null, "loc": { "source": null, "start": { diff --git a/compiler/forget/crates/fixtures/tests/fixtures/identifiers.js b/compiler/forget/crates/fixtures/tests/fixtures/identifiers.js new file mode 100644 index 0000000000..3d9c1c95be --- /dev/null +++ b/compiler/forget/crates/fixtures/tests/fixtures/identifiers.js @@ -0,0 +1,11 @@ +// import React from "react"; + +// const FOO = false; + +function id(x) { + // React; + // FOO; + Math; + id; + return x; +} diff --git a/compiler/forget/crates/fixtures/tests/fixtures_test.rs b/compiler/forget/crates/fixtures/tests/fixtures_test.rs index 68b1b4dd17..e4a399417d 100644 --- a/compiler/forget/crates/fixtures/tests/fixtures_test.rs +++ b/compiler/forget/crates/fixtures/tests/fixtures_test.rs @@ -11,6 +11,8 @@ fn fixtures() { let input = std::fs::read_to_string(path).unwrap(); let ast = parse(&input, path.to_str().unwrap()).unwrap(); + println!("{:#?}", &ast); + let mut output = String::new(); for (ix, item) in ast.body.into_iter().enumerate() { diff --git a/compiler/forget/crates/fixtures/tests/snapshots/fixtures_test__fixtures@identifiers.js.snap b/compiler/forget/crates/fixtures/tests/snapshots/fixtures_test__fixtures@identifiers.js.snap new file mode 100644 index 0000000000..636e552e63 --- /dev/null +++ b/compiler/forget/crates/fixtures/tests/snapshots/fixtures_test__fixtures@identifiers.js.snap @@ -0,0 +1,27 @@ +--- +source: crates/fixtures/tests/fixtures_test.rs +expression: "format!(\"Input:\\n{input}\\n\\nOutput:\\n{output}\")" +input_file: crates/fixtures/tests/fixtures/identifiers.js +--- +Input: +// import React from "react"; + +// const FOO = false; + +function id(x) { + // React; + // FOO; + Math; + id; + return x; +} + + +Output: +entry bb0 +bb0 + [0] unknown $0 = LoadGlobal Math + [1] unknown $2 = LoadGlobal id + [2] unknown $4 = LoadLocal unknown x$3 + [3] Return unknown $4 + diff --git a/compiler/forget/crates/fixtures/tests/snapshots/fixtures_test__fixtures.snap b/compiler/forget/crates/fixtures/tests/snapshots/fixtures_test__fixtures@simple.js.snap similarity index 50% rename from compiler/forget/crates/fixtures/tests/snapshots/fixtures_test__fixtures.snap rename to compiler/forget/crates/fixtures/tests/snapshots/fixtures_test__fixtures@simple.js.snap index e4a6506fb7..ce1e297a02 100644 --- a/compiler/forget/crates/fixtures/tests/snapshots/fixtures_test__fixtures.snap +++ b/compiler/forget/crates/fixtures/tests/snapshots/fixtures_test__fixtures@simple.js.snap @@ -13,14 +13,14 @@ function test() { Output: entry bb0 bb0 - [0] true - [1] false - [2] null - [3] 1 - [4] 3.14 - [5] "hello world!" - [6] Array [unknown $5] - [7] Array [unknown $0, unknown $1, unknown $2, unknown $3, unknown $4, ...unknown $6] - [8] 2 + [0] unknown $0 = true + [1] unknown $1 = false + [2] unknown $2 = null + [3] unknown $3 = 1 + [4] unknown $4 = 3.14 + [5] unknown $5 = "hello world!" + [6] unknown $6 = Array [unknown $5] + [7] unknown $7 = Array [unknown $0, unknown $1, unknown $2, unknown $3, unknown $4, ...unknown $6] + [8] unknown $8 = 2 [9] Return unknown $8 diff --git a/compiler/forget/crates/hir/src/instruction.rs b/compiler/forget/crates/hir/src/instruction.rs index 650ba2e10e..c34ebb5769 100644 --- a/compiler/forget/crates/hir/src/instruction.rs +++ b/compiler/forget/crates/hir/src/instruction.rs @@ -28,7 +28,7 @@ pub enum InstructionValue<'a> { // JsxFragment(JsxFragment<'a>), // JsxText(JsxText<'a>), LoadContext(LoadContext<'a>), - // LoadGlobal(LoadGlobal<'a>), + LoadGlobal(LoadGlobal<'a>), LoadLocal(LoadLocal<'a>), // MethodCall(MethodCall<'a>), // New(New<'a>), @@ -100,6 +100,11 @@ pub struct LoadContext<'a> { pub place: Place<'a>, } +#[derive(Debug)] +pub struct LoadGlobal<'a> { + pub name: String<'a>, +} + #[derive(Debug)] pub struct DeclareLocal<'a> { pub lvalue: LValue<'a>, diff --git a/compiler/forget/crates/hir/src/print.rs b/compiler/forget/crates/hir/src/print.rs index e931c0f832..1205d8a785 100644 --- a/compiler/forget/crates/hir/src/print.rs +++ b/compiler/forget/crates/hir/src/print.rs @@ -38,6 +38,8 @@ impl<'a> Print for BasicBlock<'a> { impl<'a> Print for Instruction<'a> { fn print(&self, out: &mut impl Write) -> Result { write!(out, " {} ", self.id)?; + self.lvalue.print(out)?; + write!(out, " = ")?; self.value.print(out)?; writeln!(out, "")?; Ok(()) @@ -57,6 +59,13 @@ impl<'a> Print for InstructionValue<'a> { } write!(out, "]")?; } + InstructionValue::LoadGlobal(value) => { + write!(out, "LoadGlobal {}", &value.name)?; + } + InstructionValue::LoadLocal(value) => { + write!(out, "LoadLocal ")?; + value.place.print(out)?; + } InstructionValue::Primitive(value) => { // Unlike other variants we don't print the variant name ("Primitive") since it's // obvious