obfuskator/crates/noxis-classfile/tests/roundtrip/attrs.rs

146 lines
5.2 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! Attribute round trips: bootstrap methods, nesting, flags, sealing.
#![allow(clippy::expect_used, clippy::unwrap_used)]
use super::round_trip;
use ristretto_classfile::attributes::{
Attribute as RtAttr, BootstrapMethod as RtBsm, Instruction as Rt,
};
use ristretto_classfile::{
ClassAccessFlags, ClassFile as RtClass, ConstantPool, JAVA_21, Method as RtMethod,
MethodAccessFlags, ReferenceKind,
};
/// `BootstrapMethods` + `invokedynamic` survive the round trip.
#[test]
fn bootstrap_methods_round_trip() {
let mut pool = ConstantPool::default();
let this = pool.add_class("demo/Lambda").expect("add_class");
let object = pool.add_class("java/lang/Object").expect("add_object");
let owner = pool.add_class("demo/RT").expect("owner");
let method_ref = pool.add_method_ref(owner, "boot", "()V").expect("ref");
let handle = pool
.add_method_handle(ReferenceKind::InvokeStatic, method_ref)
.expect("handle");
let arg = pool.add_string("x").expect("arg");
let bsm_name = pool.add_utf8("BootstrapMethods").expect("attr");
let name = pool.add_utf8("run").expect("name");
let desc = pool.add_utf8("()V").expect("desc");
let code_name = pool.add_utf8("Code").expect("attr");
let dynamic = pool.add_invoke_dynamic(0, "run", "()V").expect("indy");
let rt = RtClass {
version: JAVA_21,
constant_pool: pool,
access_flags: ClassAccessFlags::PUBLIC,
this_class: this,
super_class: object,
interfaces: vec![],
fields: vec![],
methods: vec![RtMethod {
access_flags: MethodAccessFlags::PUBLIC | MethodAccessFlags::STATIC,
name_index: name,
descriptor_index: desc,
attributes: vec![RtAttr::Code {
name_index: code_name,
max_stack: 1,
max_locals: 0,
code: vec![Rt::Invokedynamic(dynamic), Rt::Return],
exception_table: vec![],
attributes: vec![],
}],
}],
attributes: vec![RtAttr::BootstrapMethods {
name_index: bsm_name,
methods: vec![RtBsm {
bootstrap_method_ref: handle,
arguments: vec![arg],
}],
}],
..RtClass::default()
};
let ir = round_trip(&rt);
assert_eq!(ir.attributes.len(), 1);
assert!(format!("{:?}", ir.attributes[0]).contains("BootstrapMethods"));
}
/// `NestMembers` переживает пересборку пула (баг: `ClassFormatError`).
#[test]
fn nest_attributes_round_trip() {
let mut pool = ConstantPool::default();
let this = pool.add_class("demo/Nest").expect("c");
let object = pool.add_class("java/lang/Object").expect("o");
let member = pool.add_class("demo/Nest$Inner").expect("m");
let mn = pool.add_utf8("NestMembers").expect("n");
let mems = RtAttr::NestMembers {
name_index: mn,
class_indexes: vec![this, member],
};
let rt = RtClass {
version: JAVA_21,
constant_pool: pool,
this_class: this,
super_class: object,
attributes: vec![mems],
..RtClass::default()
};
let ir = round_trip(&rt);
assert!(format!("{:?}", ir.attributes).contains("NestMembers"));
}
/// `parse → write → parse` даёт тот же IR; записанное верифицируется.
#[test]
fn bare_interface_flags_normalize_on_parse() {
use ristretto_classfile::ClassAccessFlags;
let mut pool = ConstantPool::default();
let this = pool.add_class("demo/Bare").expect("add_class");
let object = pool.add_class("java/lang/Object").expect("add_object");
let rt = RtClass {
version: JAVA_21,
constant_pool: pool,
access_flags: ClassAccessFlags::INTERFACE,
this_class: this,
super_class: object,
interfaces: vec![],
fields: vec![],
methods: vec![],
attributes: vec![],
..RtClass::default()
};
let ir = round_trip(&rt);
assert_eq!(ir.access_flags & 0x0200, 0x0200, "still an interface");
assert_eq!(
ir.access_flags & 0x0400,
0x0400,
"abstract is implied for interfaces"
);
}
/// `PermittedSubclasses` survives the round trip: its pool indices are
/// remapped on write (not copied verbatim), or the backend rejects the
/// rebuilt pool (`bad constant type`).
#[test]
fn permitted_subclasses_round_trip() {
let mut pool = ConstantPool::default();
let this = pool.add_class("demo/Sealed").expect("add_class");
let object = pool.add_class("java/lang/Object").expect("add_object");
let permitted = pool.add_class("demo/Impl").expect("add permitted");
let attr_name = pool.add_utf8("PermittedSubclasses").expect("attr");
let rt = RtClass {
version: JAVA_21,
constant_pool: pool,
access_flags: ClassAccessFlags::PUBLIC | ClassAccessFlags::ABSTRACT,
this_class: this,
super_class: object,
interfaces: vec![],
fields: vec![],
methods: vec![],
attributes: vec![RtAttr::PermittedSubclasses {
name_index: attr_name,
class_indexes: vec![permitted],
}],
..RtClass::default()
};
let ir = round_trip(&rt);
assert_eq!(ir.this_class, "demo/Sealed");
}