rusty-javac 0.2.2

A Java compiler written in Rust.
Documentation
use crate::bytecode::codegen::CodegenCtx;
use crate::bytecode::expr_gen::{coerce, expr_ty, gen_expr, literals};
use crate::classfile::MethodWriter;
use crate::hir::{ArrayInit, Body, ExprId};
use crate::ty::Ty;
use rust_asm::opcodes;

pub(super) fn emit_new_array(
    mw: &mut MethodWriter,
    ctx: &mut CodegenCtx,
    body: &Body,
    element_type: &Ty,
    dimensions: &[Option<ExprId>],
    initializer: Option<&ArrayInit>,
) {
    if element_type.is_primitive() {
        emit_array_length(mw, ctx, body, dimensions, initializer);
        mw.visit_new_array(primitive_array_type_code(element_type));
    } else {
        emit_array_length(mw, ctx, body, dimensions, initializer);
        mw.visit_type_insn(opcodes::ANEWARRAY, &element_type.internal_name());
    }

    if let Some(initializer) = initializer {
        emit_array_initializer(mw, ctx, body, element_type, initializer);
    }
}

pub(super) fn emit_array_access(
    mw: &mut MethodWriter,
    ctx: &mut CodegenCtx,
    body: &Body,
    array: ExprId,
    index: ExprId,
) {
    let element_type = array_element_type(ctx, body, array);
    gen_expr(mw, ctx, body, array);
    gen_expr(mw, ctx, body, index);
    coerce(mw, &expr_ty(ctx, body, index), &Ty::Int);
    mw.visit_insn(array_load_opcode(&element_type));
}

pub(super) fn array_store_opcode(element_type: &Ty) -> u8 {
    match element_type.erasure() {
        Ty::Long => opcodes::LASTORE,
        Ty::Float => opcodes::FASTORE,
        Ty::Double => opcodes::DASTORE,
        Ty::Class(_) | Ty::Array(_) | Ty::TypeVar(_) | Ty::Wildcard(_) | Ty::Intersection(_) => {
            opcodes::AASTORE
        }
        Ty::Byte | Ty::Boolean => opcodes::BASTORE,
        Ty::Char => opcodes::CASTORE,
        Ty::Short => opcodes::SASTORE,
        _ => opcodes::IASTORE,
    }
}

pub(super) fn array_element_type(ctx: &CodegenCtx, body: &Body, array: ExprId) -> Ty {
    match expr_ty(ctx, body, array).erasure() {
        Ty::Array(element) => *element,
        _ => Ty::Int,
    }
}

fn emit_array_length(
    mw: &mut MethodWriter,
    ctx: &mut CodegenCtx,
    body: &Body,
    dimensions: &[Option<ExprId>],
    initializer: Option<&ArrayInit>,
) {
    if let Some(Some(size)) = dimensions.first() {
        gen_expr(mw, ctx, body, *size);
        coerce(mw, &expr_ty(ctx, body, *size), &Ty::Int);
    } else {
        let len = initializer
            .map(|init| init.elements.len())
            .unwrap_or_default();
        literals::emit_int(mw, len as i64);
    }
}

fn emit_array_initializer(
    mw: &mut MethodWriter,
    ctx: &mut CodegenCtx,
    body: &Body,
    element_type: &Ty,
    initializer: &ArrayInit,
) {
    for (index, element) in initializer.elements.iter().copied().enumerate() {
        mw.visit_insn(opcodes::DUP);
        literals::emit_int(mw, index as i64);
        gen_expr(mw, ctx, body, element);
        coerce(mw, &expr_ty(ctx, body, element), element_type);
        mw.visit_insn(array_store_opcode(element_type));
    }
}

pub(super) fn primitive_array_type_code(ty: &Ty) -> u8 {
    match ty {
        Ty::Boolean => 4,
        Ty::Char => 5,
        Ty::Float => 6,
        Ty::Double => 7,
        Ty::Byte => 8,
        Ty::Short => 9,
        Ty::Int => 10,
        Ty::Long => 11,
        _ => 10,
    }
}

pub(crate) fn array_load_opcode(element_type: &Ty) -> u8 {
    match element_type.erasure() {
        Ty::Long => opcodes::LALOAD,
        Ty::Float => opcodes::FALOAD,
        Ty::Double => opcodes::DALOAD,
        Ty::Class(_) | Ty::Array(_) | Ty::TypeVar(_) | Ty::Wildcard(_) | Ty::Intersection(_) => {
            opcodes::AALOAD
        }
        Ty::Byte | Ty::Boolean => opcodes::BALOAD,
        Ty::Char => opcodes::CALOAD,
        Ty::Short => opcodes::SALOAD,
        _ => opcodes::IALOAD,
    }
}