pipa-js 0.1.1

A fast, minimal ES2023 JavaScript runtime built in Rust.
Documentation
#![cfg(feature = "full_runtime_tests")]

use pipa::{JSRuntime, eval};

fn get_int(ctx: &mut pipa::JSContext, code: &str) -> i64 {
    let result = eval(ctx, code).unwrap();
    if result.is_int() {
        result.get_int()
    } else if result.is_float() {
        result.get_float() as i64
    } else {
        panic!("Expected number but got {:?}", result);
    }
}

#[test]
fn test_array_destructure_basic() {
    let mut runtime = JSRuntime::new();
    let mut ctx = runtime.new_context();

    let result = eval(&mut ctx, "var [a, b] = [1, 2]; a + b;");
    assert!(result.is_ok());
    assert_eq!(result.unwrap().get_int(), 3);
}

#[test]
fn test_array_destructure_skip() {
    let mut runtime = JSRuntime::new();
    let mut ctx = runtime.new_context();

    assert_eq!(get_int(&mut ctx, "var [a, , c] = [1, 2, 3]; c;"), 3);
}

#[test]
fn test_array_destructure_rest() {
    let mut runtime = JSRuntime::new();
    let mut ctx = runtime.new_context();

    let result = eval(
        &mut ctx,
        "var [first, ...rest] = [1, 2, 3, 4]; rest.length;",
    );
    assert!(result.is_ok());
    assert_eq!(result.unwrap().get_int(), 3);

    assert_eq!(get_int(&mut ctx, "var [h, ...t] = [10, 20, 30]; t[0];"), 20);
}

#[test]
fn test_object_destructure_basic() {
    let mut runtime = JSRuntime::new();
    let mut ctx = runtime.new_context();

    let result = eval(
        &mut ctx,
        "var obj = {x: 10, y: 20}; var {x, y} = obj; x + y;",
    );
    assert!(result.is_ok());
    assert_eq!(result.unwrap().get_int(), 30);
}

#[test]
fn test_object_destructure_rename() {
    let mut runtime = JSRuntime::new();
    let mut ctx = runtime.new_context();

    let result = eval(
        &mut ctx,
        "var obj = {name: \"Alice\", age: 30}; var {name: n, age: a} = obj; a;",
    );
    assert!(result.is_ok());
    assert_eq!(result.unwrap().get_int(), 30);
}

#[test]
fn test_object_destructure_rename_string() {
    let mut runtime = JSRuntime::new();
    let mut ctx = runtime.new_context();

    let r = eval(&mut ctx, "var p = {name: \"Bob\"}; var {name: n} = p; n;");
    assert!(r.is_ok());
    let val = r.unwrap();
    assert!(val.is_string());
    assert_eq!(ctx.get_atom_str(val.get_atom()), "Bob");
}

#[test]
fn test_rest_params() {
    let mut runtime = JSRuntime::new();
    let mut ctx = runtime.new_context();

    let result = eval(
        &mut ctx,
        "function sum(...args) { var total = 0; var i = 0; while (i < args.length) { total = total + args[i]; i = i + 1; } return total; } sum(1, 2, 3, 4, 5);",
    );
    assert!(result.is_ok());
    assert_eq!(result.unwrap().get_int(), 15);

    let result = eval(
        &mut ctx,
        "function first(a, b, ...rest) { return rest.length; } first(1, 2, 3, 4, 5);",
    );
    assert!(result.is_ok());
    assert_eq!(result.unwrap().get_int(), 3);
}

#[test]
fn test_rest_params_arrow() {
    let mut runtime = JSRuntime::new();
    let mut ctx = runtime.new_context();

    let result = eval(
        &mut ctx,
        "var sum = (...args) => args.reduce((a, b) => a + b, 0); sum(1, 2, 3);",
    );
    assert!(result.is_ok());
    assert_eq!(result.unwrap().get_int(), 6);
}

#[test]
fn test_spread_array_literal() {
    let mut runtime = JSRuntime::new();
    let mut ctx = runtime.new_context();

    let result = eval(&mut ctx, "var a = [1, 2]; var b = [...a, 3, 4]; b.length;");
    assert!(result.is_ok());
    assert_eq!(result.unwrap().get_int(), 4);

    assert_eq!(
        get_int(&mut ctx, "var a = [1, 2]; var b = [...a, 3, 4]; b[0];"),
        1
    );
    assert_eq!(
        get_int(&mut ctx, "var a = [1, 2]; var b = [...a, 3, 4]; b[2];"),
        3
    );

    let result = eval(
        &mut ctx,
        "var x = [1]; var y = [2]; var z = [...x, ...y]; z.length;",
    );
    assert!(result.is_ok());
    assert_eq!(result.unwrap().get_int(), 2);
}

#[test]
fn test_spread_function_call() {
    let mut runtime = JSRuntime::new();
    let mut ctx = runtime.new_context();

    let result = eval(
        &mut ctx,
        "function add(a, b, c) { return a + b + c; } var args = [1, 2, 3]; add(...args);",
    );
    assert!(result.is_ok());
    assert_eq!(result.unwrap().get_int(), 6);

    let result = eval(&mut ctx, "var nums = [3, 1, 4, 1, 5]; Math.max(...nums);");
    assert!(result.is_ok());
    let val = result.unwrap();
    let max_val = if val.is_int() {
        val.get_int()
    } else {
        val.get_float() as i64
    };
    assert_eq!(max_val, 5);
}

#[test]
fn test_spread_object_literal() {
    let mut runtime = JSRuntime::new();
    let mut ctx = runtime.new_context();

    let result = eval(
        &mut ctx,
        "var a = {x: 1, y: 2}; var b = {...a, z: 3}; b.x + b.y + b.z;",
    );
    assert!(result.is_ok());
    assert_eq!(result.unwrap().get_int(), 6);

    let result = eval(
        &mut ctx,
        "var a = {x: 1}; var b = {x: 10, y: 2}; var c = {...a, ...b}; c.x;",
    );
    assert!(result.is_ok());
    assert_eq!(result.unwrap().get_int(), 10);
}

#[test]
fn test_destructure_in_for_of() {
    let mut runtime = JSRuntime::new();
    let mut ctx = runtime.new_context();

    let r = eval(&mut ctx, "var [x, y] = [10, 20]; x + y;");
    assert!(r.is_ok(), "{:?}", r);
    assert_eq!(r.unwrap().get_int(), 30);

    let r2 = eval(
        &mut ctx,
        "var s2 = 0; var pp = [[1,2],[3,4]]; var j2 = 0; var [c, d] = pp[j2]; s2 = s2 + c + d; j2 = 1; var [c, d] = pp[j2]; s2 = s2 + c + d; s2;",
    );
    assert!(r2.is_ok(), "{:?}", r2);
    assert_eq!(r2.unwrap().get_int(), 10);

    let result = eval(
        &mut ctx,
        "var s3 = 0; var qq = [[1,2],[3,4],[5,6]]; for (var j3 = 0; j3 < qq.length; j3++) { var [e, f] = qq[j3]; s3 = s3 + e + f; } s3;",
    );
    assert!(result.is_ok(), "{:?}", result);
    assert_eq!(result.unwrap().get_int(), 21);
}

#[test]
fn test_spread_in_array_copy() {
    let mut runtime = JSRuntime::new();
    let mut ctx = runtime.new_context();

    let result = eval(&mut ctx, "var a = [1,2,3]; var b = [...a]; b.length;");
    assert!(result.is_ok());
    assert_eq!(result.unwrap().get_int(), 3);
}