Skip to main content

shape_jit/ffi/object/
format.rs

1// Heap allocation audit (PR-9 V8 Gap Closure):
2//   Category A (NaN-boxed returns): 1 site
3//     jit_box(HK_STRING, ...) — jit_format result
4//   Category B (intermediate/consumed): 0 sites
5//   Category C (heap islands): 0 sites
6//!
7//! String Formatting Utilities
8//!
9//! Functions for formatting strings with template substitution.
10
11use super::super::super::context::JITContext;
12use super::super::super::nan_boxing::*;
13
14// ============================================================================
15// String Formatting
16// ============================================================================
17
18/// Format a string with arguments
19#[inline(always)]
20pub extern "C" fn jit_format(ctx: *mut JITContext, _arg_count: usize) -> u64 {
21    unsafe {
22        if ctx.is_null() {
23            return TAG_NULL;
24        }
25
26        let ctx_ref = &mut *ctx;
27
28        // Pop arg_count value from stack first
29        if ctx_ref.stack_ptr == 0 {
30            return TAG_NULL;
31        }
32        ctx_ref.stack_ptr -= 1;
33        let arg_count_val = ctx_ref.stack[ctx_ref.stack_ptr];
34        let arg_count = if is_number(arg_count_val) {
35            unbox_number(arg_count_val) as usize
36        } else {
37            return TAG_NULL;
38        };
39
40        if arg_count == 0 {
41            return TAG_NULL;
42        }
43
44        // Pop all arguments from stack
45        let mut args = Vec::with_capacity(arg_count);
46        for _ in 0..arg_count {
47            if ctx_ref.stack_ptr == 0 {
48                return TAG_NULL;
49            }
50            ctx_ref.stack_ptr -= 1;
51            args.push(ctx_ref.stack[ctx_ref.stack_ptr]);
52        }
53        args.reverse(); // Restore original order
54
55        // First arg is the format string
56        let template_bits = args[0];
57        if !is_heap_kind(template_bits, HK_STRING) {
58            return TAG_NULL;
59        }
60        let template = jit_unbox::<String>(template_bits).clone();
61
62        // Substitute placeholders
63        let mut result = template;
64        let format_args = &args[1..];
65
66        // Handle {} placeholders (positional)
67        let mut arg_idx = 0;
68        while let Some(pos) = result.find("{}") {
69            if arg_idx < format_args.len() {
70                let replacement = value_to_string(format_args[arg_idx]);
71                result = format!("{}{}{}", &result[..pos], replacement, &result[pos + 2..]);
72                arg_idx += 1;
73            } else {
74                break;
75            }
76        }
77
78        // Handle {0}, {1}, etc. (indexed placeholders)
79        for i in 0..format_args.len() {
80            let placeholder = format!("{{{}}}", i);
81            if result.contains(&placeholder) {
82                let replacement = value_to_string(format_args[i]);
83                result = result.replace(&placeholder, &replacement);
84            }
85        }
86
87        jit_box(HK_STRING, result)
88    }
89}
90
91/// Helper to convert a value to string for format
92pub(crate) fn value_to_string(bits: u64) -> String {
93    if is_number(bits) {
94        let n = unbox_number(bits);
95        if n.fract() == 0.0 && n.abs() < 1e15 {
96            format!("{}", n as i64)
97        } else {
98            format!("{}", n)
99        }
100    } else if bits == TAG_NULL {
101        "null".to_string()
102    } else if bits == TAG_BOOL_TRUE {
103        "true".to_string()
104    } else if bits == TAG_BOOL_FALSE {
105        "false".to_string()
106    } else {
107        match heap_kind(bits) {
108            Some(HK_STRING) => {
109                let s = unsafe { jit_unbox::<String>(bits) };
110                s.clone()
111            }
112            Some(HK_ARRAY) => "[array]".to_string(),
113            Some(HK_JIT_OBJECT) | Some(HK_TYPED_OBJECT) => "[object]".to_string(),
114            _ => "[unknown]".to_string(),
115        }
116    }
117}