[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.
This commit is contained in:
Joe Savona
2023-07-06 09:24:48 +09:00
parent 08e51d3fd7
commit f6957e9a32
14 changed files with 269 additions and 65 deletions
+20 -4
View File
@@ -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),
}),
@@ -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<Identifier>) -> Result<BlockId, Diagnostic> {
pub(crate) fn resolve_break(
&self,
_label: Option<&estree::Identifier>,
) -> Result<BlockId, Diagnostic> {
todo!()
}
@@ -140,10 +150,52 @@ impl<'a> Builder<'a> {
/// provided but cannot be resolved.
pub(crate) fn resolve_continue(
&self,
_label: Option<Identifier>,
_label: Option<&estree::Identifier>,
) -> Result<BlockId, Diagnostic> {
todo!()
}
pub(crate) fn resolve_binding(
&mut self,
identifier: &estree::Identifier,
) -> Option<Binding<'a>> {
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
+84 -46
View File
@@ -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, Box<dyn std::e
Some(&comments),
)?;
let context = Context {
top_level_mark: Mark::new(),
unresolved_mark: Mark::new(),
};
let module = c.run_transform(&handler, false, || {
let unresolved_mark = Mark::new();
let top_level_mark = Mark::new();
module.fold_with(&mut resolver(unresolved_mark, top_level_mark, true))
module.fold_with(&mut resolver(
context.unresolved_mark,
context.top_level_mark,
true,
))
});
Ok(convert_program(&module))
Ok(convert_program(&context, &module))
})
}
fn convert_program(program: &Program) -> 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<estree::ModuleItem>;
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<estree::Statement> = 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(&param.pat))
.map(|param| convert_pattern(cx, &param.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<Binding> {
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),
}
+29
View File
@@ -1020,11 +1020,24 @@ pub struct AssignmentPattern {
#[derive(Serialize, Deserialize, Debug)]
pub struct Identifier {
pub name: String,
#[serde(default)]
pub binding: Option<Binding>,
pub loc: Option<SourceLocation>,
#[serde(default)]
pub range: Option<SourceRange>,
}
#[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<SourceRange>,
}
#[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<BindingId> for u32 {
fn from(value: BindingId) -> Self {
value.0.into()
}
}
#[cfg(test)]
mod tests {
use super::*;
@@ -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": {
@@ -119,6 +119,7 @@ Output:
"type": "ImportDefaultSpecifier",
"local": {
"name": "React",
"binding": null,
"loc": {
"source": null,
"start": {
@@ -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": {
@@ -364,6 +364,7 @@ Output:
"type": "FunctionDeclaration",
"id": {
"name": "foo",
"binding": null,
"loc": {
"source": null,
"start": {
@@ -0,0 +1,11 @@
// import React from "react";
// const FOO = false;
function id(x) {
// React;
// FOO;
Math;
id;
return x;
}
@@ -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() {
@@ -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
@@ -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
@@ -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>,
+9
View File
@@ -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