rusty-javac 0.2.3

A Java compiler written in Rust.
Documentation
use crate::ast::{AstNode, ClassDecl, JavaSyntaxKind, JavaSyntaxNode};
use crate::hir::lowering::expr::BodyBuilder;
use crate::hir::lowering::member::lower_class_members;
use crate::hir::lowering::syntax::ExprToken;
use crate::hir::lowering::{LowerError, LowerResult};
use crate::hir::*;
use crate::ty::{MethodSig, Ty};
use std::rc::Rc;
use ustr::Ustr;

pub(super) fn lower_object(
    body: &mut BodyBuilder,
    base_type: Ty,
    args: Vec<ExprId>,
    body_tokens: Vec<ExprToken>,
) -> LowerResult<ExprId> {
    let class_body = parse_class_body(&body_tokens)?;
    let class_name = next_class_name(body, &body_tokens)?;
    let base_internal_name = base_type.internal_name();
    let is_interface = body.type_resolver.is_interface(&base_internal_name);
    let super_name = if is_interface {
        "java/lang/Object".to_string()
    } else {
        base_internal_name
    };
    let arg_types = args
        .iter()
        .map(|arg| body.expr_ty(*arg))
        .collect::<Vec<_>>();
    let super_params = if is_interface {
        Vec::new()
    } else {
        body.type_resolver
            .resolve_constructor(&base_type, &arg_types)
            .map(|method| method.params)
            .unwrap_or_else(|| arg_types.clone())
    };
    let captures_this = body.can_capture_this;
    let outer_this = captures_this.then(|| OuterThisInfo {
        field_name: Ustr::from("this$0"),
        ty: body.type_resolver.current_class_ty(),
    });
    let super_call = SuperConstructorCall {
        owner: if is_interface {
            Ty::object()
        } else {
            base_type.clone()
        },
        params: super_params.clone(),
        arg_offset: usize::from(captures_this),
    };
    let mut members = lower_class_members(
        class_body,
        &[],
        &body
            .type_resolver
            .for_anonymous_class(class_name.as_str(), &super_name),
        body.enclosing_static_owner,
    )?;

    let mut fields = Vec::new();
    if let Some(outer_this) = &outer_this {
        fields.push(outer_this_field(outer_this));
    }
    fields.append(&mut members.fields);

    let mut methods = vec![anonymous_constructor(
        anonymous_constructor_params(outer_this.as_ref(), &super_params),
        super_call.clone(),
    )];
    methods.append(&mut members.methods);

    body.anonymous_types.push(Rc::new(TypeDecl {
        id: HirId(0),
        name: class_name,
        kind: TypeDeclKind::Class,
        access_flags: 0,
        super_class: (!is_interface).then_some(base_type.clone()),
        interfaces: is_interface
            .then_some(base_type.clone())
            .into_iter()
            .collect(),
        type_params: Vec::new(),
        generic_signature: None,
        fields,
        methods,
        inner_types: members.inner_types,
        anonymous: Some(AnonymousClassInfo {
            super_constructor: super_call,
            outer_this,
            enclosing_static_owner: body.enclosing_static_owner,
            outer_fields: body.outer_fields.clone(),
        }),
        record_components: Vec::new(),
        annotations: Vec::new(),
    }));

    Ok(body.alloc_expr(Expr::NewObject {
        class: base_type,
        args,
        anonymous: Some(AnonymousObject {
            class_name,
            constructor_params: super_params,
            captures_enclosing_this: captures_this,
        }),
    }))
}

fn next_class_name(body: &mut BodyBuilder, tokens: &[ExprToken]) -> LowerResult<Ustr> {
    let owner = body
        .type_resolver
        .current_class_name()
        .ok_or_else(|| unsupported_anonymous_class(tokens))?;
    let next = body.anonymous_counter.get() + 1;
    body.anonymous_counter.set(next);
    Ok(Ustr::from(&format!("{owner}${next}")))
}

fn parse_class_body(tokens: &[ExprToken]) -> LowerResult<crate::ast::ClassBody> {
    let body_source = token_source(tokens);
    let source = if tokens
        .first()
        .is_some_and(|token| token.kind == JavaSyntaxKind::LBrace)
    {
        format!("class Anonymous {body_source}")
    } else {
        format!("class Anonymous {{ {body_source} }}")
    };
    let parse = crate::parser::Parser::parse(&source);
    if !parse.errors.is_empty() {
        return Err(unsupported_anonymous_class(tokens));
    }

    let root = JavaSyntaxNode::new_root(parse.green_node);
    ClassDecl::cast(root.clone())
        .or_else(|| root.children().find_map(ClassDecl::cast))
        .and_then(|class| class.body())
        .ok_or_else(|| unsupported_anonymous_class(tokens))
}

fn unsupported_anonymous_class(tokens: &[ExprToken]) -> LowerError {
    tokens
        .first()
        .map(|token| LowerError::UnsupportedExpressionAt {
            line: token.line,
            range: Some(token.range),
        })
        .unwrap_or(LowerError::UnsupportedExpression)
}

fn token_source(tokens: &[ExprToken]) -> String {
    let mut source = String::new();
    let mut line = 1u16;
    for token in tokens {
        while line < token.line {
            source.push('\n');
            line += 1;
        }
        if !source.ends_with(['\n', ' ', '{', '(']) {
            source.push(' ');
        }
        source.push_str(&token.text);
    }
    source
}

fn anonymous_constructor_params(
    outer_this: Option<&OuterThisInfo>,
    super_params: &[Ty],
) -> Vec<ParamDecl> {
    let mut params = Vec::new();
    if let Some(outer_this) = outer_this {
        params.push(ParamDecl {
            name: outer_this.field_name,
            ty: outer_this.ty.clone(),
        });
    }
    params.extend(
        super_params
            .iter()
            .enumerate()
            .map(|(index, ty)| ParamDecl {
                name: Ustr::from(&format!("arg{index}")),
                ty: ty.clone(),
            }),
    );
    params
}

fn anonymous_constructor(
    params: Vec<ParamDecl>,
    constructor_call: SuperConstructorCall,
) -> MethodDecl {
    let signature = MethodSig::new(
        Ustr::from("<init>"),
        params.iter().map(|param| param.ty.clone()).collect(),
        Ty::Void,
    );
    MethodDecl {
        id: HirId(0),
        name: Ustr::from("<init>"),
        params,
        signature,
        access_flags: 0,
        source_line: None,
        generic_signature: None,
        throws: Vec::new(),
        body: Body::default(),
        root_block: Some(Block { stmts: Vec::new() }),
        constructor_call: Some(constructor_call),
    }
}

fn outer_this_field(outer_this: &OuterThisInfo) -> FieldDecl {
    FieldDecl {
        id: HirId(0),
        name: outer_this.field_name,
        ty: outer_this.ty.clone(),
        access_flags: crate::classfile::ACC_PRIVATE
            | crate::classfile::ACC_FINAL
            | crate::classfile::ACC_SYNTHETIC,
        generic_signature: None,
        body: Body::default(),
        initializer: None,
    }
}