///|
pub struct MethodSignature {
parameters : Array[String]
return_type : String
parameter_slots : Int
}
///|
fn descriptor_type(
chars : Array[Char],
start : Int,
allow_void : Bool,
) -> (String, Int, Int) raise ParseError {
let mut pos = start
let mut dimensions = 0
while pos < chars.length() && chars[pos] == '[' {
dimensions += 1
pos += 1
if dimensions > 255 {
raise Invalid(pos, "array descriptor exceeds 255 dimensions")
}
}
if pos >= chars.length() {
raise Invalid(pos, "missing descriptor component type")
}
let tag = chars[pos]
pos += 1
let (name, slots) = match tag {
'Z' => ("boolean", 1)
'B' => ("byte", 1)
'C' => ("char", 1)
'S' => ("short", 1)
'I' => ("int", 1)
'J' => ("long", 2)
'F' => ("float", 1)
'D' => ("double", 2)
'V' if allow_void && dimensions == 0 => ("void", 0)
'L' => {
let begin = pos
while pos < chars.length() && chars[pos] != ';' {
if ['[', '.', '(', ')', ':', ' ', '\u0000'].contains(chars[pos]) {
raise Invalid(pos, "invalid character in class descriptor")
}
pos += 1
}
if pos >= chars.length() || pos == begin {
raise Invalid(pos, "unterminated or empty object descriptor")
}
let name = String::from_array(chars[begin:pos]).replace_all(
old="/",
new=".",
)
pos += 1
(name, 1)
}
_ => raise Invalid(pos - 1, "invalid descriptor type")
}
(name + "[]".repeat(dimensions), pos, if dimensions > 0 { 1 } else { slots })
}
///|
/// Formats a field descriptor; void and trailing garbage are invalid.
pub fn field_descriptor(text : String) -> String raise ParseError {
let chars = text.to_array()
let (name, end, _) = descriptor_type(chars, 0, false)
if end != chars.length() {
raise Invalid(end, "trailing field descriptor data")
}
name
}
///|
/// Parses JVM method descriptors, excluding generic Signature attributes.
pub fn method_descriptor(text : String) -> MethodSignature raise ParseError {
let chars = text.to_array()
if chars.is_empty() || chars[0] != '(' {
raise Invalid(0, "expected method descriptor")
}
let parameters = []
let mut slots = 0
let mut pos = 1
while pos < chars.length() && chars[pos] != ')' {
let (name, end, width) = descriptor_type(chars, pos, false)
slots += width
if slots > 255 {
raise Invalid(pos, "method parameter slots exceed 255")
}
parameters.push(name)
pos = end
}
if pos >= chars.length() {
raise Invalid(pos, "missing method close parenthesis")
}
let (return_type, end, _) = descriptor_type(chars, pos + 1, true)
if end != chars.length() {
raise Invalid(end, "trailing method descriptor data")
}
{ parameters, return_type, parameter_slots: slots, }
}
///|
pub fn MethodSignature::display(self : MethodSignature) -> String {
"(" + self.parameters.join(", ") + ") -> " + self.return_type
}
///|
pub fn ResolvedFrame::readable_signature(
self : ResolvedFrame,
) -> String raise ParseError {
self.method_name + method_descriptor(self.signature).display()
}