mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
[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:
@@ -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
|
||||
|
||||
@@ -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(¶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<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),
|
||||
}
|
||||
|
||||
@@ -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::*;
|
||||
|
||||
+9
@@ -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() {
|
||||
|
||||
+27
@@ -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
|
||||
|
||||
+9
-9
@@ -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>,
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user