Skip to main content

rusty_javac/bytecode/
class_gen.rs

1use crate::bytecode::codegen::CodegenCtx;
2use crate::bytecode::error::BytecodeError;
3use crate::bytecode::lambda::{self, LambdaTable};
4use crate::call_resolver::ClassCatalog;
5use crate::classfile::{
6    AnnotationElementMetadata, AnnotationElementValueMetadata, AnnotationMetadata, ClassFileWriter,
7};
8use crate::hir::*;
9use crate::ty::Ty;
10use rust_asm::constants::V21;
11use rust_asm::opcodes;
12
13const OBJECT_CLASS: &str = "java/lang/Object";
14const INIT_METHOD: &str = "<init>";
15const CLINIT_METHOD: &str = "<clinit>";
16
17pub struct GeneratedClass {
18    pub internal_name: String,
19    pub bytes: Vec<u8>,
20}
21
22pub fn gen_class(unit: &CompilationUnit) -> Result<Vec<u8>, BytecodeError> {
23    let catalog = ClassCatalog::platform();
24    gen_class_with_catalog(unit, &catalog)
25}
26
27pub fn gen_class_with_catalog(
28    unit: &CompilationUnit,
29    catalog: &ClassCatalog,
30) -> Result<Vec<u8>, BytecodeError> {
31    gen_class_with_source_file(unit, catalog, None)
32}
33
34pub fn gen_class_with_source_file(
35    unit: &CompilationUnit,
36    catalog: &ClassCatalog,
37    source_file: Option<&str>,
38) -> Result<Vec<u8>, BytecodeError> {
39    gen_classes_with_source_file(unit, catalog, source_file)?
40        .into_iter()
41        .next()
42        .map(|class| class.bytes)
43        .ok_or_else(|| BytecodeError::new("no type declarations"))
44}
45
46pub fn gen_classes_with_source_file(
47    unit: &CompilationUnit,
48    catalog: &ClassCatalog,
49    source_file: Option<&str>,
50) -> Result<Vec<GeneratedClass>, BytecodeError> {
51    if unit.type_decls.is_empty() {
52        return Err(BytecodeError::new("no type declarations"));
53    }
54
55    let mut classes = Vec::new();
56    for type_decl in &unit.type_decls {
57        let nest_members = nested_type_names(type_decl);
58        gen_type_decl_class(
59            type_decl,
60            catalog,
61            source_file,
62            None,
63            &nest_members,
64            type_decl.name.as_str(),
65            &mut classes,
66        )?;
67    }
68    Ok(classes)
69}
70
71fn gen_type_decl_class(
72    type_decl: &TypeDecl,
73    catalog: &ClassCatalog,
74    source_file: Option<&str>,
75    nest_host: Option<&str>,
76    nest_members: &[String],
77    root_nest_host: &str,
78    classes: &mut Vec<GeneratedClass>,
79) -> Result<(), BytecodeError> {
80    crate::bytecode::validation::validate_type_decl(type_decl, catalog)?;
81    let mut writer = ClassFileWriter::new();
82    if let Some(source_file) = source_file {
83        writer.visit_source_file(source_file);
84    }
85    if let Some(host) = nest_host {
86        writer.visit_nest_host(host);
87    } else {
88        for member in nest_members {
89            writer.visit_nest_member(member);
90        }
91    }
92    gen_type_decl(&mut writer, type_decl, catalog);
93    classes.push(GeneratedClass {
94        internal_name: type_decl.name.to_string(),
95        bytes: writer.to_bytes().map_err(BytecodeError::new)?,
96    });
97    for inner in &type_decl.inner_types {
98        gen_type_decl_class(
99            inner,
100            catalog,
101            source_file,
102            Some(root_nest_host),
103            &[],
104            root_nest_host,
105            classes,
106        )?;
107    }
108    Ok(())
109}
110
111fn nested_type_names(type_decl: &TypeDecl) -> Vec<String> {
112    let mut names = Vec::new();
113    collect_nested_type_names(type_decl, &mut names);
114    names
115}
116
117fn collect_nested_type_names(type_decl: &TypeDecl, names: &mut Vec<String>) {
118    for inner in &type_decl.inner_types {
119        names.push(inner.name.to_string());
120        collect_nested_type_names(inner, names);
121    }
122}
123
124fn gen_type_decl(writer: &mut ClassFileWriter, type_decl: &TypeDecl, catalog: &ClassCatalog) {
125    let access_flags = class_access_flags(type_decl);
126    let super_name = super_name(type_decl);
127    let interfaces = interface_names(type_decl);
128    let interface_refs: Vec<_> = interfaces.iter().map(String::as_str).collect();
129
130    writer.visit(
131        V21,
132        access_flags,
133        &type_decl.name,
134        Some(super_name.as_str()),
135        &interface_refs,
136    );
137    if let Some(signature) = &type_decl.generic_signature {
138        writer.visit_signature(signature);
139    }
140    for annotation in &type_decl.annotations {
141        writer.visit_runtime_invisible_annotation(annotation_metadata(annotation));
142    }
143    for component in &type_decl.record_components {
144        writer.visit_record_component(
145            component.name.as_str(),
146            &component.ty.erasure().descriptor(),
147            component.generic_signature.as_deref(),
148        );
149    }
150
151    gen_fields(writer, &type_decl.fields);
152    gen_static_initializer(writer, type_decl, catalog);
153    if needs_default_constructor(type_decl) {
154        gen_default_constructor(writer, type_decl, &super_name, catalog);
155    }
156
157    let mut counter = 0u32;
158    for method in &type_decl.methods {
159        let lambdas = lambda::emit_lambda_methods(
160            writer,
161            type_decl,
162            &super_name,
163            catalog,
164            method,
165            &mut counter,
166        );
167        gen_method(writer, type_decl, method, &super_name, catalog, &lambdas);
168    }
169}
170
171fn annotation_metadata(annotation: &AnnotationUse) -> AnnotationMetadata {
172    AnnotationMetadata {
173        descriptor: annotation.descriptor.clone(),
174        elements: annotation
175            .elements
176            .iter()
177            .map(|element| AnnotationElementMetadata {
178                name: element.name.to_string(),
179                value: match &element.value {
180                    AnnotationValue::String(value) => {
181                        AnnotationElementValueMetadata::String(value.to_string())
182                    }
183                    AnnotationValue::Int(value) => AnnotationElementValueMetadata::Int(*value),
184                    AnnotationValue::Boolean(value) => {
185                        AnnotationElementValueMetadata::Boolean(*value)
186                    }
187                },
188            })
189            .collect(),
190    }
191}
192
193fn gen_fields(writer: &mut ClassFileWriter, fields: &[FieldDecl]) {
194    for field in fields {
195        let descriptor = field.ty.descriptor();
196        let mut fw = writer.visit_field(field.access_flags, &field.name, &descriptor);
197        if let Some(signature) = &field.generic_signature {
198            fw.visit_signature(signature);
199        }
200        fw.visit_end(writer);
201    }
202}
203
204fn gen_method(
205    writer: &mut ClassFileWriter,
206    type_decl: &TypeDecl,
207    method: &MethodDecl,
208    super_name: &str,
209    catalog: &ClassCatalog,
210    lambdas: &LambdaTable,
211) {
212    let descriptor = method.signature.descriptor();
213    let mut mw = writer.visit_method(method.access_flags, &method.name, &descriptor);
214    if let Some(signature) = &method.generic_signature {
215        mw.visit_signature(signature);
216    }
217    for exception in &method.throws {
218        mw.visit_exception(&exception.internal_name());
219    }
220
221    if matches!(type_decl.kind, TypeDeclKind::Enum) && method.name == "values" {
222        gen_enum_values_method(&mut mw, type_decl);
223    } else if matches!(type_decl.kind, TypeDeclKind::Enum) && method.name == "valueOf" {
224        gen_enum_value_of_method(&mut mw, type_decl);
225    } else if method_has_code(method)
226        && let Some(block) = &method.root_block
227    {
228        mw.visit_code();
229        let mut ctx = CodegenCtx::new(writer, type_decl.name, catalog);
230        ctx.set_super_name(ustr::Ustr::from(super_name));
231        ctx.set_fields(&type_decl.fields);
232        ctx.set_methods(&type_decl.methods);
233        ctx.set_anonymous_info(type_decl.anonymous.as_ref());
234        ctx.lambdas = lambdas.clone();
235        ctx.begin_method(method);
236        declare_method_locals(&mut mw, type_decl, method);
237        gen_constructor_prelude(&mut mw, &ctx, method);
238        if method.name == INIT_METHOD {
239            emit_instance_field_initializers(&mut mw, &mut ctx, &type_decl.fields);
240        }
241        crate::bytecode::method_gen::gen_method_body(&mut mw, &mut ctx, &method.body, block);
242        mw.visit_maxs(0, 0);
243    }
244
245    mw.visit_end(writer);
246}
247
248fn gen_static_initializer(
249    writer: &mut ClassFileWriter,
250    type_decl: &TypeDecl,
251    catalog: &ClassCatalog,
252) {
253    if !has_static_field_initializers(&type_decl.fields)
254        && !matches!(type_decl.kind, TypeDeclKind::Enum)
255    {
256        return;
257    }
258
259    let mut mw = writer.visit_method(crate::classfile::ACC_STATIC, CLINIT_METHOD, "()V");
260    mw.visit_code();
261    let mut ctx = CodegenCtx::new(writer, type_decl.name, catalog);
262    ctx.set_fields(&type_decl.fields);
263    ctx.set_methods(&type_decl.methods);
264    ctx.set_anonymous_info(type_decl.anonymous.as_ref());
265    emit_static_field_initializers(&mut mw, &mut ctx, &type_decl.fields);
266    if matches!(type_decl.kind, TypeDeclKind::Enum) {
267        emit_enum_values_initializer(&mut mw, type_decl);
268    }
269    mw.visit_insn(opcodes::RETURN);
270    mw.visit_maxs(0, 0);
271    mw.visit_end(writer);
272}
273
274fn gen_enum_values_method(mw: &mut crate::classfile::MethodWriter, type_decl: &TypeDecl) {
275    mw.visit_code();
276    let array_descriptor = enum_array_descriptor(type_decl);
277    mw.visit_field_insn(
278        opcodes::GETSTATIC,
279        type_decl.name.as_str(),
280        "$VALUES",
281        &array_descriptor,
282    );
283    mw.visit_method_insn(
284        opcodes::INVOKEVIRTUAL,
285        &array_descriptor,
286        "clone",
287        "()Ljava/lang/Object;",
288        false,
289    );
290    mw.visit_type_insn(opcodes::CHECKCAST, &array_descriptor);
291    mw.visit_insn(opcodes::ARETURN);
292    mw.visit_maxs(0, 0);
293}
294
295fn gen_enum_value_of_method(mw: &mut crate::classfile::MethodWriter, type_decl: &TypeDecl) {
296    mw.visit_code();
297    mw.visit_ldc_insn_type(type_decl.name.as_str());
298    mw.visit_var_insn(opcodes::ALOAD, 0);
299    mw.visit_method_insn(
300        opcodes::INVOKESTATIC,
301        "java/lang/Enum",
302        "valueOf",
303        "(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;",
304        false,
305    );
306    mw.visit_type_insn(opcodes::CHECKCAST, type_decl.name.as_str());
307    mw.visit_insn(opcodes::ARETURN);
308    mw.visit_maxs(0, 0);
309}
310
311fn emit_enum_values_initializer(mw: &mut crate::classfile::MethodWriter, type_decl: &TypeDecl) {
312    let constants = enum_constant_fields(type_decl);
313    mw.visit_ldc_insn_int(constants.len() as i32);
314    mw.visit_type_insn(opcodes::ANEWARRAY, type_decl.name.as_str());
315    for (index, field) in constants.iter().enumerate() {
316        mw.visit_insn(opcodes::DUP);
317        mw.visit_ldc_insn_int(index as i32);
318        mw.visit_field_insn(
319            opcodes::GETSTATIC,
320            type_decl.name.as_str(),
321            field.name.as_str(),
322            &field.ty.descriptor(),
323        );
324        mw.visit_insn(opcodes::AASTORE);
325    }
326    mw.visit_field_insn(
327        opcodes::PUTSTATIC,
328        type_decl.name.as_str(),
329        "$VALUES",
330        &enum_array_descriptor(type_decl),
331    );
332}
333
334fn enum_constant_fields(type_decl: &TypeDecl) -> Vec<&FieldDecl> {
335    type_decl
336        .fields
337        .iter()
338        .filter(|field| field.access_flags & crate::classfile::ACC_ENUM != 0)
339        .collect()
340}
341
342fn enum_array_descriptor(type_decl: &TypeDecl) -> String {
343    format!("[L{};", type_decl.name)
344}
345
346fn declare_method_locals(
347    mw: &mut crate::classfile::MethodWriter,
348    type_decl: &TypeDecl,
349    method: &MethodDecl,
350) {
351    let mut slot = 0;
352    if method.access_flags & crate::classfile::ACC_STATIC == 0 {
353        mw.visit_local_variable("this", &Ty::Class(type_decl.name).descriptor(), slot);
354        slot += 1;
355    }
356
357    for param in &method.params {
358        mw.visit_local_variable(param.name.as_str(), &param.ty.erasure().descriptor(), slot);
359        slot += param.ty.size() as u16;
360    }
361}
362
363fn gen_constructor_prelude(
364    mw: &mut crate::classfile::MethodWriter,
365    ctx: &CodegenCtx,
366    method: &MethodDecl,
367) {
368    if method.name != INIT_METHOD {
369        return;
370    }
371
372    if let Some(call) = &method.constructor_call {
373        mw.visit_var_insn(opcodes::ALOAD, 0);
374        let mut slot = 1u16;
375        for (index, param) in method.params.iter().enumerate() {
376            if index >= call.arg_offset && index < call.arg_offset + call.params.len() {
377                mw.visit_var_insn(crate::bytecode::local_var::load_opcode(&param.ty), slot);
378            }
379            slot += param.ty.size() as u16;
380        }
381        let descriptor = format!(
382            "({})V",
383            call.params
384                .iter()
385                .map(|ty| ty.erasure().descriptor())
386                .collect::<String>()
387        );
388        mw.visit_method_insn(
389            opcodes::INVOKESPECIAL,
390            &call.owner.internal_name(),
391            INIT_METHOD,
392            &descriptor,
393            false,
394        );
395        if let Some(outer_this) = &ctx.outer_this {
396            mw.visit_var_insn(opcodes::ALOAD, 0);
397            mw.visit_var_insn(opcodes::ALOAD, 1);
398            mw.visit_field_insn(
399                opcodes::PUTFIELD,
400                ctx.class_name.as_str(),
401                outer_this.field_name.as_str(),
402                &outer_this.ty.descriptor(),
403            );
404        }
405        return;
406    }
407
408    mw.visit_var_insn(opcodes::ALOAD, 0);
409    mw.visit_method_insn(
410        opcodes::INVOKESPECIAL,
411        ctx.super_name.as_str(),
412        INIT_METHOD,
413        "()V",
414        false,
415    );
416}
417
418fn method_has_code(method: &MethodDecl) -> bool {
419    method.access_flags & (crate::classfile::ACC_ABSTRACT | crate::classfile::ACC_NATIVE) == 0
420}
421
422fn class_access_flags(type_decl: &TypeDecl) -> u16 {
423    if matches!(
424        type_decl.kind,
425        TypeDeclKind::Class | TypeDeclKind::Record | TypeDeclKind::Enum
426    ) {
427        type_decl.access_flags | crate::classfile::ACC_SUPER
428    } else {
429        type_decl.access_flags
430    }
431}
432
433fn super_name(type_decl: &TypeDecl) -> String {
434    type_decl
435        .super_class
436        .as_ref()
437        .map(|ty| ty.internal_name())
438        .unwrap_or_else(|| OBJECT_CLASS.to_string())
439}
440
441fn interface_names(type_decl: &TypeDecl) -> Vec<String> {
442    type_decl
443        .interfaces
444        .iter()
445        .map(|ty| ty.internal_name())
446        .collect()
447}
448
449fn needs_default_constructor(type_decl: &TypeDecl) -> bool {
450    matches!(type_decl.kind, TypeDeclKind::Class)
451        && !type_decl
452            .methods
453            .iter()
454            .any(|method| method.name == INIT_METHOD)
455}
456
457fn has_static_field_initializers(fields: &[FieldDecl]) -> bool {
458    fields.iter().any(|field| {
459        field.access_flags & crate::classfile::ACC_STATIC != 0 && field.initializer.is_some()
460    })
461}
462
463fn gen_default_constructor(
464    writer: &mut ClassFileWriter,
465    type_decl: &TypeDecl,
466    super_name: &str,
467    catalog: &ClassCatalog,
468) {
469    let mut mw = writer.visit_method(crate::classfile::ACC_PUBLIC, INIT_METHOD, "()V");
470    mw.visit_code();
471    let mut ctx = CodegenCtx::new(writer, type_decl.name, catalog);
472    ctx.set_super_name(ustr::Ustr::from(super_name));
473    ctx.set_fields(&type_decl.fields);
474    ctx.set_methods(&type_decl.methods);
475    ctx.set_anonymous_info(type_decl.anonymous.as_ref());
476    mw.visit_var_insn(opcodes::ALOAD, 0);
477    mw.visit_method_insn(
478        opcodes::INVOKESPECIAL,
479        super_name,
480        INIT_METHOD,
481        "()V",
482        false,
483    );
484    emit_instance_field_initializers(&mut mw, &mut ctx, &type_decl.fields);
485    mw.visit_insn(opcodes::RETURN);
486    mw.visit_maxs(0, 0);
487    mw.visit_end(writer);
488}
489
490fn emit_instance_field_initializers(
491    mw: &mut crate::classfile::MethodWriter,
492    ctx: &mut CodegenCtx,
493    fields: &[FieldDecl],
494) {
495    for field in fields {
496        if field.access_flags & crate::classfile::ACC_STATIC != 0 {
497            continue;
498        }
499        let Some(initializer) = field.initializer else {
500            continue;
501        };
502
503        mw.visit_var_insn(opcodes::ALOAD, 0);
504        crate::bytecode::expr_gen::gen_expr(mw, ctx, &field.body, initializer);
505        let value_ty = crate::bytecode::expr_gen::expr_ty(ctx, &field.body, initializer);
506        crate::bytecode::expr_gen::coerce(mw, &value_ty, &field.ty);
507        mw.visit_field_insn(
508            opcodes::PUTFIELD,
509            ctx.class_name.as_str(),
510            field.name.as_str(),
511            &field.ty.descriptor(),
512        );
513    }
514}
515
516fn emit_static_field_initializers(
517    mw: &mut crate::classfile::MethodWriter,
518    ctx: &mut CodegenCtx,
519    fields: &[FieldDecl],
520) {
521    for field in fields {
522        if field.access_flags & crate::classfile::ACC_STATIC == 0 {
523            continue;
524        }
525        let Some(initializer) = field.initializer else {
526            continue;
527        };
528
529        crate::bytecode::expr_gen::gen_expr(mw, ctx, &field.body, initializer);
530        let value_ty = crate::bytecode::expr_gen::expr_ty(ctx, &field.body, initializer);
531        crate::bytecode::expr_gen::coerce(mw, &value_ty, &field.ty);
532        mw.visit_field_insn(
533            opcodes::PUTSTATIC,
534            ctx.class_name.as_str(),
535            field.name.as_str(),
536            &field.ty.descriptor(),
537        );
538    }
539}