223 lines
9.1 KiB
Rust
223 lines
9.1 KiB
Rust
//! Method call handlers for MIR builder
|
||
//!
|
||
//! This module contains specialized handlers for different types of method calls,
|
||
//! following the Single Responsibility Principle.
|
||
|
||
use crate::ast::ASTNode;
|
||
use crate::mir::builder::builder_calls::CallTarget;
|
||
use crate::mir::builder::calls::function_lowering;
|
||
use crate::mir::builder::{MirBuilder, ValueId};
|
||
use crate::mir::{MirInstruction, MirType, TypeOpKind};
|
||
|
||
/// Me-call 専用のポリシー箱。
|
||
///
|
||
/// - 責務:
|
||
/// - me.method(...) を「インスタンス呼び出し」か「static メソッド呼び出し」か判定する。
|
||
/// - static box 文脈で実体のない receiver を生まないように、静的メソッド降下にフォールバックする。
|
||
struct MeCallPolicyBox;
|
||
|
||
impl MeCallPolicyBox {
|
||
fn resolve_me_call(
|
||
builder: &mut MirBuilder,
|
||
method: &str,
|
||
arguments: &[ASTNode],
|
||
) -> Result<Option<ValueId>, String> {
|
||
// Instance box: prefer enclosing box method (lowered function) if存在
|
||
let enclosing_cls: Option<String> = builder
|
||
.current_function
|
||
.as_ref()
|
||
.and_then(|f| f.signature.name.split('.').next().map(|s| s.to_string()));
|
||
|
||
if let Some(cls) = enclosing_cls.as_ref() {
|
||
let mut arg_values = Vec::with_capacity(arguments.len());
|
||
for a in arguments {
|
||
arg_values.push(builder.build_expression(a.clone())?);
|
||
}
|
||
let arity = arg_values.len();
|
||
let fname = function_lowering::generate_method_function_name(cls, method, arity);
|
||
if let Some(ref module) = builder.current_module {
|
||
if let Some(func) = module.functions.get(&fname) {
|
||
// Decide whether this lowered function expects an implicit receiver.
|
||
// Instance methods: params[0] is Box(box_name)
|
||
// Static methods: params[0] is non-Box or params.is_empty()
|
||
let params = &func.signature.params;
|
||
let is_instance_method =
|
||
!params.is_empty() && matches!(params[0], MirType::Box(_));
|
||
|
||
// Expected argument count from signature (including receiver for instance)
|
||
let expected_params = params.len();
|
||
let provided_static = arg_values.len();
|
||
let provided_instance = arg_values.len() + 1;
|
||
|
||
// Build call_args based on method kind
|
||
let call_args: Vec<ValueId> = if is_instance_method {
|
||
// Instance method: prepend 'me' receiver
|
||
if expected_params != provided_instance {
|
||
if std::env::var("NYASH_ME_CALL_ARITY_STRICT").ok().as_deref()
|
||
== Some("1")
|
||
{
|
||
return Err(format!(
|
||
"[me-call] arity mismatch (instance): {}: declared {} params, got {} args(+me)",
|
||
fname, expected_params, provided_instance
|
||
));
|
||
} else if std::env::var("NYASH_STATIC_CALL_TRACE").ok().as_deref()
|
||
== Some("1")
|
||
{
|
||
eprintln!(
|
||
"[me-call] arity mismatch (instance): {}: declared {} params, got {} args(+me)",
|
||
fname, expected_params, provided_instance
|
||
);
|
||
}
|
||
}
|
||
let me_id = builder.build_me_expression()?;
|
||
let mut v = Vec::with_capacity(provided_instance);
|
||
v.push(me_id);
|
||
v.extend(arg_values.into_iter());
|
||
v
|
||
} else {
|
||
// Static method: no receiver
|
||
if expected_params != provided_static {
|
||
if std::env::var("NYASH_ME_CALL_ARITY_STRICT").ok().as_deref()
|
||
== Some("1")
|
||
{
|
||
return Err(format!(
|
||
"[me-call] arity mismatch (static): {}: declared {} params, got {} args",
|
||
fname, expected_params, provided_static
|
||
));
|
||
} else if std::env::var("NYASH_STATIC_CALL_TRACE").ok().as_deref()
|
||
== Some("1")
|
||
{
|
||
eprintln!(
|
||
"[me-call] arity mismatch (static): {}: declared {} params, got {} args",
|
||
fname, expected_params, provided_static
|
||
);
|
||
}
|
||
}
|
||
arg_values
|
||
};
|
||
|
||
let dst = builder.next_value_id();
|
||
// Emit as unified global call to lowered function
|
||
builder.emit_unified_call(
|
||
Some(dst),
|
||
CallTarget::Global(fname.clone()),
|
||
call_args,
|
||
)?;
|
||
builder.annotate_call_result_from_func_name(dst, &fname);
|
||
return Ok(Some(dst));
|
||
}
|
||
}
|
||
|
||
// Fallback: treat me.method(...) as a static method on the enclosing box.
|
||
// - 旧挙動では me を Box 値として Method 呼び出ししようとしていたが、
|
||
// static box 文脈では実体インスタンスが存在しないため UndefinedValue を生みやすい。
|
||
// - ここでは receiver を持たない Global call に揃え、FuncScannerBox などの
|
||
// static ヘルパー呼び出しを安全に扱う。
|
||
let static_dst = builder.handle_static_method_call(cls, method, arguments)?;
|
||
return Ok(Some(static_dst));
|
||
}
|
||
|
||
Ok(None)
|
||
}
|
||
}
|
||
|
||
impl MirBuilder {
|
||
/// Handle static method calls: BoxName.method(args)
|
||
pub(super) fn handle_static_method_call(
|
||
&mut self,
|
||
box_name: &str,
|
||
method: &str,
|
||
arguments: &[ASTNode],
|
||
) -> Result<ValueId, String> {
|
||
// Build argument values
|
||
let mut arg_values = Vec::new();
|
||
for arg in arguments {
|
||
arg_values.push(self.build_expression(arg.clone())?);
|
||
}
|
||
|
||
// Compose lowered function name: BoxName.method/N
|
||
let func_name = format!("{}.{}/{}", box_name, method, arg_values.len());
|
||
let dst = self.next_value_id();
|
||
|
||
if std::env::var("NYASH_STATIC_CALL_TRACE").ok().as_deref() == Some("1") {
|
||
eprintln!("[builder] static-call {}", func_name);
|
||
}
|
||
|
||
// Emit unified global call to the static-lowered function (module-local)
|
||
self.emit_unified_call(Some(dst), CallTarget::Global(func_name), arg_values)?;
|
||
Ok(dst)
|
||
}
|
||
|
||
/// Handle TypeOp method calls: value.is("Type") and value.as("Type")
|
||
pub(super) fn handle_typeop_method(
|
||
&mut self,
|
||
object_value: ValueId,
|
||
method: &str,
|
||
type_name: &str,
|
||
) -> Result<ValueId, String> {
|
||
let mir_ty = Self::parse_type_name_to_mir(type_name);
|
||
let dst = self.next_value_id();
|
||
let op = if method == "is" {
|
||
TypeOpKind::Check
|
||
} else {
|
||
TypeOpKind::Cast
|
||
};
|
||
|
||
self.emit_instruction(MirInstruction::TypeOp {
|
||
dst,
|
||
op,
|
||
value: object_value,
|
||
ty: mir_ty,
|
||
})?;
|
||
|
||
Ok(dst)
|
||
}
|
||
|
||
/// Check if this is a TypeOp method call
|
||
#[allow(dead_code)]
|
||
pub(super) fn is_typeop_method(method: &str, arguments: &[ASTNode]) -> Option<String> {
|
||
if (method == "is" || method == "as") && arguments.len() == 1 {
|
||
Self::extract_string_literal(&arguments[0])
|
||
} else {
|
||
None
|
||
}
|
||
}
|
||
|
||
/// Handle me.method() calls within static box context
|
||
pub(super) fn handle_me_method_call(
|
||
&mut self,
|
||
method: &str,
|
||
arguments: &[ASTNode],
|
||
) -> Result<Option<ValueId>, String> {
|
||
MeCallPolicyBox::resolve_me_call(self, method, arguments)
|
||
}
|
||
|
||
/// Handle standard Box/Plugin method calls (fallback)
|
||
pub(super) fn handle_standard_method_call(
|
||
&mut self,
|
||
object_value: ValueId,
|
||
method: String,
|
||
arguments: &[ASTNode],
|
||
) -> Result<ValueId, String> {
|
||
// Build argument values
|
||
let mut arg_values = Vec::new();
|
||
for arg in arguments {
|
||
arg_values.push(self.build_expression(arg.clone())?);
|
||
}
|
||
|
||
// Receiver class hintは emit_unified_call 側で起源/型から判断する(重複回避)
|
||
// 統一経路: emit_unified_call に委譲(RouterPolicy と rewrite::* で安定化)
|
||
let dst = self.next_value_id();
|
||
self.emit_unified_call(
|
||
Some(dst),
|
||
CallTarget::Method {
|
||
box_type: None,
|
||
method,
|
||
receiver: object_value,
|
||
},
|
||
arg_values,
|
||
)?;
|
||
Ok(dst)
|
||
}
|
||
}
|