use crate::context::JITContext;
use crate::ffi::jit_kinds::*;
use crate::ffi::value_ffi::*;
use shape_runtime::context::ExecutionContext;
use shape_value::{HeapKind, NativeKind};
use std::collections::HashMap;
pub mod array;
pub mod duration;
pub mod matrix;
pub mod number;
pub mod object;
pub mod result;
pub mod string;
pub mod time;
pub use array::call_array_method;
pub use duration::call_duration_method;
pub use matrix::call_matrix_method;
pub use number::call_number_method;
pub use object::call_object_method;
pub use result::call_result_method;
pub use string::call_string_method;
pub use time::call_time_method;
unsafe fn receiver_type_name(
receiver_bits: u64,
receiver_kind: NativeKind,
exec_ctx: &ExecutionContext,
) -> Option<String> {
use crate::ffi::typed_object::jit_typed_object_schema_id;
match receiver_kind {
NativeKind::Float64
| NativeKind::NullableFloat64
| NativeKind::Int8
| NativeKind::NullableInt8
| NativeKind::UInt8
| NativeKind::NullableUInt8
| NativeKind::Int16
| NativeKind::NullableInt16
| NativeKind::UInt16
| NativeKind::NullableUInt16
| NativeKind::Int32
| NativeKind::NullableInt32
| NativeKind::UInt32
| NativeKind::NullableUInt32
| NativeKind::Int64
| NativeKind::NullableInt64
| NativeKind::NullableUInt64
| NativeKind::IntSize
| NativeKind::NullableIntSize
| NativeKind::UIntSize
| NativeKind::NullableUIntSize => Some("number".to_string()),
NativeKind::Bool => Some("bool".to_string()),
NativeKind::Null => Some("null".to_string()),
NativeKind::String => Some("string".to_string()),
NativeKind::Float32 => Some("number".to_string()),
NativeKind::Char => Some("char".to_string()),
NativeKind::StringV2 => Some("string".to_string()),
NativeKind::DecimalV2 => Some("decimal".to_string()),
NativeKind::Ptr(HeapKind::String) => Some("string".to_string()),
NativeKind::Ptr(HeapKind::TypedObject) => {
let schema_id = jit_typed_object_schema_id(receiver_bits);
if schema_id == 0 {
return None;
}
let global = shape_runtime::type_schema::lookup_schema_by_id_public(schema_id)
.map(|s| s.name.clone());
if global.is_some() {
return global;
}
let _ = exec_ctx;
super::control::with_trampoline_vm(|vm| {
vm.program()
.type_schema_registry
.get_by_id(schema_id)
.map(|s| s.name.clone())
})
.flatten()
}
NativeKind::Ptr(HeapKind::TypedArray) => Some("Array".to_string()),
NativeKind::Ptr(HeapKind::Decimal) => Some("decimal".to_string()),
NativeKind::Ptr(HeapKind::BigInt) => Some("bigint".to_string()),
NativeKind::Ptr(HeapKind::DataTable) => Some("Table".to_string()),
NativeKind::Ptr(HeapKind::HashMap) => Some("HashMap".to_string()),
NativeKind::Ptr(HeapKind::HashSet) => Some("Set".to_string()),
NativeKind::Ptr(HeapKind::Future) => Some("Future".to_string()),
NativeKind::Ptr(HeapKind::TaskGroup) => Some("TaskGroup".to_string()),
NativeKind::Ptr(HeapKind::Closure) => Some("Closure".to_string()),
NativeKind::Ptr(HeapKind::Temporal) => Some("Temporal".to_string()),
NativeKind::Ptr(HeapKind::TableView) => Some("TableView".to_string()),
NativeKind::Ptr(HeapKind::Content) => Some("Content".to_string()),
NativeKind::Ptr(HeapKind::Instant) => Some("Instant".to_string()),
NativeKind::Ptr(HeapKind::IoHandle) => Some("IoHandle".to_string()),
NativeKind::Ptr(HeapKind::Char) => Some("char".to_string()),
NativeKind::Ptr(HeapKind::Iterator) => Some("Iterator".to_string()),
NativeKind::Ptr(HeapKind::Deque) => Some("Deque".to_string()),
NativeKind::Ptr(HeapKind::Channel) => Some("Channel".to_string()),
NativeKind::Ptr(HeapKind::PriorityQueue) => Some("PriorityQueue".to_string()),
NativeKind::Ptr(HeapKind::Range) => Some("Range".to_string()),
NativeKind::Ptr(HeapKind::Result) => Some("Result".to_string()),
NativeKind::Ptr(HeapKind::Option) => Some("Option".to_string()),
NativeKind::Ptr(HeapKind::TraitObject) => Some("TraitObject".to_string()),
NativeKind::Ptr(HeapKind::Mutex) => Some("Mutex".to_string()),
NativeKind::Ptr(HeapKind::Atomic) => Some("Atomic".to_string()),
NativeKind::Ptr(HeapKind::Lazy) => Some("Lazy".to_string()),
NativeKind::Ptr(HeapKind::ModuleFn) => Some("ModuleFn".to_string()),
NativeKind::Ptr(HeapKind::Matrix) => Some("Matrix".to_string()),
NativeKind::Ptr(HeapKind::MatrixSlice) => Some("Vec<number>".to_string()),
NativeKind::Ptr(HeapKind::FilterExpr)
| NativeKind::Ptr(HeapKind::Reference)
| NativeKind::Ptr(HeapKind::SharedCell)
| NativeKind::Ptr(HeapKind::NativeScalar)
| NativeKind::Ptr(HeapKind::NativeView) => None,
NativeKind::UInt64 => {
if receiver_bits == 0 || receiver_bits == TAG_NULL || receiver_bits == TAG_NONE {
return None;
}
match read_heap_kind(receiver_bits) {
HK_STRING => Some("string".to_string()),
HK_ARRAY => Some("Array".to_string()),
HK_TYPED_OBJECT => {
let schema_id = jit_typed_object_schema_id(receiver_bits);
if schema_id == 0 {
return None;
}
let global = shape_runtime::type_schema::lookup_schema_by_id_public(schema_id)
.map(|s| s.name.clone());
if global.is_some() {
return global;
}
let _ = exec_ctx;
super::control::with_trampoline_vm(|vm| {
vm.program()
.type_schema_registry
.get_by_id(schema_id)
.map(|s| s.name.clone())
})
.flatten()
}
HK_JIT_OBJECT => Some("object".to_string()),
HK_DURATION => Some("Duration".to_string()),
HK_TIME => Some("DateTime".to_string()),
_ => None,
}
}
}
}
unsafe fn find_function_by_name(ctx_ref: &JITContext, ufcs_name: &str) -> Option<usize> {
if ctx_ref.function_names_ptr.is_null() || ctx_ref.function_names_len == 0 {
return None;
}
let names = unsafe {
std::slice::from_raw_parts(ctx_ref.function_names_ptr, ctx_ref.function_names_len)
};
for (idx, name) in names.iter().enumerate() {
if name == ufcs_name {
return Some(idx);
}
}
None
}
unsafe fn try_call_user_method(
ctx: *const JITContext,
receiver_bits: u64,
receiver_kind: NativeKind,
method_name: &str,
arg_pairs: &[(u64, NativeKind)],
) -> Option<u64> {
use crate::ffi::stack_kind_code;
let ctx_ref = unsafe { &*ctx };
if ctx_ref.exec_context_ptr.is_null() {
return None;
}
let exec_ctx = unsafe { &*(ctx_ref.exec_context_ptr as *const ExecutionContext) };
let type_name = unsafe { receiver_type_name(receiver_bits, receiver_kind, exec_ctx) }?;
let ufcs_name = format!("{}::{}", type_name, method_name);
let func_idx = unsafe { find_function_by_name(ctx_ref, &ufcs_name) }?;
if ctx_ref.function_table.is_null() || func_idx >= ctx_ref.function_table_len {
return None;
}
let raw_fn_ptr = unsafe { *(ctx_ref.function_table as *const *const u8).add(func_idx) };
if raw_fn_ptr.is_null() {
return None;
}
let ctx_mut = unsafe { &mut *(ctx as *mut JITContext) };
let _ = stack_kind_code::SENTINEL; ctx_mut.stack_ptr = 0;
let mut native_args: Vec<u64> = Vec::with_capacity(arg_pairs.len() + 1);
native_args.push(receiver_bits);
for &(bits, _kind) in arg_pairs {
native_args.push(bits);
}
let _result_code = unsafe {
crate::ffi::control::call_jit_fn_with_args(raw_fn_ptr, ctx_mut, &native_args)
};
if ctx_mut.stack_ptr > 0 {
ctx_mut.stack_ptr -= 1;
let result = ctx_mut.stack[ctx_mut.stack_ptr];
ctx_mut.stack_kinds[ctx_mut.stack_ptr] = stack_kind_code::SENTINEL;
Some(result)
} else {
Some(TAG_NULL)
}
}
pub extern "C" fn jit_call_method(ctx: *mut JITContext, stack_count: usize) -> u64 {
use crate::ffi::stack_kind_code;
use shape_value::{HeapKind, NativeKind};
unsafe {
if ctx.is_null() || stack_count < 3 {
return TAG_NULL;
}
let ctx_ref = &mut *ctx;
if ctx_ref.stack_ptr == 0 {
return TAG_NULL;
}
ctx_ref.stack_ptr -= 1;
let arg_count = ctx_ref.stack[ctx_ref.stack_ptr] as usize;
ctx_ref.stack_kinds[ctx_ref.stack_ptr] = stack_kind_code::SENTINEL;
if ctx_ref.stack_ptr == 0 {
return TAG_NULL;
}
ctx_ref.stack_ptr -= 1;
let method_bits = ctx_ref.stack[ctx_ref.stack_ptr];
let method_kind_code = ctx_ref.stack_kinds[ctx_ref.stack_ptr];
ctx_ref.stack_kinds[ctx_ref.stack_ptr] = stack_kind_code::SENTINEL;
let method_kind = match stack_kind_code::decode(method_kind_code) {
Some(k) => k,
None => {
tracing::debug!(
target: "shape_jit",
method_kind_code,
stack_ptr = ctx_ref.stack_ptr,
"jit-call-method SURFACE \u{a7}2.7.7 / Q9: method-name \
kind-byte is SENTINEL / reserved. The producing call \
site at terminators.rs:243 must stamp NativeKind::String \
\u{2014} no Bool-default.",
);
return TAG_NULL;
}
};
if !matches!(method_kind, NativeKind::String) {
tracing::debug!(
target: "shape_jit",
method_kind = ?method_kind,
"jit-call-method SURFACE: method-name kind != \
NativeKind::String. Producer-site contract violated \
(terminators.rs:243 must stamp String).",
);
return TAG_NULL;
}
let method_name: String = unbox_string(method_bits).to_string();
tracing::debug!(
target: "shape_jit",
arg_count,
method_name = %method_name,
stack_ptr = ctx_ref.stack_ptr,
"jit-call-method dispatch",
);
let mut arg_pairs: Vec<(u64, NativeKind)> = Vec::with_capacity(arg_count);
for _ in 0..arg_count {
if ctx_ref.stack_ptr == 0 {
return TAG_NULL;
}
ctx_ref.stack_ptr -= 1;
let bits = ctx_ref.stack[ctx_ref.stack_ptr];
let code = ctx_ref.stack_kinds[ctx_ref.stack_ptr];
ctx_ref.stack_kinds[ctx_ref.stack_ptr] = stack_kind_code::SENTINEL;
let kind = match stack_kind_code::decode(code) {
Some(k) => k,
None => {
tracing::debug!(
target: "shape_jit",
code,
stack_ptr = ctx_ref.stack_ptr,
"jit-call-method SURFACE \u{a7}2.7.7 / Q9: arg \
kind-byte is SENTINEL / reserved. The producing \
call site at `mir_compiler/terminators.rs` must \
stamp a concrete NativeKind per ADR-006 \u{a7}2.7.5 \
producer-side classification \u{2014} no Bool-default \
fallback (\u{a7}2.7.7 #9).",
);
return TAG_NULL;
}
};
arg_pairs.push((bits, kind));
}
arg_pairs.reverse();
if ctx_ref.stack_ptr == 0 {
return TAG_NULL;
}
ctx_ref.stack_ptr -= 1;
let receiver_bits = ctx_ref.stack[ctx_ref.stack_ptr];
let receiver_code = ctx_ref.stack_kinds[ctx_ref.stack_ptr];
ctx_ref.stack_kinds[ctx_ref.stack_ptr] = stack_kind_code::SENTINEL;
let receiver_kind = match stack_kind_code::decode(receiver_code) {
Some(k) => k,
None => {
tracing::debug!(
target: "shape_jit",
receiver_code,
stack_ptr = ctx_ref.stack_ptr,
"jit-call-method SURFACE \u{a7}2.7.7 / Q9: receiver \
kind-byte is SENTINEL / reserved. The producing call \
site must stamp the receiver's NativeKind per ADR-006 \
\u{a7}2.7.5. No Bool-default fallback (\u{a7}2.7.7 #9).",
);
return TAG_NULL;
}
};
tracing::debug!(
target: "shape_jit",
method_name = %method_name,
receiver_kind = ?receiver_kind,
receiver_code,
receiver_bits,
"jit-call-method receiver classified",
);
let delegated = match receiver_kind {
NativeKind::Ptr(HeapKind::HashSet)
| NativeKind::Ptr(HeapKind::HashMap)
| NativeKind::Ptr(HeapKind::Deque)
| NativeKind::Ptr(HeapKind::PriorityQueue)
| NativeKind::Ptr(HeapKind::Channel)
| NativeKind::Ptr(HeapKind::Mutex)
| NativeKind::Ptr(HeapKind::Atomic)
| NativeKind::Ptr(HeapKind::Lazy)
| NativeKind::Ptr(HeapKind::Result)
| NativeKind::Ptr(HeapKind::Option)
| NativeKind::Ptr(HeapKind::TypedArray)
| NativeKind::Float64
| NativeKind::NullableFloat64
| NativeKind::Int8
| NativeKind::NullableInt8
| NativeKind::UInt8
| NativeKind::NullableUInt8
| NativeKind::Int16
| NativeKind::NullableInt16
| NativeKind::UInt16
| NativeKind::NullableUInt16
| NativeKind::Int32
| NativeKind::NullableInt32
| NativeKind::UInt32
| NativeKind::NullableUInt32
| NativeKind::Int64
| NativeKind::NullableInt64
| NativeKind::NullableUInt64
| NativeKind::IntSize
| NativeKind::NullableIntSize
| NativeKind::UIntSize
| NativeKind::NullableUIntSize
| NativeKind::Bool
| NativeKind::Float32
| NativeKind::Char => true,
NativeKind::StringV2 | NativeKind::DecimalV2 => true,
NativeKind::String => false,
NativeKind::UInt64 => false,
NativeKind::Ptr(_) => false,
NativeKind::Null => true,
};
if matches!(receiver_kind, NativeKind::Ptr(HeapKind::TypedArray))
&& arg_pairs
.iter()
.any(|(_, k)| matches!(k, NativeKind::Ptr(HeapKind::Closure)))
{
tracing::debug!(
target: "shape_jit",
method_name = %method_name,
"jit-call-method SURFACE: typed-array higher-order method \
with a JIT-format closure arg cannot delegate to the VM \
trampoline (carrier-shape mismatch) \u{2014} raising \
pending_call_error for MIR-emitted deopt to interpreter \
fall-through (W12 pattern)",
);
super::control::set_jit_runtime_error(format!(
"JIT codegen for typed-array `.{}()` with a closure \
argument is unimplemented \u{2014} deopting to interpreter",
method_name,
));
ctx_ref.pending_call_error = 1;
return TAG_NULL;
}
if delegated {
tracing::debug!(
target: "shape_jit",
method_name = %method_name,
receiver_kind = ?receiver_kind,
receiver_bits,
arg_count,
"jit-call-method delegating to VM",
);
let receiver_pair = (receiver_bits, receiver_kind);
let result = super::control::with_trampoline_vm_mut(|vm| {
vm.jit_trampoline_call_method(
&method_name,
receiver_pair,
&arg_pairs,
None,
)
});
match result {
Some(Ok(bits)) => return bits,
Some(Err(e)) => {
tracing::debug!(
target: "shape_jit",
method_name = %method_name,
receiver_kind = ?receiver_kind,
error = ?e,
"jit-call-method VM trampoline returned error \u{2014} \
raising pending_call_error for MIR-emitted deopt",
);
super::control::set_jit_runtime_error(e.to_string());
ctx_ref.pending_call_error = 1;
return TAG_NULL;
}
None => {
tracing::debug!(
target: "shape_jit",
method_name = %method_name,
receiver_kind = ?receiver_kind,
"jit-call-method VM trampoline unavailable \u{2014} \
TRAMPOLINE_VM is null. Surfaces.",
);
super::control::set_jit_runtime_error(format!(
"JIT method dispatch for `{}` could not reach the \
interpreter trampoline",
method_name,
));
ctx_ref.pending_call_error = 1;
return TAG_NULL;
}
}
}
let args: Vec<u64> = arg_pairs.iter().map(|(b, _)| *b).collect();
if is_heap_kind(receiver_bits, HK_ARRAY) {
match method_name.as_str() {
"count" | "group" | "groupBy" => {
tracing::debug!(
target: "shape_jit",
method_name = %method_name,
"jit-call-method SURFACE: array `count`/`group`/\
`groupBy` JIT-format codegen unimplemented \u{2014} \
raising pending_call_error for MIR-emitted deopt \
to interpreter fall-through (W12 pattern)",
);
super::control::set_jit_runtime_error(format!(
"JIT codegen for array `.{}()` is unimplemented \
\u{2014} deopting to interpreter",
method_name,
));
ctx_ref.pending_call_error = 1;
return TAG_NULL;
}
_ => {}
}
match method_name.as_str() {
"find" | "findIndex" | "some" | "every" | "filter" | "map"
| "reduce" => {
if args.is_empty() {
return TAG_NULL;
}
let predicate = args[0];
let working_array_bits = receiver_bits;
if method_name == "reduce" {
let (callback, initial) = if args.len() > 1 {
(args[1], args[0])
} else {
(args[0], box_number(0.0))
};
ctx_ref.stack[ctx_ref.stack_ptr] = working_array_bits;
ctx_ref.stack_ptr += 1;
ctx_ref.stack[ctx_ref.stack_ptr] = callback;
ctx_ref.stack_ptr += 1;
ctx_ref.stack[ctx_ref.stack_ptr] = initial;
ctx_ref.stack_ptr += 1;
ctx_ref.stack[ctx_ref.stack_ptr] = box_number(3.0);
ctx_ref.stack_ptr += 1;
return super::control::jit_control_reduce(ctx);
}
ctx_ref.stack[ctx_ref.stack_ptr] = working_array_bits;
ctx_ref.stack_ptr += 1;
ctx_ref.stack[ctx_ref.stack_ptr] = predicate;
ctx_ref.stack_ptr += 1;
ctx_ref.stack[ctx_ref.stack_ptr] = box_number(2.0);
ctx_ref.stack_ptr += 1;
let result = match method_name.as_str() {
"find" => super::control::jit_control_find(ctx),
"findIndex" => super::control::jit_control_find_index(ctx),
"some" => super::control::jit_control_some(ctx),
"every" => super::control::jit_control_every(ctx),
"filter" => super::control::jit_control_filter(ctx),
"map" => super::control::jit_control_map(ctx),
_ => TAG_NULL,
};
return result;
}
_ => {}
}
}
let builtin_result = match receiver_kind {
NativeKind::String => call_string_method(receiver_bits, &method_name, &args),
NativeKind::Ptr(_) => TAG_NULL,
NativeKind::UInt64 => {
if receiver_bits == 0
|| receiver_bits == TAG_NULL
|| receiver_bits == TAG_NONE
{
TAG_NULL
} else {
match read_heap_kind(receiver_bits) {
HK_OK | HK_ERR => {
call_result_method(receiver_bits, &method_name, &args)
}
HK_ARRAY => call_array_method(receiver_bits, &method_name, &args),
HK_STRING => call_string_method(receiver_bits, &method_name, &args),
HK_JIT_OBJECT => call_object_method(receiver_bits, &method_name, &args),
HK_DURATION => {
call_duration_method(receiver_bits, &method_name, &args)
}
HK_COLUMN_REF => TAG_NULL,
HK_MATRIX => call_matrix_method(receiver_bits, &method_name, &args),
HK_TIME => call_time_method(receiver_bits, &method_name, &args),
_ => TAG_NULL,
}
}
}
NativeKind::Float64
| NativeKind::NullableFloat64
| NativeKind::Int8
| NativeKind::NullableInt8
| NativeKind::UInt8
| NativeKind::NullableUInt8
| NativeKind::Int16
| NativeKind::NullableInt16
| NativeKind::UInt16
| NativeKind::NullableUInt16
| NativeKind::Int32
| NativeKind::NullableInt32
| NativeKind::UInt32
| NativeKind::NullableUInt32
| NativeKind::Int64
| NativeKind::NullableInt64
| NativeKind::NullableUInt64
| NativeKind::IntSize
| NativeKind::NullableIntSize
| NativeKind::UIntSize
| NativeKind::NullableUIntSize
| NativeKind::Bool
| NativeKind::Float32
| NativeKind::Char
| NativeKind::StringV2
| NativeKind::DecimalV2 => TAG_NULL,
NativeKind::Null => TAG_NULL,
};
if builtin_result == TAG_NULL {
if let Some(user_result) = try_call_user_method(
ctx,
receiver_bits,
receiver_kind,
&method_name,
&arg_pairs,
) {
return user_result;
}
}
builtin_result
}
}