feat: IS_PRIMOP
This commit is contained in:
@@ -23,14 +23,17 @@ pub fn run(script: &str) -> Result<Value> {
|
||||
struct RuntimeContext<'a, 'b> {
|
||||
scope: &'a v8::PinnedRef<'a, v8::HandleScope<'b>>,
|
||||
is_thunk_symbol: Option<v8::Local<'a, v8::Symbol>>,
|
||||
is_primop_symbol: Option<v8::Local<'a, v8::Symbol>>,
|
||||
}
|
||||
|
||||
impl<'a, 'b> RuntimeContext<'a, 'b> {
|
||||
fn new(scope: &'a v8::PinnedRef<'a, v8::HandleScope<'b>>) -> Self {
|
||||
let is_thunk_symbol = Self::get_is_thunk_symbol(scope);
|
||||
let is_primop_symbol = Self::get_is_primop_symbol(scope);
|
||||
Self {
|
||||
scope,
|
||||
is_thunk_symbol,
|
||||
is_primop_symbol,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +53,23 @@ impl<'a, 'b> RuntimeContext<'a, 'b> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn get_is_primop_symbol(
|
||||
scope: &v8::PinnedRef<'a, v8::HandleScope<'b>>,
|
||||
) -> Option<v8::Local<'a, v8::Symbol>> {
|
||||
let global = scope.get_current_context().global(scope);
|
||||
let nix_key = v8::String::new(scope, "Nix")?;
|
||||
let nix_obj = global.get(scope, nix_key.into())?.to_object(scope)?;
|
||||
|
||||
let is_primop_sym_key = v8::String::new(scope, "IS_PRIMOP")?;
|
||||
let is_primop_sym = nix_obj.get(scope, is_primop_sym_key.into())?;
|
||||
|
||||
if is_primop_sym.is_symbol() {
|
||||
is_primop_sym.try_cast().ok()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn run_impl(script: &str, isolate: &mut v8::Isolate) -> Result<Value> {
|
||||
@@ -127,8 +147,11 @@ fn to_value<'a, 'b>(val: v8::Local<'a, v8::Value>, ctx: &RuntimeContext<'a, 'b>)
|
||||
_ if val.is_number() => {
|
||||
let val = val.to_number(scope).unwrap().value();
|
||||
// Heuristic: convert whole numbers to Int (for backward compatibility and JS interop)
|
||||
if val.is_finite() && val.fract() == 0.0
|
||||
&& val >= i64::MIN as f64 && val <= i64::MAX as f64 {
|
||||
if val.is_finite()
|
||||
&& val.fract() == 0.0
|
||||
&& val >= i64::MIN as f64
|
||||
&& val <= i64::MAX as f64
|
||||
{
|
||||
Value::Const(Const::Int(val as i64))
|
||||
} else {
|
||||
Value::Const(Const::Float(val))
|
||||
@@ -152,7 +175,15 @@ fn to_value<'a, 'b>(val: v8::Local<'a, v8::Value>, ctx: &RuntimeContext<'a, 'b>)
|
||||
.collect();
|
||||
Value::List(List::new(list))
|
||||
}
|
||||
_ if val.is_function() => Value::Func,
|
||||
_ if val.is_function() => {
|
||||
if let Some(name) = primop_app_name(val, ctx) {
|
||||
Value::PrimOpApp(name)
|
||||
} else if let Some(name) = primop_name(val, ctx) {
|
||||
Value::PrimOp(name)
|
||||
} else {
|
||||
Value::Func
|
||||
}
|
||||
}
|
||||
_ if val.is_object() => {
|
||||
if is_thunk(val, ctx) {
|
||||
return Value::Thunk;
|
||||
@@ -193,6 +224,58 @@ fn is_thunk<'a, 'b>(val: v8::Local<'a, v8::Value>, ctx: &RuntimeContext<'a, 'b>)
|
||||
matches!(obj.get(scope, is_thunk_sym.into()), Some(v) if v.is_true())
|
||||
}
|
||||
|
||||
/// Check if a function is a primop
|
||||
fn primop_name<'a, 'b>(
|
||||
val: v8::Local<'a, v8::Value>,
|
||||
ctx: &RuntimeContext<'a, 'b>,
|
||||
) -> Option<String> {
|
||||
if !val.is_function() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Use cached IS_PRIMOP symbol from context
|
||||
let is_primop_sym = ctx.is_primop_symbol?;
|
||||
|
||||
let scope = ctx.scope;
|
||||
let obj = val.to_object(scope).unwrap();
|
||||
|
||||
if let Some(metadata) = obj.get(scope, is_primop_sym.into())
|
||||
&& let Some(metadata_obj) = metadata.to_object(scope)
|
||||
&& let Some(name_key) = v8::String::new(scope, "name")
|
||||
&& let Some(name_val) = metadata_obj.get(scope, name_key.into())
|
||||
{
|
||||
Some(name_val.to_rust_string_lossy(scope))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if a primop is partially applied (has applied > 0)
|
||||
fn primop_app_name<'a, 'b>(
|
||||
val: v8::Local<'a, v8::Value>,
|
||||
ctx: &RuntimeContext<'a, 'b>,
|
||||
) -> Option<String> {
|
||||
let name = primop_name(val, ctx)?;
|
||||
|
||||
// Get cached IS_PRIMOP symbol
|
||||
let is_primop_sym = ctx.is_primop_symbol?;
|
||||
|
||||
let scope = ctx.scope;
|
||||
let obj = val.to_object(scope).unwrap();
|
||||
|
||||
if let Some(metadata) = obj.get(scope, is_primop_sym.into())
|
||||
&& let Some(metadata_obj) = metadata.to_object(scope)
|
||||
&& let Some(applied_key) = v8::String::new(scope, "applied")
|
||||
&& let Some(applied_val) = metadata_obj.get(scope, applied_key.into())
|
||||
&& let Some(applied_num) = applied_val.to_number(scope)
|
||||
&& applied_num.value() > 0.0
|
||||
{
|
||||
Some(name)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_value_working() {
|
||||
assert_eq!(
|
||||
|
||||
Reference in New Issue
Block a user