@samitouri / QOS-React-2 / commits / f6957e9a32

[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.

Joe Savona committed Jul 6, 2023 at 09:24 UTC f6957e9a323c63d70ecb4b0639d38f4dab826d80
14 files changed +269 -65
compiler/forget/crates/build-hir/src/build.rs
+20 -4
@@ -2,10 +2,10 @@ use bumpalo::collections::{CollectIn, String};
2 use estree::{ExpressionLike, FunctionDeclaration, Literal, LiteralValue, Statement};
3 use hir::{
4 ArrayElement, BlockKind, Environment, Function, GotoKind, Identifier, InstructionValue,
5 - LoadLocal, Place, PrimitiveValue, TerminalValue,
5 + LoadGlobal, LoadLocal, Place, PrimitiveValue, TerminalValue,
6 };
7
8 -use crate::builder::Builder;
8 +use crate::builder::{Binding, Builder};
9
10 /// Converts a React function in ESTree format into HIR. Returns the HIR
11 /// if it was constructed sucessfully, otherwise a list of diagnostics
@@ -61,7 +61,7 @@ fn lower_statement<'a>(
61 }
62 }
63 Statement::BreakStatement(stmt) => {
64 - let block = builder.resolve_break(stmt.label)?;
64 + let block = builder.resolve_break(stmt.label.as_ref())?;
65 builder.terminate(
66 TerminalValue::GotoTerminal(hir::GotoTerminal {
67 block,
@@ -71,7 +71,7 @@ fn lower_statement<'a>(
71 );
72 }
73 Statement::ContinueStatement(stmt) => {
74 - let block = builder.resolve_continue(stmt.label)?;
74 + let block = builder.resolve_continue(stmt.label.as_ref())?;
75 builder.terminate(
76 TerminalValue::GotoTerminal(hir::GotoTerminal {
77 block,
@@ -127,6 +127,22 @@ fn lower_expression<'a>(
127 expr: ExpressionLike,
128 ) -> InstructionValue<'a> {
129 match expr {
130 + ExpressionLike::Identifier(expr) => {
131 + // TODO: handle unbound variables
132 + let binding = builder.resolve_binding(&expr).unwrap();
133 + match binding {
134 + Binding::Local(identifier) => {
135 + let place = Place {
136 + effect: None,
137 + identifier,
138 + };
139 + InstructionValue::LoadLocal(LoadLocal { place })
140 + }
141 + Binding::Module(..) | Binding::Global => InstructionValue::LoadGlobal(LoadGlobal {
142 + name: String::from_str_in(&expr.name, &env.allocator),
143 + }),
144 + }
145 + }
146 ExpressionLike::Literal(expr) => InstructionValue::Primitive(hir::Primitive {
147 value: lower_primitive(env, builder, *expr),
148 }),
compiler/forget/crates/build-hir/src/builder.rs
+57 -5
@@ -1,9 +1,13 @@
1 use bumpalo::collections::Vec;
2 -use estree::Identifier;
3 -use std::{cell::RefCell, collections::HashSet, rc::Rc};
2 +use estree::BindingId;
3 +use std::{
4 + cell::RefCell,
5 + collections::{HashMap, HashSet},
6 + rc::Rc,
7 +};
8
9 use hir::{
6 - BasicBlock, BlockId, BlockKind, Environment, GotoKind, IdentifierData, Instruction,
10 + BasicBlock, BlockId, BlockKind, Environment, GotoKind, Identifier, IdentifierData, Instruction,
11 InstructionIdGenerator, InstructionValue, Place, Terminal, TerminalValue, Type, HIR,
12 };
13 use indexmap::IndexMap;
@@ -28,6 +32,8 @@ pub struct Builder<'a> {
32 wip: WipBlock<'a>,
33
34 id_gen: InstructionIdGenerator,
35 +
36 + bindings: HashMap<(bumpalo::collections::String<'a>, BindingId), Identifier<'a>>,
37 }
38
39 impl<'a> Builder<'a> {
@@ -44,6 +50,7 @@ impl<'a> Builder<'a> {
50 entry,
51 wip: current,
52 id_gen: InstructionIdGenerator::new(),
53 + bindings: Default::default(),
54 }
55 }
56
@@ -131,7 +138,10 @@ impl<'a> Builder<'a> {
138 /// Resolves the target for the given break label (if present), or returns the default
139 /// break target given the current context. Returns a diagnostic if the label is
140 /// provided but cannot be resolved.
134 - pub(crate) fn resolve_break(&self, _label: Option<Identifier>) -> Result<BlockId, Diagnostic> {
141 + pub(crate) fn resolve_break(
142 + &self,
143 + _label: Option<&estree::Identifier>,
144 + ) -> Result<BlockId, Diagnostic> {
145 todo!()
146 }
147
@@ -140,10 +150,52 @@ impl<'a> Builder<'a> {
150 /// provided but cannot be resolved.
151 pub(crate) fn resolve_continue(
152 &self,
143 - _label: Option<Identifier>,
153 + _label: Option<&estree::Identifier>,
154 ) -> Result<BlockId, Diagnostic> {
155 todo!()
156 }
157 +
158 + pub(crate) fn resolve_binding(
159 + &mut self,
160 + identifier: &estree::Identifier,
161 + ) -> Option<Binding<'a>> {
162 + identifier.binding.as_ref().map(|binding| match binding {
163 + estree::Binding::Global => Binding::Global,
164 + estree::Binding::Local(id) => {
165 + Binding::Local(self.resolve_binding_identifier(&identifier.name, *id))
166 + }
167 + estree::Binding::Module(id) => {
168 + Binding::Module(self.resolve_binding_identifier(&identifier.name, *id))
169 + }
170 + })
171 + }
172 +
173 + fn resolve_binding_identifier(&mut self, name: &str, binding_id: BindingId) -> Identifier<'a> {
174 + let key_name = bumpalo::collections::String::from_str_in(name, &self.environment.allocator);
175 + if let Some(identifier) = self.bindings.get(&(key_name.clone(), binding_id)) {
176 + identifier.clone()
177 + } else {
178 + let id = self.environment.next_identifier_id();
179 + let identifier = Identifier {
180 + id,
181 + name: Some(key_name.clone()),
182 + data: Rc::new(RefCell::new(IdentifierData {
183 + mutable_range: Default::default(),
184 + scope: None,
185 + type_: Type::Var(self.environment.next_type_var_id()),
186 + })),
187 + };
188 + self.bindings
189 + .insert((key_name, binding_id), identifier.clone());
190 + identifier
191 + }
192 + }
193 +}
194 +
195 +pub(crate) enum Binding<'a> {
196 + Local(Identifier<'a>),
197 + Module(Identifier<'a>),
198 + Global,
199 }
200
201 /// Modifies the HIR to put the blocks in reverse postorder, with predecessors before
compiler/forget/crates/estree-swc/src/lib.rs
+84 -46
@@ -1,9 +1,10 @@
1 use std::{io::stderr, num::NonZeroU32, sync::Arc};
2
3 +use estree::{Binding, BindingId};
4 use swc::Compiler;
5 use swc_core::common::errors::Handler;
6 use swc_core::common::source_map::Pos;
6 -use swc_core::common::{FileName, FilePathMapping, Mark, SourceMap, Span, GLOBALS};
7 +use swc_core::common::{FileName, FilePathMapping, Mark, SourceMap, Span, SyntaxContext, GLOBALS};
8 use swc_core::ecma::ast::{
9 AssignOp, BinaryOp, BlockStmt, Decl, EsVersion, Expr, Ident, Lit, ModuleItem, Pat, PatOrExpr,
10 Program, Stmt, UnaryOp, VarDeclKind, VarDeclOrExpr,
@@ -33,24 +34,37 @@ pub fn parse(source: &str, file: &str) -> Result<estree::Program, Box<dyn std::e
34 Some(&comments),
35 )?;
36
37 + let context = Context {
38 + top_level_mark: Mark::new(),
39 + unresolved_mark: Mark::new(),
40 + };
41 +
42 let module = c.run_transform(&handler, false, || {
37 - let unresolved_mark = Mark::new();
38 - let top_level_mark = Mark::new();
39 - module.fold_with(&mut resolver(unresolved_mark, top_level_mark, true))
43 + module.fold_with(&mut resolver(
44 + context.unresolved_mark,
45 + context.top_level_mark,
46 + true,
47 + ))
48 });
49
42 - Ok(convert_program(&module))
50 + Ok(convert_program(&context, &module))
51 })
52 }
53
46 -fn convert_program(program: &Program) -> estree::Program {
54 +#[derive(Debug)]
55 +struct Context {
56 + unresolved_mark: Mark,
57 + top_level_mark: Mark,
58 +}
59 +
60 +fn convert_program(cx: &Context, program: &Program) -> estree::Program {
61 let mut program_items: Vec<estree::ModuleItem>;
62 match program {
63 Program::Module(program) => {
64 let body = &program.body;
65 program_items = Vec::with_capacity(body.len());
66 for item in body {
53 - program_items.push(convert_module_item(item));
67 + program_items.push(convert_module_item(cx, item));
68 }
69 }
70 Program::Script(program) => {
@@ -58,7 +72,7 @@ fn convert_program(program: &Program) -> estree::Program {
72 program_items = Vec::with_capacity(body.len());
73 for item in body {
74 program_items.push(estree::ModuleItem::Statement(Box::new(convert_statement(
61 - item,
75 + cx, item,
76 ))));
77 }
78 }
@@ -76,10 +90,12 @@ fn convert_program(program: &Program) -> estree::Program {
90 }
91 }
92
79 -fn convert_module_item(item: &ModuleItem) -> estree::ModuleItem {
93 +fn convert_module_item(cx: &Context, item: &ModuleItem) -> estree::ModuleItem {
94 match item {
81 - ModuleItem::Stmt(item) => estree::ModuleItem::Statement(Box::new(convert_statement(item))),
82 - _ => todo!(),
95 + ModuleItem::Stmt(item) => {
96 + estree::ModuleItem::Statement(Box::new(convert_statement(cx, item)))
97 + }
98 + _ => todo!("Convert {:#?}", item),
99 }
100 }
101
@@ -98,10 +114,10 @@ fn convert_decl_kind(kind: &VarDeclKind) -> estree::VariableDeclarationKind {
114 }
115 }
116
101 -fn convert_block_statement(stmt: &BlockStmt) -> estree::BlockStatement {
117 +fn convert_block_statement(cx: &Context, stmt: &BlockStmt) -> estree::BlockStatement {
118 let mut body: Vec<estree::Statement> = Vec::with_capacity(stmt.stmts.len());
119 for stmt in &stmt.stmts {
104 - body.push(convert_statement(stmt));
120 + body.push(convert_statement(cx, stmt));
121 }
122 estree::BlockStatement {
123 body,
@@ -110,13 +126,14 @@ fn convert_block_statement(stmt: &BlockStmt) -> estree::BlockStatement {
126 }
127 }
128
113 -fn convert_statement(stmt: &Stmt) -> estree::Statement {
129 +fn convert_statement(cx: &Context, stmt: &Stmt) -> estree::Statement {
130 match stmt {
131 Stmt::Decl(Decl::Fn(item)) => {
132 let name = item.ident.sym.to_string();
133 estree::Statement::FunctionDeclaration(Box::new(estree::FunctionDeclaration {
134 id: Some(estree::Identifier {
135 name,
136 + binding: convert_binding(cx, item.ident.span.ctxt),
137 loc: None,
138 range: convert_span(&item.ident.span),
139 }),
@@ -124,10 +141,10 @@ fn convert_statement(stmt: &Stmt) -> estree::Statement {
141 .function
142 .params
143 .iter()
127 - .map(|param| convert_pattern(&param.pat))
144 + .map(|param| convert_pattern(cx, &param.pat))
145 .collect(),
146 body: item.function.body.as_ref().map(|body| {
130 - estree::Statement::BlockStatement(Box::new(convert_block_statement(body)))
147 + estree::Statement::BlockStatement(Box::new(convert_block_statement(cx, body)))
148 }),
149 is_async: item.function.is_async,
150 is_generator: item.function.is_generator,
@@ -136,16 +153,22 @@ fn convert_statement(stmt: &Stmt) -> estree::Statement {
153 }))
154 }
155 Stmt::Block(item) => {
139 - estree::Statement::BlockStatement(Box::new(convert_block_statement(item)))
156 + estree::Statement::BlockStatement(Box::new(convert_block_statement(cx, item)))
157 }
158 Stmt::Break(item) => estree::Statement::BreakStatement(Box::new(estree::BreakStatement {
142 - label: item.label.as_ref().map(|label| convert_identifier(label)),
159 + label: item
160 + .label
161 + .as_ref()
162 + .map(|label| convert_identifier(cx, label)),
163 loc: None,
164 range: convert_span(&item.span),
165 })),
166 Stmt::Continue(item) => {
167 estree::Statement::ContinueStatement(Box::new(estree::ContinueStatement {
148 - label: item.label.as_ref().map(|label| convert_identifier(label)),
168 + label: item
169 + .label
170 + .as_ref()
171 + .map(|label| convert_identifier(cx, label)),
172 loc: None,
173 range: convert_span(&item.span),
174 }))
@@ -158,8 +181,8 @@ fn convert_statement(stmt: &Stmt) -> estree::Statement {
181 }
182 Stmt::DoWhile(item) => {
183 estree::Statement::DoWhileStatement(Box::new(estree::DoWhileStatement {
161 - body: convert_statement(&item.body),
162 - test: convert_expression(&item.test),
184 + body: convert_statement(cx, &item.body),
185 + test: convert_expression(cx, &item.test),
186 loc: None,
187 range: convert_span(&item.span),
188 }))
@@ -169,7 +192,7 @@ fn convert_statement(stmt: &Stmt) -> estree::Statement {
192 range: convert_span(&item.span),
193 })),
194 Stmt::Expr(item) => {
172 - let expression = convert_expression(&item.expr);
195 + let expression = convert_expression(cx, &item.expr);
196 estree::Statement::ExpressionStatement(Box::new(estree::ExpressionStatement {
197 expression,
198 directive: None,
@@ -180,7 +203,7 @@ fn convert_statement(stmt: &Stmt) -> estree::Statement {
203 Stmt::For(item) => estree::Statement::ForStatement(Box::new(estree::ForStatement {
204 init: item.init.as_ref().map(|init| match init {
205 VarDeclOrExpr::Expr(init) => {
183 - estree::ForInit::Expression(Box::new(convert_expression(init)))
206 + estree::ForInit::Expression(Box::new(convert_expression(cx, init)))
207 }
208 VarDeclOrExpr::VarDecl(init) => {
209 assert_eq!(init.decls.len(), 1);
@@ -188,8 +211,8 @@ fn convert_statement(stmt: &Stmt) -> estree::Statement {
211 estree::ForInit::VariableDeclaration(Box::new(estree::VariableDeclaration {
212 kind: convert_decl_kind(&init.kind),
213 declarations: vec![estree::VariableDeclarator {
191 - id: convert_pattern(&decl.name),
192 - init: decl.init.as_ref().map(|init| convert_expression(init)),
214 + id: convert_pattern(cx, &decl.name),
215 + init: decl.init.as_ref().map(|init| convert_expression(cx, init)),
216 loc: None,
217 range: convert_span(&decl.span),
218 }],
@@ -198,31 +221,31 @@ fn convert_statement(stmt: &Stmt) -> estree::Statement {
221 }))
222 }
223 }),
201 - test: item.test.as_ref().map(|test| convert_expression(test)),
224 + test: item.test.as_ref().map(|test| convert_expression(cx, test)),
225 update: item
226 .update
227 .as_ref()
205 - .map(|update| convert_expression(update)),
206 - body: convert_statement(&item.body),
228 + .map(|update| convert_expression(cx, update)),
229 + body: convert_statement(cx, &item.body),
230 loc: None,
231 range: convert_span(&item.span),
232 })),
233 Stmt::Return(item) => {
234 estree::Statement::ReturnStatement(Box::new(estree::ReturnStatement {
212 - argument: item.arg.as_ref().map(|arg| convert_expression(arg)),
235 + argument: item.arg.as_ref().map(|arg| convert_expression(cx, arg)),
236 loc: None,
237 range: convert_span(&item.span),
238 }))
239 }
240 Stmt::Throw(item) => estree::Statement::ThrowStatement(Box::new(estree::ThrowStatement {
218 - argument: convert_expression(&item.arg),
241 + argument: convert_expression(cx, &item.arg),
242 loc: None,
243 range: convert_span(&item.span),
244 })),
245 Stmt::If(item) => estree::Statement::IfStatement(Box::new(estree::IfStatement {
223 - test: convert_expression(&item.test),
224 - consequent: convert_statement(&item.cons),
225 - alternate: item.alt.as_ref().map(|alt| convert_statement(alt)),
246 + test: convert_expression(cx, &item.test),
247 + consequent: convert_statement(cx, &item.cons),
248 + alternate: item.alt.as_ref().map(|alt| convert_statement(cx, alt)),
249 loc: None,
250 range: convert_span(&item.span),
251 })),
@@ -230,9 +253,11 @@ fn convert_statement(stmt: &Stmt) -> estree::Statement {
253 }
254 }
255
233 -fn convert_expression(expr: &Expr) -> estree::ExpressionLike {
256 +fn convert_expression(cx: &Context, expr: &Expr) -> estree::ExpressionLike {
257 match expr {
235 - Expr::Ident(expr) => estree::ExpressionLike::Identifier(Box::new(convert_identifier(expr))),
258 + Expr::Ident(expr) => {
259 + estree::ExpressionLike::Identifier(Box::new(convert_identifier(cx, expr)))
260 + }
261 Expr::Array(expr) => {
262 estree::ExpressionLike::ArrayExpression(Box::new(estree::ArrayExpression {
263 elements: expr
@@ -244,12 +269,12 @@ fn convert_expression(expr: &Expr) -> estree::ExpressionLike {
269 match value.spread {
270 Some(spread) => estree::ExpressionLike::SpreadElement(Box::new(
271 estree::SpreadElement {
247 - argument: convert_expression(&value.expr),
272 + argument: convert_expression(cx, &value.expr),
273 loc: None,
274 range: convert_span(&spread),
275 },
276 )),
252 - None => convert_expression(&value.expr),
277 + None => convert_expression(cx, &value.expr),
278 }
279 })
280 .collect(),
@@ -259,7 +284,7 @@ fn convert_expression(expr: &Expr) -> estree::ExpressionLike {
284 }
285 Expr::Await(expr) => {
286 estree::ExpressionLike::AwaitExpression(Box::new(estree::AwaitExpression {
262 - argument: convert_expression(&expr.arg),
287 + argument: convert_expression(cx, &expr.arg),
288 loc: None,
289 range: convert_span(&expr.span),
290 }))
@@ -268,7 +293,7 @@ fn convert_expression(expr: &Expr) -> estree::ExpressionLike {
293 estree::ExpressionLike::UnaryExpression(Box::new(estree::UnaryExpression {
294 operator: convert_unary_operator(expr.op),
295 is_prefix: false,
271 - argument: convert_expression(&expr.arg),
296 + argument: convert_expression(cx, &expr.arg),
297 loc: None,
298 range: convert_span(&expr.span),
299 }))
@@ -277,8 +302,8 @@ fn convert_expression(expr: &Expr) -> estree::ExpressionLike {
302 Operator::Binary(op) => {
303 estree::ExpressionLike::BinaryExpression(Box::new(estree::BinaryExpression {
304 operator: op,
280 - left: convert_expression(&expr.left),
281 - right: convert_expression(&expr.right),
305 + left: convert_expression(cx, &expr.left),
306 + right: convert_expression(cx, &expr.right),
307 loc: None,
308 range: convert_span(&expr.span),
309 }))
@@ -286,8 +311,8 @@ fn convert_expression(expr: &Expr) -> estree::ExpressionLike {
311 Operator::Logical(op) => {
312 estree::ExpressionLike::LogicalExpression(Box::new(estree::LogicalExpression {
313 operator: op,
289 - left: convert_expression(&expr.left),
290 - right: convert_expression(&expr.right),
314 + left: convert_expression(cx, &expr.left),
315 + right: convert_expression(cx, &expr.right),
316 loc: None,
317 range: convert_span(&expr.span),
318 }))
@@ -321,7 +346,7 @@ fn convert_expression(expr: &Expr) -> estree::ExpressionLike {
346 estree::ExpressionLike::AssignmentExpression(Box::new(estree::AssignmentExpression {
347 operator: convert_assignment_operator(expr.op),
348 left: convert_assignment_target(&expr.left),
324 - right: convert_expression(&expr.right),
349 + right: convert_expression(cx, &expr.right),
350 loc: None,
351 range: convert_span(&expr.span),
352 }))
@@ -389,10 +414,11 @@ fn convert_binary_operator(op: BinaryOp) -> Operator {
414 }
415 }
416
392 -fn convert_pattern(pat: &Pat) -> estree::Pattern {
417 +fn convert_pattern(cx: &Context, pat: &Pat) -> estree::Pattern {
418 match pat {
419 Pat::Ident(pat) => estree::Pattern::Identifier(Box::new(estree::Identifier {
420 name: pat.id.sym.to_string(),
421 + binding: convert_binding(cx, pat.id.span.ctxt),
422 loc: None,
423 range: convert_span(&pat.span),
424 })),
@@ -400,10 +426,22 @@ fn convert_pattern(pat: &Pat) -> estree::Pattern {
426 }
427 }
428
403 -fn convert_identifier(identifier: &Ident) -> estree::Identifier {
429 +fn convert_binding(context: &Context, binding_cx: SyntaxContext) -> Option<Binding> {
430 + let id = BindingId::new(NonZeroU32::new(binding_cx.as_u32()).unwrap());
431 + if binding_cx.as_u32() == context.top_level_mark.as_u32() {
432 + Some(Binding::Global)
433 + } else if binding_cx.as_u32() == context.unresolved_mark.as_u32() {
434 + Some(Binding::Module(id))
435 + } else {
436 + Some(Binding::Local(id))
437 + }
438 +}
439 +
440 +fn convert_identifier(cx: &Context, identifier: &Ident) -> estree::Identifier {
441 let name = identifier.sym.as_ref().to_string();
442 estree::Identifier {
443 name,
444 + binding: convert_binding(cx, identifier.span.ctxt),
445 loc: None,
446 range: convert_span(&identifier.span),
447 }
compiler/forget/crates/estree/src/lib.rs
+29
@@ -1020,11 +1020,24 @@ pub struct AssignmentPattern {
1020 #[derive(Serialize, Deserialize, Debug)]
1021 pub struct Identifier {
1022 pub name: String,
1023 +
1024 + #[serde(default)]
1025 + pub binding: Option<Binding>,
1026 +
1027 pub loc: Option<SourceLocation>,
1028 +
1029 #[serde(default)]
1030 pub range: Option<SourceRange>,
1031 }
1032
1033 +#[derive(Serialize, Deserialize, Debug)]
1034 +#[serde(tag = "type")]
1035 +pub enum Binding {
1036 + Local(BindingId),
1037 + Module(BindingId),
1038 + Global,
1039 +}
1040 +
1041 #[derive(Serialize, Deserialize, Debug)]
1042 pub struct MemberExpression {
1043 pub object: ExpressionLike,
@@ -1240,6 +1253,22 @@ pub struct JSXText {
1253 pub range: Option<SourceRange>,
1254 }
1255
1256 +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone, Copy)]
1257 +#[serde(transparent)]
1258 +pub struct BindingId(NonZeroU32);
1259 +
1260 +impl BindingId {
1261 + pub fn new(id: NonZeroU32) -> Self {
1262 + Self(id)
1263 + }
1264 +}
1265 +
1266 +impl From<BindingId> for u32 {
1267 + fn from(value: BindingId) -> Self {
1268 + value.0.into()
1269 + }
1270 +}
1271 +
1272 #[cfg(test)]
1273 mod tests {
1274 use super::*;
compiler/forget/crates/estree/src/snapshots/estree__tests__fixtures@for-statement.json.snap
+9
@@ -541,6 +541,7 @@ Output:
541 "type": "FunctionDeclaration",
542 "id": {
543 "name": "Component",
544 + "binding": null,
545 "loc": {
546 "source": null,
547 "start": {
@@ -561,6 +562,7 @@ Output:
562 {
563 "type": "Identifier",
564 "name": "props",
565 + "binding": null,
566 "loc": {
567 "source": null,
568 "start": {
@@ -590,6 +592,7 @@ Output:
592 "id": {
593 "type": "Identifier",
594 "name": "x",
595 + "binding": null,
596 "loc": {
597 "source": null,
598 "start": {
@@ -669,6 +672,7 @@ Output:
672 "id": {
673 "type": "Identifier",
674 "name": "i",
675 + "binding": null,
676 "loc": {
677 "source": null,
678 "start": {
@@ -745,6 +749,7 @@ Output:
749 "left": {
750 "type": "Identifier",
751 "name": "i",
752 + "binding": null,
753 "loc": {
754 "source": null,
755 "start": {
@@ -803,6 +808,7 @@ Output:
808 "argument": {
809 "type": "Identifier",
810 "name": "i",
811 + "binding": null,
812 "loc": {
813 "source": null,
814 "start": {
@@ -847,6 +853,7 @@ Output:
853 "left": {
854 "type": "Identifier",
855 "name": "x",
856 + "binding": null,
857 "loc": {
858 "source": null,
859 "start": {
@@ -866,6 +873,7 @@ Output:
873 "right": {
874 "type": "Identifier",
875 "name": "i",
876 + "binding": null,
877 "loc": {
878 "source": null,
879 "start": {
@@ -953,6 +961,7 @@ Output:
961 "argument": {
962 "type": "Identifier",
963 "name": "x",
964 + "binding": null,
965 "loc": {
966 "source": null,
967 "start": {
compiler/forget/crates/estree/src/snapshots/estree__tests__fixtures@import.json.snap
+1
@@ -119,6 +119,7 @@ Output:
119 "type": "ImportDefaultSpecifier",
120 "local": {
121 "name": "React",
122 + "binding": null,
123 "loc": {
124 "source": null,
125 "start": {
compiler/forget/crates/estree/src/snapshots/estree__tests__fixtures@simple.json.snap
+4
@@ -202,6 +202,7 @@ Output:
202 "type": "FunctionDeclaration",
203 "id": {
204 "name": "Component",
205 + "binding": null,
206 "loc": {
207 "source": null,
208 "start": {
@@ -222,6 +223,7 @@ Output:
223 {
224 "type": "Identifier",
225 "name": "props",
226 + "binding": null,
227 "loc": {
228 "source": null,
229 "start": {
@@ -251,6 +253,7 @@ Output:
253 "object": {
254 "type": "Identifier",
255 "name": "props",
256 + "binding": null,
257 "loc": {
258 "source": null,
259 "start": {
@@ -270,6 +273,7 @@ Output:
273 "property": {
274 "type": "Identifier",
275 "name": "value",
276 + "binding": null,
277 "loc": {
278 "source": null,
279 "start": {
compiler/forget/crates/estree/src/snapshots/estree__tests__fixtures@test.json.snap
+1
@@ -364,6 +364,7 @@ Output:
364 "type": "FunctionDeclaration",
365 "id": {
366 "name": "foo",
367 + "binding": null,
368 "loc": {
369 "source": null,
370 "start": {
compiler/forget/crates/fixtures/tests/fixtures/identifiers.js new
+11
@@ -0,0 +1,11 @@
1 +// import React from "react";
2 +
3 +// const FOO = false;
4 +
5 +function id(x) {
6 + // React;
7 + // FOO;
8 + Math;
9 + id;
10 + return x;
11 +}
compiler/forget/crates/fixtures/tests/fixtures_test.rs
+2
@@ -11,6 +11,8 @@ fn fixtures() {
11 let input = std::fs::read_to_string(path).unwrap();
12 let ast = parse(&input, path.to_str().unwrap()).unwrap();
13
14 + println!("{:#?}", &ast);
15 +
16 let mut output = String::new();
17
18 for (ix, item) in ast.body.into_iter().enumerate() {
compiler/forget/crates/fixtures/tests/snapshots/fixtures_test__fixtures@identifiers.js.snap new
+27
@@ -0,0 +1,27 @@
1 +---
2 +source: crates/fixtures/tests/fixtures_test.rs
3 +expression: "format!(\"Input:\\n{input}\\n\\nOutput:\\n{output}\")"
4 +input_file: crates/fixtures/tests/fixtures/identifiers.js
5 +---
6 +Input:
7 +// import React from "react";
8 +
9 +// const FOO = false;
10 +
11 +function id(x) {
12 + // React;
13 + // FOO;
14 + Math;
15 + id;
16 + return x;
17 +}
18 +
19 +
20 +Output:
21 +entry bb0
22 +bb0
23 + [0] unknown $0 = LoadGlobal Math
24 + [1] unknown $2 = LoadGlobal id
25 + [2] unknown $4 = LoadLocal unknown x$3
26 + [3] Return unknown $4
27 +
compiler/forget/crates/fixtures/tests/snapshots/fixtures_test__fixtures@simple.js.snap renamed
+9 -9
@@ -13,14 +13,14 @@ function test() {
13 Output:
14 entry bb0
15 bb0
16 - [0] true
17 - [1] false
18 - [2] null
19 - [3] 1
20 - [4] 3.14
21 - [5] "hello world!"
22 - [6] Array [unknown $5]
23 - [7] Array [unknown $0, unknown $1, unknown $2, unknown $3, unknown $4, ...unknown $6]
24 - [8] 2
16 + [0] unknown $0 = true
17 + [1] unknown $1 = false
18 + [2] unknown $2 = null
19 + [3] unknown $3 = 1
20 + [4] unknown $4 = 3.14
21 + [5] unknown $5 = "hello world!"
22 + [6] unknown $6 = Array [unknown $5]
23 + [7] unknown $7 = Array [unknown $0, unknown $1, unknown $2, unknown $3, unknown $4, ...unknown $6]
24 + [8] unknown $8 = 2
25 [9] Return unknown $8
26
compiler/forget/crates/hir/src/instruction.rs
+6 -1
@@ -28,7 +28,7 @@ pub enum InstructionValue<'a> {
28 // JsxFragment(JsxFragment<'a>),
29 // JsxText(JsxText<'a>),
30 LoadContext(LoadContext<'a>),
31 - // LoadGlobal(LoadGlobal<'a>),
31 + LoadGlobal(LoadGlobal<'a>),
32 LoadLocal(LoadLocal<'a>),
33 // MethodCall(MethodCall<'a>),
34 // New(New<'a>),
@@ -100,6 +100,11 @@ pub struct LoadContext<'a> {
100 pub place: Place<'a>,
101 }
102
103 +#[derive(Debug)]
104 +pub struct LoadGlobal<'a> {
105 + pub name: String<'a>,
106 +}
107 +
108 #[derive(Debug)]
109 pub struct DeclareLocal<'a> {
110 pub lvalue: LValue<'a>,
compiler/forget/crates/hir/src/print.rs
+9
@@ -38,6 +38,8 @@ impl<'a> Print for BasicBlock<'a> {
38 impl<'a> Print for Instruction<'a> {
39 fn print(&self, out: &mut impl Write) -> Result {
40 write!(out, " {} ", self.id)?;
41 + self.lvalue.print(out)?;
42 + write!(out, " = ")?;
43 self.value.print(out)?;
44 writeln!(out, "")?;
45 Ok(())
@@ -57,6 +59,13 @@ impl<'a> Print for InstructionValue<'a> {
59 }
60 write!(out, "]")?;
61 }
62 + InstructionValue::LoadGlobal(value) => {
63 + write!(out, "LoadGlobal {}", &value.name)?;
64 + }
65 + InstructionValue::LoadLocal(value) => {
66 + write!(out, "LoadLocal ")?;
67 + value.place.print(out)?;
68 + }
69 InstructionValue::Primitive(value) => {
70 // Unlike other variants we don't print the variant name ("Primitive") since it's
71 // obvious