rusty-javac 0.2.3

A Java compiler written in Rust.
Documentation
use crate::ast::{AstNode, EnumDecl, JavaSyntaxKind, JavaSyntaxNode};
use crate::call_resolver::ClassCatalog;
use crate::hir::lowering::expr::BodyBuilder;
use crate::hir::lowering::member::{MemberOptions, lower_members};
use crate::hir::lowering::signature::{class_signature, lower_type_params};
use crate::hir::lowering::syntax::{ExprToken, first_ident, tokens_in_first_parens};
use crate::hir::lowering::types::TypeResolver;
use crate::hir::lowering::unit::internal_class_name;
use crate::hir::lowering::{LowerError, LowerResult};
use crate::hir::*;
use crate::ty::{MethodSig, Ty};
use ustr::Ustr;

const ENUM_VALUES_FIELD: &str = "$VALUES";

pub(super) fn lower_enum_decl(
    enum_decl: EnumDecl,
    access_flags: u16,
    modifiers: Option<&JavaSyntaxNode>,
    package: Option<&Package>,
    imports: &[Import],
    catalog: &ClassCatalog,
) -> LowerResult<TypeDecl> {
    let name = enum_decl.name().ok_or(LowerError::MissingClassName)?;
    let internal_name = internal_class_name(package, name.text());
    let resolver = TypeResolver::for_class(package, imports, &internal_name, catalog)?;
    let annotations =
        crate::hir::lowering::annotation::lower_annotation_uses(modifiers, package, &resolver)?;
    let type_params = lower_type_params(enum_decl.syntax(), &resolver)?;
    let generic_signature = class_signature(enum_decl.syntax(), &type_params, &resolver)?;
    let constants = enum_constants(enum_decl.syntax(), &internal_name, &resolver)?;

    let mut members = enum_decl
        .body()
        .map(|body| {
            lower_members(
                body.syntax(),
                &type_params,
                &resolver,
                Some(Ustr::from(&internal_name)),
                MemberOptions::class(),
            )
        })
        .transpose()?
        .unwrap_or_default();

    let mut fields = constants;
    fields.append(&mut members.fields);
    fields.push(enum_values_field(&internal_name, fields.len() as u32 + 1));

    let mut methods = members.methods;
    prepare_enum_constructors(&mut methods);
    if !methods.iter().any(|method| method.name == "<init>") {
        methods.push(default_enum_constructor(methods.len() as u32 + 1));
    }
    methods.push(enum_values_method(&internal_name, methods.len() as u32 + 1));
    methods.push(enum_value_of_method(
        &internal_name,
        methods.len() as u32 + 1,
    ));

    Ok(TypeDecl {
        id: HirId(0),
        name: Ustr::from(&internal_name),
        kind: TypeDeclKind::Enum,
        access_flags: access_flags | crate::classfile::ACC_FINAL | crate::classfile::ACC_ENUM,
        super_class: Some(Ty::class("java/lang/Enum")),
        interfaces: Vec::new(),
        type_params,
        generic_signature,
        fields,
        methods,
        inner_types: members.inner_types,
        anonymous: None,
        record_components: Vec::new(),
        annotations,
    })
}

fn enum_constants(
    enum_decl: &JavaSyntaxNode,
    owner: &str,
    resolver: &TypeResolver,
) -> LowerResult<Vec<FieldDecl>> {
    let Some(list) = enum_decl
        .descendants()
        .find(|node| node.kind() == JavaSyntaxKind::EnumConstantList)
    else {
        return Ok(Vec::new());
    };

    list.children()
        .filter(|child| child.kind() == JavaSyntaxKind::EnumConstant)
        .enumerate()
        .map(|(index, constant)| enum_constant_field(&constant, owner, resolver, index))
        .collect()
}

fn enum_constant_field(
    constant: &JavaSyntaxNode,
    owner: &str,
    resolver: &TypeResolver,
    index: usize,
) -> LowerResult<FieldDecl> {
    let name = first_ident(constant).ok_or(LowerError::MissingMethodName)?;
    let mut body = BodyBuilder::new(resolver.clone());
    let mut args = vec![
        body.alloc_expr(Expr::StringLiteral(Ustr::from(name.text()))),
        body.alloc_expr(Expr::IntLiteral(index as i64)),
    ];
    args.extend(enum_constant_args(constant, &mut body)?);
    let initializer = body.alloc_expr(Expr::NewObject {
        class: Ty::class(owner),
        args,
        anonymous: None,
    });

    Ok(FieldDecl {
        id: HirId(index as u32 + 1),
        name: Ustr::from(name.text()),
        ty: Ty::class(owner),
        access_flags: crate::classfile::ACC_PUBLIC
            | crate::classfile::ACC_STATIC
            | crate::classfile::ACC_FINAL
            | crate::classfile::ACC_ENUM,
        generic_signature: None,
        body: body.body,
        initializer: Some(initializer),
    })
}

