mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
[rust] basic VariableDeclaration support
This commit is contained in:
Generated
+1
@@ -587,6 +587,7 @@ name = "hir"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"estree",
|
||||
"indexmap 2.0.0",
|
||||
"serde",
|
||||
]
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
use bumpalo::collections::{CollectIn, String};
|
||||
use estree::{ExpressionLike, FunctionDeclaration, Literal, LiteralValue, Statement};
|
||||
use estree::{
|
||||
ExpressionLike, FunctionDeclaration, Literal, LiteralValue, Pattern, Statement,
|
||||
VariableDeclarationKind,
|
||||
};
|
||||
use hir::{
|
||||
ArrayElement, BlockKind, Environment, Function, GotoKind, Identifier, InstructionValue,
|
||||
LoadGlobal, LoadLocal, Place, PrimitiveValue, TerminalValue,
|
||||
ArrayElement, BlockKind, Environment, Function, GotoKind, Identifier, InstructionKind,
|
||||
InstructionValue, LValue, LoadGlobal, LoadLocal, Place, PrimitiveValue, TerminalValue,
|
||||
};
|
||||
|
||||
use crate::builder::{Binding, Builder};
|
||||
@@ -102,6 +105,39 @@ fn lower_statement<'a>(
|
||||
Statement::EmptyStatement(_) => {
|
||||
// no-op
|
||||
}
|
||||
Statement::VariableDeclaration(stmt) => {
|
||||
let kind = match stmt.kind {
|
||||
VariableDeclarationKind::Const => InstructionKind::Const,
|
||||
VariableDeclarationKind::Let => InstructionKind::Let,
|
||||
VariableDeclarationKind::Var => panic!("`var` declarations are not supported"),
|
||||
};
|
||||
for declaration in stmt.declarations {
|
||||
if let Some(init) = declaration.init {
|
||||
let value = lower_expression_to_temporary(env, builder, init);
|
||||
lower_assignment(env, builder, kind, declaration.id, value);
|
||||
} else {
|
||||
if let Pattern::Identifier(id) = declaration.id {
|
||||
// TODO: handle unbound variables
|
||||
let binding = builder.resolve_binding(&id).unwrap();
|
||||
let identifier = match binding {
|
||||
Binding::Local(identifier) => identifier,
|
||||
_ => panic!("Expected variable declaration to be a local binding"),
|
||||
};
|
||||
let place = Place {
|
||||
effect: None,
|
||||
identifier,
|
||||
};
|
||||
lower_value_to_temporary(
|
||||
env,
|
||||
builder,
|
||||
InstructionValue::DeclareLocal(hir::DeclareLocal {
|
||||
lvalue: LValue { place, kind },
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => todo!("Lower {stmt:#?}"),
|
||||
}
|
||||
Ok(())
|
||||
@@ -168,6 +204,46 @@ fn lower_expression<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
fn lower_assignment<'a>(
|
||||
env: &'a Environment<'a>,
|
||||
builder: &mut Builder<'a>,
|
||||
kind: InstructionKind,
|
||||
lvalue: Pattern,
|
||||
value: Place<'a>,
|
||||
) -> InstructionValue<'a> {
|
||||
match lvalue {
|
||||
Pattern::Identifier(lvalue) => {
|
||||
let place = lower_identifier_for_assignment(env, builder, kind, *lvalue).unwrap();
|
||||
let temporary = lower_value_to_temporary(
|
||||
env,
|
||||
builder,
|
||||
InstructionValue::StoreLocal(hir::StoreLocal {
|
||||
lvalue: LValue { place, kind },
|
||||
value,
|
||||
}),
|
||||
);
|
||||
InstructionValue::LoadLocal(LoadLocal { place: temporary })
|
||||
}
|
||||
_ => todo!("lower assignment for {:#?}", lvalue),
|
||||
}
|
||||
}
|
||||
|
||||
fn lower_identifier_for_assignment<'a>(
|
||||
env: &'a Environment<'a>,
|
||||
builder: &mut Builder<'a>,
|
||||
kind: InstructionKind,
|
||||
identifier: estree::Identifier,
|
||||
) -> Option<Place<'a>> {
|
||||
let binding = builder.resolve_binding(&identifier)?;
|
||||
match binding {
|
||||
Binding::Module(..) | Binding::Global => panic!("Cannot reassign a global"),
|
||||
Binding::Local(id) => Some(Place {
|
||||
identifier: id,
|
||||
effect: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Given an already lowered InstructionValue:
|
||||
/// - if the instruction is a LoadLocal for a temporary location, avoid the indirection
|
||||
/// and return the place that the LoadLocal loads from
|
||||
|
||||
@@ -21,7 +21,7 @@ use indexmap::IndexMap;
|
||||
/// generally involves driving calls to enter/exit blocks, resolve
|
||||
/// labels and variables, and then calling `build()` when the HIR
|
||||
/// is complete.
|
||||
pub struct Builder<'a> {
|
||||
pub(crate) struct Builder<'a> {
|
||||
#[allow(dead_code)]
|
||||
environment: &'a Environment<'a>,
|
||||
|
||||
@@ -32,8 +32,12 @@ pub struct Builder<'a> {
|
||||
wip: WipBlock<'a>,
|
||||
|
||||
id_gen: InstructionIdGenerator,
|
||||
}
|
||||
|
||||
bindings: HashMap<(bumpalo::collections::String<'a>, BindingId), Identifier<'a>>,
|
||||
pub(crate) struct WipBlock<'a> {
|
||||
pub id: BlockId,
|
||||
pub kind: BlockKind,
|
||||
pub instructions: Vec<'a, Instruction<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> Builder<'a> {
|
||||
@@ -50,7 +54,6 @@ impl<'a> Builder<'a> {
|
||||
entry,
|
||||
wip: current,
|
||||
id_gen: InstructionIdGenerator::new(),
|
||||
bindings: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,35 +151,16 @@ impl<'a> Builder<'a> {
|
||||
) -> 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))
|
||||
}
|
||||
estree::Binding::Local(id) => Binding::Local(
|
||||
self.environment
|
||||
.resolve_binding_identifier(&identifier.name, *id),
|
||||
),
|
||||
estree::Binding::Module(id) => Binding::Module(
|
||||
self.environment
|
||||
.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> {
|
||||
@@ -231,12 +215,6 @@ fn reverse_postorder_blocks<'a>(hir: &mut HIR<'a>) {
|
||||
hir.blocks = blocks;
|
||||
}
|
||||
|
||||
pub(crate) struct WipBlock<'a> {
|
||||
pub id: BlockId,
|
||||
pub kind: BlockKind,
|
||||
pub instructions: Vec<'a, Instruction<'a>>,
|
||||
}
|
||||
|
||||
/// Prunes ForTerminal.update values (sets to None) if they are unreachable
|
||||
fn remove_unreachable_for_updates<'a>(hir: &mut HIR<'a>) {
|
||||
let block_ids: HashSet<BlockId> = hir.blocks.keys().cloned().collect();
|
||||
|
||||
@@ -7,7 +7,7 @@ use swc_core::common::source_map::Pos;
|
||||
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,
|
||||
Program, Stmt, UnaryOp, VarDecl, VarDeclKind, VarDeclOrExpr,
|
||||
};
|
||||
use swc_core::ecma::parser::Syntax;
|
||||
use swc_core::ecma::transforms::base::resolver;
|
||||
@@ -95,7 +95,7 @@ fn convert_module_item(cx: &Context, item: &ModuleItem) -> estree::ModuleItem {
|
||||
ModuleItem::Stmt(item) => {
|
||||
estree::ModuleItem::Statement(Box::new(convert_statement(cx, item)))
|
||||
}
|
||||
_ => todo!("Convert {:#?}", item),
|
||||
_ => todo!("translate module item {:#?}", item),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,6 +152,9 @@ fn convert_statement(cx: &Context, stmt: &Stmt) -> estree::Statement {
|
||||
range: convert_span(&item.function.span),
|
||||
}))
|
||||
}
|
||||
Stmt::Decl(Decl::Var(item)) => {
|
||||
estree::Statement::VariableDeclaration(Box::new(convert_variable_declaration(cx, item)))
|
||||
}
|
||||
Stmt::Block(item) => {
|
||||
estree::Statement::BlockStatement(Box::new(convert_block_statement(cx, item)))
|
||||
}
|
||||
@@ -207,18 +210,9 @@ fn convert_statement(cx: &Context, stmt: &Stmt) -> estree::Statement {
|
||||
}
|
||||
VarDeclOrExpr::VarDecl(init) => {
|
||||
assert_eq!(init.decls.len(), 1);
|
||||
let decl = &init.decls[0];
|
||||
estree::ForInit::VariableDeclaration(Box::new(estree::VariableDeclaration {
|
||||
kind: convert_decl_kind(&init.kind),
|
||||
declarations: vec![estree::VariableDeclarator {
|
||||
id: convert_pattern(cx, &decl.name),
|
||||
init: decl.init.as_ref().map(|init| convert_expression(cx, init)),
|
||||
loc: None,
|
||||
range: convert_span(&decl.span),
|
||||
}],
|
||||
loc: None,
|
||||
range: convert_span(&init.span),
|
||||
}))
|
||||
estree::ForInit::VariableDeclaration(Box::new(convert_variable_declaration(
|
||||
cx, init,
|
||||
)))
|
||||
}
|
||||
}),
|
||||
test: item.test.as_ref().map(|test| convert_expression(cx, test)),
|
||||
@@ -249,7 +243,28 @@ fn convert_statement(cx: &Context, stmt: &Stmt) -> estree::Statement {
|
||||
loc: None,
|
||||
range: convert_span(&item.span),
|
||||
})),
|
||||
_ => todo!(),
|
||||
_ => todo!("translate statement {:#?}", stmt),
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_variable_declaration(cx: &Context, decl: &VarDecl) -> estree::VariableDeclaration {
|
||||
estree::VariableDeclaration {
|
||||
kind: convert_decl_kind(&decl.kind),
|
||||
declarations: decl
|
||||
.decls
|
||||
.iter()
|
||||
.map(|declarator| estree::VariableDeclarator {
|
||||
id: convert_pattern(cx, &declarator.name),
|
||||
init: declarator
|
||||
.init
|
||||
.as_ref()
|
||||
.map(|init| convert_expression(cx, init)),
|
||||
loc: None,
|
||||
range: convert_span(&decl.span),
|
||||
})
|
||||
.collect(),
|
||||
loc: None,
|
||||
range: convert_span(&decl.span),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -351,12 +366,12 @@ fn convert_expression(cx: &Context, expr: &Expr) -> estree::ExpressionLike {
|
||||
range: convert_span(&expr.span),
|
||||
}))
|
||||
}
|
||||
_ => todo!(),
|
||||
_ => todo!("translate expression {:#?}", expr),
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_assignment_target(_target: &PatOrExpr) -> estree::AssignmentTarget {
|
||||
todo!()
|
||||
todo!("translate assignment target")
|
||||
}
|
||||
|
||||
fn convert_unary_operator(op: UnaryOp) -> estree::UnaryOperator {
|
||||
@@ -374,7 +389,7 @@ fn convert_unary_operator(op: UnaryOp) -> estree::UnaryOperator {
|
||||
fn convert_assignment_operator(op: AssignOp) -> estree::AssignmentOperator {
|
||||
match op {
|
||||
AssignOp::AddAssign => estree::AssignmentOperator::PlusEquals,
|
||||
_ => todo!(),
|
||||
_ => todo!("translate assignment operator"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -422,7 +437,7 @@ fn convert_pattern(cx: &Context, pat: &Pat) -> estree::Pattern {
|
||||
loc: None,
|
||||
range: convert_span(&pat.span),
|
||||
})),
|
||||
_ => todo!(),
|
||||
_ => todo!("translate pattern {:#?}", pat),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1038,6 +1038,22 @@ pub enum Binding {
|
||||
Global,
|
||||
}
|
||||
|
||||
#[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()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct MemberExpression {
|
||||
pub object: ExpressionLike,
|
||||
@@ -1253,22 +1269,6 @@ 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::*;
|
||||
|
||||
@@ -7,5 +7,9 @@ function id(x) {
|
||||
// FOO;
|
||||
Math;
|
||||
id;
|
||||
let y = true;
|
||||
y;
|
||||
let z;
|
||||
z;
|
||||
return x;
|
||||
}
|
||||
|
||||
@@ -11,8 +11,6 @@ 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() {
|
||||
|
||||
+11
-2
@@ -13,6 +13,10 @@ function id(x) {
|
||||
// FOO;
|
||||
Math;
|
||||
id;
|
||||
let y = true;
|
||||
y;
|
||||
let z;
|
||||
z;
|
||||
return x;
|
||||
}
|
||||
|
||||
@@ -22,6 +26,11 @@ entry bb0
|
||||
bb0
|
||||
[0] unknown $0 = LoadGlobal Math
|
||||
[1] unknown $2 = LoadGlobal id
|
||||
[2] unknown $4 = LoadLocal unknown x$3
|
||||
[3] Return unknown $4
|
||||
[2] unknown $3 = true
|
||||
[3] unknown $5 = StoreLocal Let unknown y$4 = unknown $3
|
||||
[4] unknown $6 = LoadLocal unknown y$4
|
||||
[5] unknown $8 = DeclareLocal Let unknown z$7
|
||||
[6] unknown $9 = LoadLocal unknown z$7
|
||||
[7] unknown $11 = LoadLocal unknown x$10
|
||||
[8] Return unknown $11
|
||||
|
||||
|
||||
@@ -7,5 +7,6 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
bumpalo = { version = "3.13.0", features = ["boxed", "collections"] }
|
||||
estree = { path = "../estree" }
|
||||
indexmap = "2.0.0"
|
||||
serde = "1.0.164"
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
use std::cell::Cell;
|
||||
use std::{
|
||||
cell::{Cell, RefCell},
|
||||
collections::HashMap,
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
use bumpalo::Bump;
|
||||
use estree::BindingId;
|
||||
|
||||
use crate::{BlockId, Features, IdentifierId, Registry, TypeVarId};
|
||||
use crate::{
|
||||
BlockId, Features, Identifier, IdentifierData, IdentifierId, Registry, Type, TypeVarId,
|
||||
};
|
||||
|
||||
/// Stores all the contextual information about the top-level React function being
|
||||
/// compiled. Environments may not be reused between React functions, but *are*
|
||||
@@ -28,6 +35,8 @@ pub struct Environment<'a> {
|
||||
next_identifier_id: Cell<IdentifierId>,
|
||||
|
||||
next_type_var_id: Cell<TypeVarId>,
|
||||
|
||||
bindings: Rc<RefCell<HashMap<(bumpalo::collections::String<'a>, BindingId), Identifier<'a>>>>,
|
||||
}
|
||||
|
||||
impl<'a> Environment<'a> {
|
||||
@@ -39,6 +48,7 @@ impl<'a> Environment<'a> {
|
||||
next_block_id: Cell::new(BlockId(0)),
|
||||
next_identifier_id: Cell::new(IdentifierId(0)),
|
||||
next_type_var_id: Cell::new(TypeVarId(0)),
|
||||
bindings: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,4 +77,25 @@ impl<'a> Environment<'a> {
|
||||
self.next_type_var_id.set(id.next());
|
||||
id
|
||||
}
|
||||
|
||||
pub fn resolve_binding_identifier(&self, name: &str, binding_id: BindingId) -> Identifier<'a> {
|
||||
let key_name = bumpalo::collections::String::from_str_in(name, &self.allocator);
|
||||
let mut bindings = self.bindings.borrow_mut();
|
||||
if let Some(identifier) = bindings.get(&(key_name.clone(), binding_id)) {
|
||||
identifier.clone()
|
||||
} else {
|
||||
let id = self.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.next_type_var_id()),
|
||||
})),
|
||||
};
|
||||
bindings.insert((key_name, binding_id), identifier.clone());
|
||||
identifier
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,6 +145,16 @@ pub enum InstructionKind {
|
||||
Reassign,
|
||||
}
|
||||
|
||||
impl Display for InstructionKind {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Const => f.write_str("Const"),
|
||||
Self::Let => f.write_str("Let"),
|
||||
Self::Reassign => f.write_str("Reassign"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||
pub enum Effect {
|
||||
/// This reference freezes the value (corresponds to a place where codegen should emit a freeze instruction)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use std::fmt::{Result, Write};
|
||||
|
||||
use crate::{
|
||||
ArrayElement, BasicBlock, Function, Instruction, InstructionValue, Place, PrimitiveValue,
|
||||
Terminal, TerminalValue,
|
||||
ArrayElement, BasicBlock, Function, Instruction, InstructionValue, LValue, Place,
|
||||
PrimitiveValue, Terminal, TerminalValue,
|
||||
};
|
||||
|
||||
/// Trait for HIR types to describe how they print themselves.
|
||||
@@ -80,6 +80,16 @@ impl<'a> Print for InstructionValue<'a> {
|
||||
PrimitiveValue::Undefined => write!(out, "<undefined>")?,
|
||||
};
|
||||
}
|
||||
InstructionValue::StoreLocal(value) => {
|
||||
write!(out, "StoreLocal ")?;
|
||||
value.lvalue.print(out)?;
|
||||
write!(out, " = ")?;
|
||||
value.value.print(out)?;
|
||||
}
|
||||
InstructionValue::DeclareLocal(value) => {
|
||||
write!(out, "DeclareLocal ")?;
|
||||
value.lvalue.print(out)?;
|
||||
}
|
||||
_ => write!(out, "{:?}", self)?,
|
||||
}
|
||||
Ok(())
|
||||
@@ -117,6 +127,13 @@ impl<'a> Print for Place<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Print for LValue<'a> {
|
||||
fn print(&self, out: &mut impl Write) -> Result {
|
||||
write!(out, "{} ", self.kind)?;
|
||||
self.place.print(out)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Print for Terminal<'a> {
|
||||
fn print(&self, out: &mut impl Write) -> Result {
|
||||
write!(out, " {} ", self.id)?;
|
||||
|
||||
Reference in New Issue
Block a user