quickjs-oxide 0.4.0

A safe Rust JavaScript engine targeting semantic parity with QuickJS
Documentation
use super::*;

#[test]
fn for_in_of_array_bindings_use_nested_iterator_records() {
    let for_of = compile_unlinked_script("for(const [a,,b,] of [[1,2,3]])a+b").unwrap();
    assert_eq!(
        for_of
            .code()
            .iter()
            .filter(|instruction| matches!(instruction, Instruction::ForOfStart))
            .count(),
        2
    );
    assert_eq!(
        for_of
            .code()
            .iter()
            .filter(|instruction| matches!(instruction, Instruction::ForOfNext(0)))
            .count(),
        4
    );
    assert_eq!(
        for_of
            .code()
            .iter()
            .filter(|instruction| matches!(instruction, Instruction::IteratorClose))
            .count(),
        2
    );
    assert_eq!(
        for_of
            .code()
            .iter()
            .filter(|instruction| matches!(instruction, Instruction::InitializeLocal(_)))
            .count(),
        2
    );

    let for_in = compile_unlinked_script("for(var [a,b] in {ab:1})a+b").unwrap();
    assert_eq!(
        for_in
            .code()
            .iter()
            .filter(|instruction| matches!(instruction, Instruction::ForInStart))
            .count(),
        1
    );
    assert_eq!(
        for_in
            .code()
            .iter()
            .filter(|instruction| matches!(instruction, Instruction::ForOfStart))
            .count(),
        1
    );
    assert_eq!(
        for_in
            .code()
            .iter()
            .filter(|instruction| matches!(instruction, Instruction::ForOfNext(1)))
            .count(),
        2
    );

    for source in [
        "for(var [a]=[1] in {a:1})a",
        "for(let [a]=[1] in {a:1})a",
        "for(const [a]=[1] of [[1]])a",
    ] {
        let error = compile_unlinked_script(source).unwrap_err();
        assert_eq!(error.kind(), ErrorKind::Syntax, "{source}");
        assert_eq!(
            error.message(),
            format!(
                "a declaration in the head of a for-{} loop can't have an initializer",
                if source.contains(" of ") { "of" } else { "in" }
            ),
            "{source}"
        );
    }
}

#[test]
fn object_binding_declarations_cover_direct_and_loop_surfaces() {
    for (source, expected) in [
        (
            "(function(){var {fixed}={fixed:'v'};let {['computed']:computed}={computed:'l'};const {nested:{value}}={nested:{value:'c'}};return fixed+computed+value})()",
            "vlc",
        ),
        (
            "(function(){var result='';for(var {fixed}={fixed:'v'};;){result+=fixed;break}for(let {['computed']:computed}={computed:'l'};;){result+=computed;break}for(const {nested:{value}}={nested:{value:'c'}};;){result+=value;break}return result})()",
            "vlc",
        ),
        (
            "(function(){var result='';for(var {0:first} in {ab:1})result+=first;for(let {[1]:second} in {ab:1})result+=second;for(const {constructor:{name}} in {ab:1})result+=name;return result})()",
            "abString",
        ),
        (
            "(function(){var result='';for(var {fixed} of [{fixed:'v'}])result+=fixed;for(let {['computed']:computed} of [{computed:'l'}])result+=computed;for(const {nested:{value}} of [{nested:{value:'c'}}])result+=value;return result})()",
            "vlc",
        ),
    ] {
        assert_eq!(
            evaluate_in_context(source),
            Value::String(JsString::from_static(expected)),
            "{source}"
        );
    }
}