Boa 0.10.0

Boa is a Javascript lexer, parser and Just-in-Time compiler written in Rust. Currently, it has support for some of the language.
Documentation
use crate::{forward, forward_val, object::PROTOTYPE, value::same_value, Context};

#[test]
fn json_sanity() {
    let mut engine = Context::new();
    assert_eq!(
        forward(&mut engine, r#"JSON.parse('{"aaa":"bbb"}').aaa == 'bbb'"#),
        "true"
    );
    assert_eq!(
        forward(
            &mut engine,
            r#"JSON.stringify({aaa: 'bbb'}) == '{"aaa":"bbb"}'"#
        ),
        "true"
    );
}

#[test]
fn json_stringify_remove_undefined_values_from_objects() {
    let mut engine = Context::new();

    let actual = forward(
        &mut engine,
        r#"JSON.stringify({ aaa: undefined, bbb: 'ccc' })"#,
    );
    let expected = r#""{"bbb":"ccc"}""#;

    assert_eq!(actual, expected);
}

#[test]
fn json_stringify_remove_function_values_from_objects() {
    let mut engine = Context::new();

    let actual = forward(
        &mut engine,
        r#"JSON.stringify({ aaa: () => {}, bbb: 'ccc' })"#,
    );
    let expected = r#""{"bbb":"ccc"}""#;

    assert_eq!(actual, expected);
}

#[test]
fn json_stringify_remove_symbols_from_objects() {
    let mut engine = Context::new();

    let actual = forward(
        &mut engine,
        r#"JSON.stringify({ aaa: Symbol(), bbb: 'ccc' })"#,
    );
    let expected = r#""{"bbb":"ccc"}""#;

    assert_eq!(actual, expected);
}

#[test]
fn json_stringify_replacer_array_strings() {
    let mut engine = Context::new();
    let actual = forward(
        &mut engine,
        r#"JSON.stringify({aaa: 'bbb', bbb: 'ccc', ccc: 'ddd'}, ['aaa', 'bbb'])"#,
    );
    let expected = forward(&mut engine, r#"'{"aaa":"bbb","bbb":"ccc"}'"#);
    assert_eq!(actual, expected);
}

#[test]
fn json_stringify_replacer_array_numbers() {
    let mut engine = Context::new();
    let actual = forward(
        &mut engine,
        r#"JSON.stringify({ 0: 'aaa', 1: 'bbb', 2: 'ccc'}, [1, 2])"#,
    );
    let expected = forward(&mut engine, r#"'{"1":"bbb","2":"ccc"}'"#);
    assert_eq!(actual, expected);
}

#[test]
fn json_stringify_replacer_function() {
    let mut engine = Context::new();
    let actual = forward(
        &mut engine,
        r#"JSON.stringify({ aaa: 1, bbb: 2}, (key, value) => {
            if (key === 'aaa') {
                return undefined;
            }

            return value;
        })"#,
    );
    let expected = forward(&mut engine, r#"'{"bbb":2}'"#);
    assert_eq!(actual, expected);
}

#[test]
fn json_stringify_arrays() {
    let mut engine = Context::new();
    let actual = forward(&mut engine, r#"JSON.stringify(['a', 'b'])"#);
    let expected = forward(&mut engine, r#"'["a","b"]'"#);

    assert_eq!(actual, expected);
}

#[test]
fn json_stringify_object_array() {
    let mut engine = Context::new();
    let actual = forward(&mut engine, r#"JSON.stringify([{a: 'b'}, {b: 'c'}])"#);
    let expected = forward(&mut engine, r#"'[{"a":"b"},{"b":"c"}]'"#);

    assert_eq!(actual, expected);
}

#[test]
fn json_stringify_array_converts_undefined_to_null() {
    let mut engine = Context::new();
    let actual = forward(&mut engine, r#"JSON.stringify([undefined])"#);
    let expected = forward(&mut engine, r#"'[null]'"#);

    assert_eq!(actual, expected);
}

#[test]
fn json_stringify_array_converts_function_to_null() {
    let mut engine = Context::new();
    let actual = forward(&mut engine, r#"JSON.stringify([() => {}])"#);
    let expected = forward(&mut engine, r#"'[null]'"#);

    assert_eq!(actual, expected);
}

#[test]
fn json_stringify_array_converts_symbol_to_null() {
    let mut engine = Context::new();
    let actual = forward(&mut engine, r#"JSON.stringify([Symbol()])"#);
    let expected = forward(&mut engine, r#"'[null]'"#);

    assert_eq!(actual, expected);
}
#[test]
fn json_stringify_function_replacer_propogate_error() {
    let mut engine = Context::new();

    let actual = forward(
        &mut engine,
        r#"
        let thrown = 0;
        try {
            JSON.stringify({x: 1}, (key, value) => { throw 1 })
        } catch (err) {
            thrown = err;
        }
        thrown
        "#,
    );
    let expected = forward(&mut engine, "1");

    assert_eq!(actual, expected);
}

#[test]
fn json_stringify_function() {
    let mut engine = Context::new();

    let actual_function = forward(&mut engine, r#"JSON.stringify(() => {})"#);
    let expected = forward(&mut engine, r#"undefined"#);

    assert_eq!(actual_function, expected);
}

#[test]
fn json_stringify_undefined() {
    let mut engine = Context::new();
    let actual_undefined = forward(&mut engine, r#"JSON.stringify(undefined)"#);
    let expected = forward(&mut engine, r#"undefined"#);

    assert_eq!(actual_undefined, expected);
}

#[test]
fn json_stringify_symbol() {
    let mut engine = Context::new();

    let actual_symbol = forward(&mut engine, r#"JSON.stringify(Symbol())"#);
    let expected = forward(&mut engine, r#"undefined"#);

    assert_eq!(actual_symbol, expected);
}

#[test]
fn json_stringify_no_args() {
    let mut engine = Context::new();

    let actual_no_args = forward(&mut engine, r#"JSON.stringify()"#);
    let expected = forward(&mut engine, r#"undefined"#);

    assert_eq!(actual_no_args, expected);
}

#[test]
fn json_parse_array_with_reviver() {
    let mut engine = Context::new();
    let result = forward_val(
        &mut engine,
        r#"JSON.parse('[1,2,3,4]', function(k, v){
            if (typeof v == 'number') {
                return v * 2;
            } else {
                v
        }})"#,
    )
    .unwrap();
    assert_eq!(
        result.get_field("0").to_number(&mut engine).unwrap() as u8,
        2u8
    );
    assert_eq!(
        result.get_field("1").to_number(&mut engine).unwrap() as u8,
        4u8
    );
    assert_eq!(
        result.get_field("2").to_number(&mut engine).unwrap() as u8,
        6u8
    );
    assert_eq!(
        result.get_field("3").to_number(&mut engine).unwrap() as u8,
        8u8
    );
}

#[test]
fn json_parse_object_with_reviver() {
    let mut engine = Context::new();
    let result = forward(
        &mut engine,
        r#"
        var myObj = new Object();
        myObj.firstname = "boa";
        myObj.lastname = "snake";
        var jsonString = JSON.stringify(myObj);

        function dataReviver(key, value) {
            if (key == 'lastname') {
                return 'interpreter';
            } else {
                return value;
            }
        }

        var jsonObj = JSON.parse(jsonString, dataReviver);

        JSON.stringify(jsonObj);"#,
    );
    assert_eq!(result, r#""{"firstname":"boa","lastname":"interpreter"}""#);
}

#[test]
fn json_parse_sets_prototypes() {
    let mut engine = Context::new();
    let init = r#"
        const jsonString = "{
            \"ob\":{\"ject\":1},
            \"arr\": [0,1]
        }";
        const jsonObj = JSON.parse(jsonString);
    "#;
    eprintln!("{}", forward(&mut engine, init));
    let object_prototype = forward_val(&mut engine, r#"jsonObj.ob"#)
        .unwrap()
        .as_object()
        .unwrap()
        .prototype_instance()
        .clone();
    let array_prototype = forward_val(&mut engine, r#"jsonObj.arr"#)
        .unwrap()
        .as_object()
        .unwrap()
        .prototype_instance()
        .clone();
    let global_object_prototype = engine
        .global_object()
        .get_field("Object")
        .get_field(PROTOTYPE);
    let global_array_prototype = engine
        .global_object()
        .get_field("Array")
        .get_field(PROTOTYPE);
    assert_eq!(
        same_value(&object_prototype, &global_object_prototype),
        true
    );
    assert_eq!(same_value(&array_prototype, &global_array_prototype), true);
}

#[test]
fn json_fields_should_be_enumerable() {
    let mut engine = Context::new();
    let actual_object = forward(
        &mut engine,
        r#"
        var a = JSON.parse('{"x":0}');
        a.propertyIsEnumerable('x');
    "#,
    );
    let actual_array_index = forward(
        &mut engine,
        r#"
        var b = JSON.parse('[0, 1]');
        b.propertyIsEnumerable('0');
        "#,
    );
    let expected = forward(&mut engine, r#"true"#);

    assert_eq!(actual_object, expected);
    assert_eq!(actual_array_index, expected);
}