280 lines
7.2 KiB
Rust
280 lines
7.2 KiB
Rust
use oxide_compiler::compiler::Compiler;
|
|
use oxide_types::value::JsValue;
|
|
use oxide_vm::vm::Vm;
|
|
|
|
fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
|
|
let allocator = oxide_parser::Allocator::default();
|
|
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
|
|
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
|
|
vm.run(&module)
|
|
}
|
|
|
|
fn string_value(vm: &Vm, val: JsValue) -> String {
|
|
vm.kernel_core()
|
|
.string_forge()
|
|
.lookup(val.as_string_index())
|
|
.unwrap_or_default()
|
|
}
|
|
|
|
// -- JSON.parse --
|
|
|
|
#[test]
|
|
fn json_parse_object() {
|
|
let mut vm = Vm::new();
|
|
let result = eval(&mut vm, "JSON.parse('{\"a\":1,\"b\":2}')").unwrap();
|
|
assert!(result.is_object());
|
|
let obj = unsafe { &*result.as_js_object_ptr() };
|
|
assert_eq!(obj.prop_count(), 2);
|
|
}
|
|
|
|
#[test]
|
|
fn json_parse_array() {
|
|
let mut vm = Vm::new();
|
|
let result = eval(&mut vm, "JSON.parse('[1,2,3]')").unwrap();
|
|
assert!(result.is_object());
|
|
let obj = unsafe { &*result.as_js_object_ptr() };
|
|
assert!(obj.is_array());
|
|
assert_eq!(obj.prop_count(), 3);
|
|
assert_eq!(obj.get_prop_at(2).as_double(), 3.0);
|
|
}
|
|
|
|
#[test]
|
|
fn json_parse_string() {
|
|
let mut vm = Vm::new();
|
|
let result = eval(&mut vm, "JSON.parse('\"hello\"')").unwrap();
|
|
assert!(result.is_string());
|
|
let s = string_value(&vm, result);
|
|
assert_eq!(s, "hello");
|
|
}
|
|
|
|
#[test]
|
|
fn json_parse_number() {
|
|
let mut vm = Vm::new();
|
|
let result = eval(&mut vm, "JSON.parse('42')").unwrap();
|
|
assert!(result.is_double());
|
|
}
|
|
|
|
#[test]
|
|
fn json_parse_float() {
|
|
let mut vm = Vm::new();
|
|
let result = eval(&mut vm, "JSON.parse('3.14')").unwrap();
|
|
assert!(result.is_double());
|
|
}
|
|
|
|
#[test]
|
|
fn json_parse_true() {
|
|
let mut vm = Vm::new();
|
|
let result = eval(&mut vm, "JSON.parse('true')").unwrap();
|
|
assert!(result.is_bool());
|
|
assert!(result.as_bool());
|
|
}
|
|
|
|
#[test]
|
|
fn json_parse_false() {
|
|
let mut vm = Vm::new();
|
|
let result = eval(&mut vm, "JSON.parse('false')").unwrap();
|
|
assert!(result.is_bool());
|
|
assert!(!result.as_bool());
|
|
}
|
|
|
|
#[test]
|
|
fn json_parse_null() {
|
|
let mut vm = Vm::new();
|
|
let result = eval(&mut vm, "JSON.parse('null')").unwrap();
|
|
assert!(result.is_null());
|
|
}
|
|
|
|
#[test]
|
|
fn json_parse_nested() {
|
|
let mut vm = Vm::new();
|
|
let result = eval(&mut vm, "JSON.parse('{\"a\":1,\"b\":[1,2],\"c\":{\"d\":\"hello\"}}')").unwrap();
|
|
assert!(result.is_object());
|
|
}
|
|
|
|
#[test]
|
|
fn json_parse_invalid_throws() {
|
|
let mut vm = Vm::new();
|
|
let err = eval(&mut vm, "JSON.parse('not json')");
|
|
assert!(err.is_err());
|
|
}
|
|
|
|
// -- JSON.stringify --
|
|
|
|
#[test]
|
|
fn json_stringify_object() {
|
|
let mut vm = Vm::new();
|
|
let result = eval(&mut vm, "JSON.stringify({a:1})").unwrap();
|
|
assert!(result.is_string());
|
|
let s = string_value(&vm, result);
|
|
assert_eq!(s, "{\"a\":1}");
|
|
}
|
|
|
|
#[test]
|
|
fn json_stringify_array() {
|
|
let mut vm = Vm::new();
|
|
let result = eval(&mut vm, "JSON.stringify([1,2,3])").unwrap();
|
|
assert!(result.is_string());
|
|
let s = string_value(&vm, result);
|
|
assert_eq!(s, "[1,2,3]");
|
|
}
|
|
|
|
#[test]
|
|
fn json_stringify_string() {
|
|
let mut vm = Vm::new();
|
|
let result = eval(&mut vm, "JSON.stringify('hello')").unwrap();
|
|
assert!(result.is_string());
|
|
let s = string_value(&vm, result);
|
|
assert_eq!(s, "\"hello\"");
|
|
}
|
|
|
|
#[test]
|
|
fn json_stringify_number() {
|
|
let mut vm = Vm::new();
|
|
let result = eval(&mut vm, "JSON.stringify(42)").unwrap();
|
|
assert!(result.is_string());
|
|
let s = string_value(&vm, result);
|
|
assert_eq!(s, "42");
|
|
}
|
|
|
|
#[test]
|
|
fn json_stringify_float() {
|
|
let mut vm = Vm::new();
|
|
let result = eval(&mut vm, "JSON.stringify(3.14)").unwrap();
|
|
assert!(result.is_string());
|
|
let s = string_value(&vm, result);
|
|
assert!(s.starts_with("3.14"));
|
|
}
|
|
|
|
#[test]
|
|
fn json_stringify_true() {
|
|
let mut vm = Vm::new();
|
|
let result = eval(&mut vm, "JSON.stringify(true)").unwrap();
|
|
assert!(result.is_string());
|
|
let s = string_value(&vm, result);
|
|
assert_eq!(s, "true");
|
|
}
|
|
|
|
#[test]
|
|
fn json_stringify_false() {
|
|
let mut vm = Vm::new();
|
|
let result = eval(&mut vm, "JSON.stringify(false)").unwrap();
|
|
assert!(result.is_string());
|
|
let s = string_value(&vm, result);
|
|
assert_eq!(s, "false");
|
|
}
|
|
|
|
#[test]
|
|
fn json_stringify_null() {
|
|
let mut vm = Vm::new();
|
|
let result = eval(&mut vm, "JSON.stringify(null)").unwrap();
|
|
assert!(result.is_string());
|
|
let s = string_value(&vm, result);
|
|
assert_eq!(s, "null");
|
|
}
|
|
|
|
#[test]
|
|
fn json_stringify_undefined() {
|
|
let mut vm = Vm::new();
|
|
let result = eval(&mut vm, "JSON.stringify(undefined)").unwrap();
|
|
assert!(result.is_undefined());
|
|
}
|
|
|
|
// -- Roundtrip --
|
|
|
|
#[test]
|
|
fn json_parse_stringify_roundtrip() {
|
|
let mut vm = Vm::new();
|
|
let result = eval(&mut vm, "JSON.parse(JSON.stringify({a:1,b:2}))").unwrap();
|
|
assert!(result.is_object());
|
|
let obj = unsafe { &*result.as_js_object_ptr() };
|
|
assert_eq!(obj.prop_vec_len(), 2);
|
|
}
|
|
|
|
#[test]
|
|
fn json_roundtrip_array() {
|
|
let mut vm = Vm::new();
|
|
let _ = eval(&mut vm, "var arr = JSON.parse(JSON.stringify([1,2,3])); arr").unwrap();
|
|
}
|
|
|
|
// -- Edge cases --
|
|
|
|
#[test]
|
|
fn json_stringify_empty_object() {
|
|
let mut vm = Vm::new();
|
|
let result = eval(&mut vm, "JSON.stringify({})").unwrap();
|
|
assert!(result.is_string());
|
|
let s = string_value(&vm, result);
|
|
assert_eq!(s, "{}");
|
|
}
|
|
|
|
#[test]
|
|
fn json_stringify_empty_array() {
|
|
let mut vm = Vm::new();
|
|
let result = eval(&mut vm, "JSON.stringify([])").unwrap();
|
|
assert!(result.is_string());
|
|
let s = string_value(&vm, result);
|
|
assert_eq!(s, "[]");
|
|
}
|
|
|
|
#[test]
|
|
fn json_stringify_escape_quotes() {
|
|
let mut vm = Vm::new();
|
|
let result = eval(&mut vm, "JSON.stringify('he\"llo')").unwrap();
|
|
assert!(result.is_string());
|
|
let s = string_value(&vm, result);
|
|
assert_eq!(s, "\"he\\\"llo\"");
|
|
}
|
|
|
|
#[test]
|
|
fn json_stringify_escape_backslash() {
|
|
let mut vm = Vm::new();
|
|
let result = eval(&mut vm, "JSON.stringify('a\\\\b')").unwrap();
|
|
assert!(result.is_string());
|
|
let s = string_value(&vm, result);
|
|
assert_eq!(s, "\"a\\\\b\"");
|
|
}
|
|
|
|
#[test]
|
|
fn json_stringify_nested_objects() {
|
|
let mut vm = Vm::new();
|
|
let result = eval(&mut vm, "JSON.stringify({a: {b: 1}})").unwrap();
|
|
assert!(result.is_string());
|
|
let s = string_value(&vm, result);
|
|
assert_eq!(s, "{\"a\":{\"b\":1}}");
|
|
}
|
|
|
|
#[test]
|
|
fn json_parse_empty_object() {
|
|
let mut vm = Vm::new();
|
|
let result = eval(&mut vm, "JSON.parse('{}')").unwrap();
|
|
assert!(result.is_object());
|
|
let obj = unsafe { &*result.as_js_object_ptr() };
|
|
assert_eq!(obj.prop_vec_len(), 0);
|
|
}
|
|
|
|
#[test]
|
|
fn json_parse_empty_array() {
|
|
let mut vm = Vm::new();
|
|
let result = eval(&mut vm, "JSON.parse('[]')").unwrap();
|
|
assert!(result.is_object());
|
|
let obj = unsafe { &*result.as_js_object_ptr() };
|
|
assert!(obj.is_array());
|
|
assert_eq!(obj.prop_vec_len(), 0);
|
|
}
|
|
|
|
#[test]
|
|
fn json_parse_int_is_double() {
|
|
let mut vm = Vm::new();
|
|
let result = eval(&mut vm, "JSON.parse('123')").unwrap();
|
|
assert!((result.as_double() - 123.0).abs() < 0.0001);
|
|
}
|
|
|
|
// -- Cycle detection --
|
|
|
|
#[test]
|
|
fn json_stringify_cycle_throws_type_error() {
|
|
let mut vm = Vm::new();
|
|
let err = eval(&mut vm, "var a = {}; a.self = a; JSON.stringify(a)");
|
|
assert!(err.is_err());
|
|
}
|