fn enum_constant_args(
    constant: &JavaSyntaxNode,
    body: &mut BodyBuilder,
) -> LowerResult<Vec<ExprId>> {
    argument_token_groups(constant)
        .into_iter()
        .map(|tokens| {
            body.lower_expr_tokens(&tokens)?
                .ok_or(LowerError::UnsupportedExpression)
        })
        .collect()
}

fn argument_token_groups(node: &JavaSyntaxNode) -> Vec<Vec<ExprToken>> {
    let Ok(tokens) = tokens_in_first_parens(node) else {
        return Vec::new();
    };
    let mut groups = Vec::new();
    let mut current = Vec::new();
    let mut depth = 0usize;

    for token in tokens {
        match token.kind {
            JavaSyntaxKind::LParen => {
                depth += 1;
                current.push(token);
            }
            JavaSyntaxKind::RParen => {
                depth = depth.saturating_sub(1);
                current.push(token);
            }
            JavaSyntaxKind::Comma if depth == 0 => {
                groups.push(std::mem::take(&mut current));
            }
            _ => current.push(token),
        }
    }

    if !current.is_empty() {
        groups.push(current);
    }
    groups
}

fn enum_values_field(owner: &str, id: u32) -> FieldDecl {
    FieldDecl {
        id: HirId(id),
        name: Ustr::from(ENUM_VALUES_FIELD),
        ty: Ty::Array(Box::new(Ty::class(owner))),
        access_flags: crate::classfile::ACC_PRIVATE
            | crate::classfile::ACC_STATIC
            | crate::classfile::ACC_FINAL
            | crate::classfile::ACC_SYNTHETIC,
        generic_signature: None,
        body: Body::default(),
        initializer: None,
    }
}

fn prepare_enum_constructors(methods: &mut [MethodDecl]) {
    for method in methods.iter_mut().filter(|method| method.name == "<init>") {
        let mut params = enum_synthetic_params();
        params.extend(method.params.clone());
        method.params = params;
        method.signature.params = method.params.iter().map(|param| param.ty.clone()).collect();
        method.access_flags = private_access(method.access_flags);
        method.constructor_call = Some(enum_super_call());
    }
}

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

fn enum_synthetic_params() -> Vec<ParamDecl> {
    vec![
        ParamDecl {
            name: Ustr::from("$name"),
            ty: Ty::string(),
        },
        ParamDecl {
            name: Ustr::from("$ordinal"),
            ty: Ty::Int,
        },
    ]
}

fn enum_super_call() -> SuperConstructorCall {
    SuperConstructorCall {
        owner: Ty::class("java/lang/Enum"),
        params: vec![Ty::string(), Ty::Int],
        arg_offset: 0,
    }
}

fn private_access(flags: u16) -> u16 {
    let access_mask = crate::classfile::ACC_PUBLIC
        | crate::classfile::ACC_PROTECTED
        | crate::classfile::ACC_PRIVATE;
    (flags & !access_mask) | crate::classfile::ACC_PRIVATE
}

fn enum_values_method(owner: &str, id: u32) -> MethodDecl {
    let return_ty = Ty::Array(Box::new(Ty::class(owner)));
    MethodDecl {
        id: HirId(id),
        name: Ustr::from("values"),
        params: Vec::new(),
        signature: MethodSig::new(Ustr::from("values"), Vec::new(), return_ty),
        access_flags: crate::classfile::ACC_PUBLIC | crate::classfile::ACC_STATIC,
        source_line: Some(1),
        generic_signature: None,
        throws: Vec::new(),
        body: Body::default(),
        root_block: None,
        constructor_call: None,
    }
}

fn enum_value_of_method(owner: &str, id: u32) -> MethodDecl {
    let params = vec![ParamDecl {
        name: Ustr::from("name"),
        ty: Ty::string(),
    }];
    MethodDecl {
        id: HirId(id),
        name: Ustr::from("valueOf"),
        params: params.clone(),
        signature: MethodSig::new(
            Ustr::from("valueOf"),
            params.iter().map(|param| param.ty.clone()).collect(),
            Ty::class(owner),
        ),
        access_flags: crate::classfile::ACC_PUBLIC | crate::classfile::ACC_STATIC,
        source_line: Some(1),
        generic_signature: None,
        throws: Vec::new(),
        body: Body::default(),
        root_block: None,
        constructor_call: None,
    }
}