use crate::Error::InternalError;
use crate::JavaError::BootstrapMethodError;
use crate::assignable::Assignable;
use crate::call_site_cache::CallSiteKey;
use crate::frame::{ExecutionResult, Frame};
use crate::operand_stack::OperandStack;
use crate::thread::Thread;
use crate::{JavaObject, Result};
use ristretto_classfile::attributes::{Attribute, BootstrapMethod};
use ristretto_classfile::{Constant, ConstantPool, FieldType, JavaStr, ReferenceKind};
use ristretto_classloader::{Class, Method, Reference, Value};
use ristretto_intrinsics::call_method_handle_target;
use std::borrow::Cow;
use std::sync::Arc;
use tracing::debug;
fn get_bootstrap_method_attribute_name_and_type(
constant_pool: &ConstantPool<'_>,
method_index: u16,
) -> Result<(u16, u16)> {
if let Constant::InvokeDynamic {
bootstrap_method_attr_index,
name_and_type_index,
} = constant_pool.try_get(method_index)?
{
Ok((*bootstrap_method_attr_index, *name_and_type_index))
} else {
Err(BootstrapMethodError(format!(
"Invalid constant pool index for invokedynamic: {method_index}"
))
.into())
}
}
fn get_bootstrap_method_attribute(
frame: &Frame,
bootstrap_method_attr_index: u16,
) -> Result<BootstrapMethod> {
let class = frame.class();
class
.class_file()
.attributes
.iter()
.find_map(|attribute| {
if let Attribute::BootstrapMethods { methods, .. } = attribute {
methods.get(bootstrap_method_attr_index as usize).cloned()
} else {
None
}
})
.ok_or_else(|| {
BootstrapMethodError(format!(
"No bootstrap methods found for class {} at index {}",
class.name(),
bootstrap_method_attr_index
))
.into()
})
}
async fn resolve_bootstrap_method<'a>(
thread: &Arc<Thread>,
constant_pool: &'a ConstantPool<'a>,
bootstrap_method_attribute: &BootstrapMethod,
) -> Result<(&'a ReferenceKind, Arc<Class>, Cow<'a, str>, Arc<Method>)> {
let (reference_kind, method_ref) =
constant_pool.try_get_method_handle(bootstrap_method_attribute.bootstrap_method_ref)?;
let (class_index, name_and_type_index) = constant_pool.try_get_method_ref(*method_ref)?;
let bootstrap_class_name = constant_pool.try_get_class(*class_index)?;
let bootstrap_class = thread.class_java_str(bootstrap_class_name).await?;
let (name_index, descriptor_index) =
constant_pool.try_get_name_and_type(*name_and_type_index)?;
let bootstrap_method_name = constant_pool.try_get_utf8(*name_index)?;
let bootstrap_method_descriptor = constant_pool.try_get_utf8(*descriptor_index)?;
let bootstrap_method_name = bootstrap_method_name.to_str_lossy();
let bootstrap_method_descriptor = bootstrap_method_descriptor.to_str_lossy();
let bootstrap_method =
bootstrap_class.try_get_method(&bootstrap_method_name, &bootstrap_method_descriptor)?;
let is_valid_third_arg = bootstrap_method_descriptor.contains("Ljava/lang/invoke/MethodType;")
|| bootstrap_method_descriptor.contains("Ljava/lang/invoke/TypeDescriptor;");
let is_valid_return = bootstrap_method_descriptor.ends_with(")Ljava/lang/invoke/CallSite;")
|| bootstrap_method_descriptor.ends_with(")Ljava/lang/Object;");
if !bootstrap_method_descriptor
.starts_with("(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;")
|| !is_valid_third_arg
|| !is_valid_return
{
return Err(BootstrapMethodError(format!(
"Invalid bootstrap method descriptor: {bootstrap_method_descriptor}"
))
.into());
}
Ok((
reference_kind,
bootstrap_class,
bootstrap_method_descriptor,
bootstrap_method,
))
}
async fn get_method_handles_lookup(thread: &Thread) -> Result<Value> {
let method_handles_class = thread.class("java.lang.invoke.MethodHandles").await?;
let lookup_method = method_handles_class
.try_get_method("lookup", "()Ljava/lang/invoke/MethodHandles$Lookup;")?;
let lookup = thread
.try_execute(&method_handles_class, &lookup_method, &[] as &[Value])
.await?;
Ok(lookup)
}
async fn get_private_lookup(thread: &Thread, caller_class: &Arc<Class>) -> Result<Value> {
let caller_class_object = caller_class.to_object(thread).await?;
let lookup_class = thread
.class("java.lang.invoke.MethodHandles$Lookup")
.await?;
let mut lookup_instance = ristretto_classloader::Object::new(lookup_class.clone())?;
lookup_instance.set_value("lookupClass", caller_class_object.clone())?;
let _ = lookup_instance.set_value("prevLookupClass", Value::Object(None));
lookup_instance.set_value("allowedModes", Value::Int(-1))?;
debug!(
"Created trusted lookup for class '{}' with allowedModes=-1",
caller_class.name()
);
Ok(Value::from_object(
thread.vm()?.garbage_collector(),
lookup_instance,
))
}
async fn get_field_type_class(thread: &Thread, field_type: Option<FieldType>) -> Result<Value> {
let class_name = if let Some(field_type) = field_type {
field_type.class_name()
} else {
"void".to_string()
};
let class_name_java = JavaStr::cow_from_str(&class_name);
let class = thread.load_and_link_class(&class_name_java).await?;
class.to_object(thread).await
}
async fn get_method_type(thread: &Thread, method_descriptor: &str) -> Result<Value> {
let method_descriptor = JavaStr::cow_from_str(method_descriptor);
let (argument_types, return_type) = FieldType::parse_method_descriptor(&method_descriptor)?;
let return_class = get_field_type_class(thread, return_type).await?;
let method_type_class = thread.class("java.lang.invoke.MethodType").await?;
if argument_types.is_empty() {
let method = method_type_class.try_get_method(
"methodType",
"(Ljava/lang/Class;)Ljava/lang/invoke/MethodType;",
)?;
return thread
.try_execute(&method_type_class, &method, &[return_class])
.await;
}
let first_argument = get_field_type_class(thread, argument_types.first().cloned()).await?;
let mut argument_classes: Vec<Value> = Vec::with_capacity(argument_types.len() - 1);
for argument_type in argument_types.iter().skip(1) {
let argument_class = get_field_type_class(thread, Some(argument_type.clone())).await?;
argument_classes.push(argument_class);
}
let class_array = thread.class("[Ljava/lang/Class;").await?;
let reference_array = Reference::try_from((class_array, argument_classes))?;
let arguments = Value::new_object(thread.vm()?.garbage_collector(), reference_array);
let method = method_type_class.try_get_method(
"methodType",
"(Ljava/lang/Class;Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/invoke/MethodType;",
)?;
let arguments = vec![return_class, first_argument, arguments];
thread
.try_execute(&method_type_class, &method, &arguments)
.await
}
#[expect(clippy::too_many_lines)]
pub async fn get_method_handle(
thread: &Arc<Thread>,
constant_pool: &ConstantPool<'_>,
reference_kind: &ReferenceKind,
reference_index: u16,
) -> Result<Value> {
let target = constant_pool.try_get(reference_index)?;
let (class_name, member_name, member_descriptor, is_method) = match target {
Constant::FieldRef {
class_index,
name_and_type_index,
} => {
let class_name = constant_pool.try_get_class(*class_index)?;
let (field_name_index, field_descriptor_index) =
constant_pool.try_get_name_and_type(*name_and_type_index)?;
let field_name = constant_pool.try_get_utf8(*field_name_index)?;
let field_descriptor = constant_pool.try_get_utf8(*field_descriptor_index)?;
(class_name, field_name, field_descriptor, false)
}
Constant::MethodRef {
class_index,
name_and_type_index,
}
| Constant::InterfaceMethodRef {
class_index,
name_and_type_index,
} => {
let class_name = constant_pool.try_get_class(*class_index)?;
let (method_name_index, method_descriptor_index) =
constant_pool.try_get_name_and_type(*name_and_type_index)?;
let method_name = constant_pool.try_get_utf8(*method_name_index)?;
let method_descriptor = constant_pool.try_get_utf8(*method_descriptor_index)?;
(class_name, method_name, method_descriptor, true)
}
Constant::MethodHandle {
reference_kind: nested_reference_kind,
reference_index: nested_reference_index,
} => {
return Box::pin(get_method_handle(
thread,
constant_pool,
nested_reference_kind,
*nested_reference_index,
))
.await;
}
_ => {
return Err(InternalError(format!(
"Unsupported MethodHandle target constant type at index {reference_index}: {target:?}",
)));
}
};
let class = thread.load_and_link_class(class_name).await?;
let class_object = class.to_object(thread).await?;
let lookup_class = thread
.class("java.lang.invoke.MethodHandles$Lookup")
.await?;
let lookup = if let Ok(impl_lookup) = lookup_class.static_value("IMPL_LOOKUP") {
if impl_lookup.is_null() {
let mut trusted_lookup = ristretto_classloader::Object::new(lookup_class.clone())?;
trusted_lookup.set_value("lookupClass", class_object.clone())?;
let _ = trusted_lookup.set_value("prevLookupClass", Value::Object(None));
trusted_lookup.set_value("allowedModes", Value::Int(-1))?;
Value::from_object(thread.vm()?.garbage_collector(), trusted_lookup)
} else {
impl_lookup
}
} else {
let mut trusted_lookup = ristretto_classloader::Object::new(lookup_class.clone())?;
trusted_lookup.set_value("lookupClass", class_object.clone())?;
let _ = trusted_lookup.set_value("prevLookupClass", Value::Object(None));
trusted_lookup.set_value("allowedModes", Value::Int(-1))?;
Value::from_object(thread.vm()?.garbage_collector(), trusted_lookup)
};
let name_value = member_name.to_object(thread).await?;
let method_type = if is_method {
get_method_type(thread, &member_descriptor.to_str_lossy()).await?
} else {
Value::Object(None)
};
let method_handle = match (reference_kind, is_method) {
(ReferenceKind::InvokeStatic, true) => {
let method_handle = lookup_class.try_get_method(
"findStatic",
"(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;",
)?;
thread
.try_execute(
&lookup_class,
&method_handle,
&[lookup.clone(), class_object, name_value, method_type],
)
.await?
}
(ReferenceKind::InvokeVirtual | ReferenceKind::InvokeInterface, true) => {
let method_handle = lookup_class.try_get_method(
"findVirtual",
"(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;",
)?;
thread
.try_execute(
&lookup_class,
&method_handle,
&[lookup.clone(), class_object, name_value, method_type],
)
.await?
}
(ReferenceKind::InvokeSpecial, true) => {
let method_handle = lookup_class.try_get_method(
"findSpecial",
"(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle;",
)?;
thread
.try_execute(
&lookup_class,
&method_handle,
&[
lookup.clone(),
class_object.clone(),
name_value,
method_type.clone(),
class_object,
],
)
.await?
}
(ReferenceKind::NewInvokeSpecial, true) => {
let method_handle = lookup_class.try_get_method(
"findConstructor",
"(Ljava/lang/Class;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;",
)?;
thread
.try_execute(
&lookup_class,
&method_handle,
&[lookup.clone(), class_object, method_type],
)
.await?
}
(ReferenceKind::GetField | ReferenceKind::GetStatic, false) => {
let method_name = if *reference_kind == ReferenceKind::GetField {
"findGetter"
} else {
"findStaticGetter"
};
let method_handle = lookup_class.try_get_method(
method_name,
"(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle;",
)?;
let field_type = FieldType::parse_java_str(member_descriptor)?.class_name();
let field_class = thread.class(field_type).await?;
let field_class_object = field_class.to_object(thread).await?;
thread
.try_execute(
&lookup_class,
&method_handle,
&[lookup.clone(), class_object, name_value, field_class_object],
)
.await?
}
(ReferenceKind::PutField | ReferenceKind::PutStatic, false) => {
let method_name = if *reference_kind == ReferenceKind::PutField {
"findSetter"
} else {
"findStaticSetter"
};
let method_handle = lookup_class.try_get_method(
method_name,
"(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle;",
)?;
let field_type = FieldType::parse_java_str(member_descriptor)?.class_name();
let field_class = thread.class(field_type).await?;
let field_class_object = field_class.to_object(thread).await?;
thread
.try_execute(
&lookup_class,
&method_handle,
&[lookup.clone(), class_object, name_value, field_class_object],
)
.await?
}
_ => {
return Err(InternalError(format!(
"Unsupported method handle reference kind: {reference_kind:?} is_method: {is_method}"
)));
}
};
Ok(method_handle)
}
async fn resolve_static_bootstrap_arguments(
thread: &Arc<Thread>,
constant_pool: &ConstantPool<'_>,
bootstrap_method: &BootstrapMethod,
) -> Result<Vec<Value>> {
let mut arguments = Vec::with_capacity(bootstrap_method.arguments.len());
for argument in &bootstrap_method.arguments {
let constant = constant_pool.try_get(*argument)?;
match constant {
Constant::Integer(value) => {
arguments.push(Value::Int(*value));
}
Constant::Float(value) => {
arguments.push(Value::Float(*value));
}
Constant::Long(value) => {
arguments.push(Value::Long(*value));
}
Constant::Double(value) => {
arguments.push(Value::Double(*value));
}
Constant::String(value) => {
let string = constant_pool.try_get_utf8(*value)?;
let value = string.to_object(thread).await?;
arguments.push(value);
}
Constant::Class(class_index) => {
let class_name = constant_pool.try_get_utf8(*class_index)?;
let class = thread.load_and_link_class(class_name).await?;
let class_object = class.to_object(thread).await?;
arguments.push(class_object);
}
Constant::MethodType(descriptor_index) => {
let descriptor = constant_pool.try_get_utf8(*descriptor_index)?;
let s = descriptor.to_str_lossy();
let method_type = get_method_type(thread, &s).await?;
arguments.push(method_type);
}
Constant::MethodHandle {
reference_kind,
reference_index,
} => {
let method_handle =
get_method_handle(thread, constant_pool, reference_kind, *reference_index)
.await?;
arguments.push(method_handle);
}
_ => {
return Err(BootstrapMethodError(format!(
"Invalid bootstrap argument type at constant pool index {} (tag: {})",
argument,
constant.tag()
))
.into());
}
}
}
Ok(arguments)
}
async fn adjust_varargs_static_arguments(
thread: &Arc<Thread>,
bootstrap_method_descriptor: &str,
mut static_arguments: Vec<Value>,
) -> Result<Vec<Value>> {
debug!(
"adjust_varargs_static_arguments: descriptor={}, static_arguments.len()={}",
bootstrap_method_descriptor,
static_arguments.len()
);
let bootstrap_method_descriptor = JavaStr::cow_from_str(bootstrap_method_descriptor);
let (param_types, _) = FieldType::parse_method_descriptor(&bootstrap_method_descriptor)?;
let static_param_types: Vec<_> = param_types.into_iter().skip(3).collect();
debug!(
"adjust_varargs_static_arguments: static_param_types.len()={}",
static_param_types.len()
);
if static_param_types.is_empty() {
return Ok(static_arguments);
}
if let Some(FieldType::Array(component_type)) = static_param_types.last() {
let fixed_count = static_param_types.len() - 1;
debug!(
"adjust_varargs_static_arguments: varargs detected, fixed_count={}, static_arguments.len()={}",
fixed_count,
static_arguments.len()
);
if static_arguments.len() >= fixed_count {
let varargs: Vec<Value> = static_arguments.drain(fixed_count..).collect();
debug!(
"adjust_varargs_static_arguments: collected {} varargs",
varargs.len()
);
let array_type_name = format!("[{}", component_type.descriptor());
let array_class = thread.class(&array_type_name).await?;
let boxed_varargs = if array_type_name == "[Ljava/lang/Object;" {
let mut boxed = Vec::with_capacity(varargs.len());
for val in varargs {
boxed.push(val.to_object(thread).await?);
}
boxed
} else {
varargs
};
let array_ref = Reference::try_from((array_class, boxed_varargs))?;
static_arguments.push(Value::new_object(
thread.vm()?.garbage_collector(),
array_ref,
));
debug!(
"adjust_varargs_static_arguments: final static_arguments.len()={}",
static_arguments.len()
);
}
}
Ok(static_arguments)
}
async fn resolve_call_site(frame: &Frame, method_index: u16) -> Result<Value> {
let thread = frame.thread()?;
let current_class = frame.class();
let cache_key = CallSiteKey::new(current_class.name().to_string(), method_index);
debug!(
"resolve_call_site: Attempting to resolve call site for class '{}' at CP index {}",
cache_key.class_name, cache_key.instruction_index
);
let vm = thread.vm()?;
let call_site_cache = vm.call_site_cache();
debug!("resolve_call_site: Got VM and cache references successfully");
call_site_cache
.resolve_with_cache(cache_key, || async {
debug!(
"resolve_call_site: Actually resolving (not cached) for class '{}' at CP index {method_index}",
current_class.name(),
);
resolve_call_site_uncached(frame, method_index).await
})
.await
}
async fn resolve_call_site_uncached(frame: &Frame, method_index: u16) -> Result<Value> {
let thread = frame.thread()?;
let current_class = frame.class();
let constant_pool = current_class.constant_pool();
let (bootstrap_method_attr_index, name_and_type_index) =
get_bootstrap_method_attribute_name_and_type(constant_pool, method_index)?;
let (bootstrap_method_attr_name_index, bootstrap_method_attr_descriptor_index) =
constant_pool.try_get_name_and_type(name_and_type_index)?;
let bootstrap_method_attr_name =
constant_pool.try_get_utf8(*bootstrap_method_attr_name_index)?;
let bootstrap_method_attr_descriptor =
constant_pool.try_get_utf8(*bootstrap_method_attr_descriptor_index)?;
let bootstrap_method_attr_descriptor = bootstrap_method_attr_descriptor.to_str_lossy();
let bootstrap_method_attribute =
get_bootstrap_method_attribute(frame, bootstrap_method_attr_index)?;
let (reference_kind, bootstrap_class, _bootstrap_method_descriptor, bootstrap_method) =
resolve_bootstrap_method(&thread, constant_pool, &bootstrap_method_attribute).await?;
debug!(
"invokedynamic: bootstrap method({reference_kind}): {}.{}{}",
bootstrap_class.name(),
bootstrap_method.name(),
bootstrap_method.descriptor()
);
let caller_class = frame.class();
let caller_lookup = get_private_lookup(&thread, caller_class).await?;
if let Ok(lookup_obj) = caller_lookup.as_object_ref()
&& let Ok(modes) = lookup_obj.value("allowedModes")
&& let Ok(modes_int) = modes.as_i32()
{
debug!(
"Bootstrap lookup for '{}': allowedModes={modes_int}",
caller_class.name()
);
}
let method_name = bootstrap_method_attr_name.to_object(&thread).await?;
let method_type = get_method_type(&thread, &bootstrap_method_attr_descriptor).await?;
let static_arguments =
resolve_static_bootstrap_arguments(&thread, constant_pool, &bootstrap_method_attribute)
.await?;
let bootstrap_method_descriptor = bootstrap_method.descriptor();
let static_arguments =
adjust_varargs_static_arguments(&thread, bootstrap_method_descriptor, static_arguments)
.await?;
let mut arguments = vec![caller_lookup, method_name, method_type];
arguments.extend(static_arguments);
let call_site_result = thread
.execute(&bootstrap_class, &bootstrap_method, &arguments)
.await?
.ok_or_else(|| BootstrapMethodError("Bootstrap method returned null".to_string()))?;
validate_call_site(
&thread,
&bootstrap_method_attr_descriptor,
&call_site_result,
)
.await?;
Ok(call_site_result)
}
async fn invoke_bootstrap_method(
thread: &Arc<Thread>,
method_handle: Value,
mut arguments: Vec<Value>,
) -> Result<Value> {
if let Value::Object(None) = method_handle {
return Err(BootstrapMethodError("Bootstrap method handle is null".to_string()).into());
}
let (form, member) = {
let mh_ref = method_handle.as_object_ref()?;
let form = mh_ref.value("form").ok();
let member = mh_ref.value("member").ok();
(form, member)
};
if let Some(ref form_val) = form
&& !form_val.is_null()
{
let vmentry = {
let form_ref = form_val.as_object_ref()?;
form_ref.value("vmentry").ok()
};
if let Some(ref vmentry_val) = vmentry
&& !vmentry_val.is_null()
{
arguments.insert(0, method_handle.clone());
let result = call_method_handle_target(thread.clone(), vmentry_val, arguments).await?;
if let Value::Object(None) = result {
return Err(
BootstrapMethodError("Bootstrap method returned null".to_string()).into(),
);
}
return Ok(result);
}
}
if let Some(ref member_val) = member
&& !member_val.is_null()
{
let result = call_method_handle_target(thread.clone(), member_val, arguments).await?;
if let Value::Object(None) = result {
return Err(BootstrapMethodError("Bootstrap method returned null".to_string()).into());
}
return Ok(result);
}
Err(BootstrapMethodError("MethodHandle has neither vmentry nor member".to_string()).into())
}
async fn validate_call_site(
thread: &Arc<Thread>,
bootstrap_method_descriptor: &str,
call_site: &Value,
) -> Result<()> {
if call_site.is_null() {
return Err(BootstrapMethodError("Bootstrap method returned null".to_string()).into());
} else if !call_site.is_object() {
return Err(
BootstrapMethodError("Bootstrap method did not return an object".to_string()).into(),
);
}
let call_site_class = thread.class("java.lang.invoke.CallSite").await?;
let method_handle_class = thread.class("java.lang.invoke.MethodHandle").await?;
let object_class = {
let object = call_site.as_object_ref()?;
object.class().clone()
};
let is_call_site = call_site_class
.is_assignable_from(thread, &object_class)
.await?;
let is_method_handle = method_handle_class
.is_assignable_from(thread, &object_class)
.await?;
if !is_call_site && !is_method_handle {
return Err(BootstrapMethodError(format!(
"Bootstrap method returned object of type {} which is neither a CallSite nor a MethodHandle",
object_class.name()
))
.into());
}
if is_call_site {
let type_method =
call_site_class.try_get_method("type", "()Ljava/lang/invoke/MethodType;")?;
let call_site_type = thread
.try_execute(
&call_site_class,
&type_method,
std::slice::from_ref(call_site),
)
.await?;
let method_type_class = thread.class("java.lang.invoke.MethodType").await?;
let to_descriptor =
method_type_class.try_get_method("toMethodDescriptorString", "()Ljava/lang/String;")?;
let call_site_descriptor = thread
.try_execute(&method_type_class, &to_descriptor, &[call_site_type])
.await?;
let call_site_descriptor_str = call_site_descriptor.as_string()?;
if call_site_descriptor_str != bootstrap_method_descriptor {
return Err(BootstrapMethodError(format!(
"CallSite type '{call_site_descriptor_str}' does not match expected descriptor '{bootstrap_method_descriptor}'"
))
.into());
}
}
Ok(())
}
#[inline]
pub(crate) async fn invokedynamic(
frame: &Frame,
stack: &mut OperandStack,
method_index: u16,
) -> Result<ExecutionResult> {
let thread = frame.thread()?;
let current_class = frame.class();
debug!(
"invokedynamic: Starting for class '{}' at CP index {}",
current_class.name(),
method_index
);
let call_site_or_method_handle = resolve_call_site(frame, method_index).await?;
let call_site_class = thread.class("java.lang.invoke.CallSite").await?;
let method_handle_class = thread.class("java.lang.invoke.MethodHandle").await?;
let object_class = {
let object = call_site_or_method_handle.as_object_ref()?;
object.class().clone()
};
let target_method_handle = if call_site_class
.is_assignable_from(&thread, &object_class)
.await?
{
let get_target_method =
object_class.try_get_method("getTarget", "()Ljava/lang/invoke/MethodHandle;")?;
thread
.try_execute(
&object_class,
&get_target_method,
&[call_site_or_method_handle],
)
.await?
} else if method_handle_class
.is_assignable_from(&thread, &object_class)
.await?
{
call_site_or_method_handle
} else {
return Err(BootstrapMethodError(format!(
"Bootstrap method returned object of type {} which is neither a CallSite nor a MethodHandle",
object_class.name()
))
.into());
};
let current_class = frame.class();
let constant_pool = current_class.constant_pool();
let (_, name_and_type_index) =
get_bootstrap_method_attribute_name_and_type(constant_pool, method_index)?;
let (_, bootstrap_method_attr_descriptor_index) =
constant_pool.try_get_name_and_type(name_and_type_index)?;
let method_descriptor = constant_pool.try_get_utf8(*bootstrap_method_attr_descriptor_index)?;
let (argument_types, return_type) = FieldType::parse_method_descriptor(method_descriptor)?;
let mut parameters = stack.drain_last(argument_types.len());
let (form, member) = {
let mh_ref = target_method_handle.as_object_ref()?;
let form = mh_ref.value("form").ok();
let member = mh_ref.value("member").ok();
(form, member)
};
let result = if let Some(ref form_val) = form
&& !form_val.is_null()
{
let vmentry = {
let form_ref = form_val.as_object_ref()?;
form_ref.value("vmentry").ok()
};
if let Some(ref vmentry_val) = vmentry
&& !vmentry_val.is_null()
{
parameters.insert(0, target_method_handle.clone());
call_method_handle_target(thread.clone(), vmentry_val, parameters).await?
} else if let Some(ref member_val) = member
&& !member_val.is_null()
{
call_method_handle_target(thread.clone(), member_val, parameters).await?
} else {
return Err(BootstrapMethodError(
"MethodHandle has neither vmentry nor member".to_string(),
)
.into());
}
} else if let Some(ref member_val) = member
&& !member_val.is_null()
{
call_method_handle_target(thread.clone(), member_val, parameters).await?
} else {
return Err(BootstrapMethodError(
"MethodHandle has neither vmentry nor member".to_string(),
)
.into());
};
if let Some(_return_type) = return_type {
stack.push(result)?;
}
Ok(ExecutionResult::Continue)
}