use oxide_compiler::compiler::Compiler; use oxide_types::object::JsObject; use oxide_types::value::JsValue; use oxide_vm::vm::Vm; fn eval(vm: &mut Vm, source: &str) -> Result { 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 to_str(vm: &Vm, val: JsValue) -> String { vm.lookup_str(val).unwrap_or_default() } #[test] fn iterator_global_exists() { let mut vm = Vm::new(); let result = eval(&mut vm, "typeof Iterator").unwrap(); assert_eq!(to_str(&vm, result), "function"); } #[test] fn iterator_from_array_returns_values_until_done() { let mut vm = Vm::new(); let result = eval( &mut vm, "var iter = Iterator.from([1, 2, 3]); \ var a = iter.next(); var b = iter.next(); var c = iter.next(); var d = iter.next(); \ a.value === 1 && a.done === false && b.value === 2 && c.value === 3 && d.done === true", ) .unwrap(); assert!(result.as_bool()); } #[test] fn iterator_from_string_returns_chars() { let mut vm = Vm::new(); let result = eval(&mut vm, "var iter = Iterator.from('abc'); iter.next().value").unwrap(); assert_eq!(to_str(&vm, result), "a"); } #[test] fn iterator_from_iterator_forwards_next() { let mut vm = Vm::new(); let result = eval( &mut vm, "var inner = { next: function() { return { value: 7, done: false }; } }; \ Iterator.from(inner).next().value", ) .unwrap(); assert_eq!(result.as_int(), 7); } #[test] fn new_iterator_throws_type_error() { let mut vm = Vm::new(); let result = eval(&mut vm, "try { new Iterator() } catch (e) { e instanceof TypeError }").unwrap(); assert!(result.as_bool()); } #[test] fn iterator_from_string_full_sequence() { // 手动 next 逐字符取完全部 ASCII 字符后 done(游标推进路径)。 let mut vm = Vm::new(); let result = eval( &mut vm, "var it = Iterator.from('ab'); \ var r = []; var x; while (!(x = it.next()).done) r.push(x.value); \ r.join('')", ) .unwrap(); assert_eq!(to_str(&vm, result), "ab"); } #[test] fn iterator_from_string_astral_scalar() { // 字符串迭代按 Unicode 标量推进:astral 字符整体产出(标量语义未动)。 let mut vm = Vm::new(); let result = eval(&mut vm, "var it = Iterator.from('\\u{1F600}'); it.next().value").unwrap(); assert_eq!(to_str(&vm, result), "\u{1F600}"); } #[test] fn iterator_from_string_empty_done() { let mut vm = Vm::new(); let result = eval(&mut vm, "Iterator.from('').next().done").unwrap(); assert!(result.as_bool()); } #[test] fn for_of_string_chars() { // for-of 字符串循环产出逐字符(走字节游标 + 单字符缓存)。 let mut vm = Vm::new(); let result = eval(&mut vm, "var out = []; for (const c of 'abc') out.push(c); out.join('')").unwrap(); assert_eq!(to_str(&vm, result), "abc"); } #[test] fn iterator_proto_chain_and_self_iteration() { let mut vm = Vm::new(); let cases = [ // 各家族迭代器自迭代恒等(%IteratorPrototype% 的 @@iterator 返回 this)。 ("var it = [1,2].values(); it[Symbol.iterator]() === it", true), ("var it = new Map([['a',1]]).values(); it[Symbol.iterator]() === it", true), ("var it = new Set([1]).values(); it[Symbol.iterator]() === it", true), ("var it = new Uint8Array([1]).values(); it[Symbol.iterator]() === it", true), ("var it = 'abc'[Symbol.iterator](); it[Symbol.iterator]() === it", true), ("var it = 'ab'.matchAll(/a/g); it[Symbol.iterator]() === it", true), // Array/TA 共享 %ArrayIteratorPrototype%;Map/Set 各自原型内部共享。 ("Object.getPrototypeOf([].values()) === Object.getPrototypeOf([].entries())", true), ( "Object.getPrototypeOf(new Uint8Array(0).values()) === Object.getPrototypeOf([].values())", true, ), ( "Object.getPrototypeOf(new Map().values()) === Object.getPrototypeOf(new Map().keys())", true, ), ( "Object.getPrototypeOf(new Set().values()) === Object.getPrototypeOf(new Set().entries())", true, ), // %IteratorPrototype% 自身可迭代:其 @@iterator 返回 this。 ( "var P = Object.getPrototypeOf(Object.getPrototypeOf([].values())); P[Symbol.iterator]() === P", true, ), // 原型链:%ArrayIteratorPrototype% → %IteratorPrototype% → Object.prototype。 ( "Object.getPrototypeOf(Object.getPrototypeOf([].values())) === \ Object.getPrototypeOf(Object.getPrototypeOf(Object.getPrototypeOf([].values())))", false, ), ( "Object.getPrototypeOf(Object.getPrototypeOf([].values()))[Symbol.iterator] === \ Object.getPrototypeOf(new Map().values())[Symbol.iterator]", true, ), ]; for (src, expected) in cases { let result = eval(&mut vm, src).unwrap(); assert_eq!(result.as_bool(), expected, "for {}", src); } } #[test] fn array_string_symbol_iterator_bound() { let mut vm = Vm::new(); // G4 别名:Array/String 的 @@iterator 属性存在且可迭代。 let bool_cases = [ ("[][Symbol.iterator] === [].values", true), ("var out = []; for (const c of 'a\\u{1F600}b') out.push(c); out.length === 3", true), ]; for (src, expected) in bool_cases { let result = eval(&mut vm, src).unwrap(); assert_eq!(result.as_bool(), expected, "for {}", src); } let str_cases = [ ("typeof 'abc'[Symbol.iterator]", "function"), ("[...'ab'].join('')", "ab"), ("Array.from('ab').join('')", "ab"), ("[...[1,2,3]].join(',')", "1,2,3"), ("Array.from([1,2]).join(',')", "1,2"), ]; for (src, expected) in str_cases { let result = eval(&mut vm, src).unwrap(); assert_eq!(to_str(&vm, result), expected, "for {}", src); } // String 迭代器语义:null/undefined 抛 TypeError,对象走 ToString。 let err = eval(&mut vm, "try { String.prototype[Symbol.iterator].call(null) } catch (e) { e.name }").unwrap(); assert_eq!(to_str(&vm, err), "TypeError"); let result = eval(&mut vm, "[...String.prototype[Symbol.iterator].call({toString: () => 'xy'})].join('')").unwrap(); assert_eq!(to_str(&vm, result), "xy"); } #[test] fn iterator_proto_object_proto_parent() { // 链:values() → %ArrayIteratorPrototype% → %IteratorPrototype% → Object.prototype。 let mut vm = Vm::new(); let result = eval( &mut vm, "Object.getPrototypeOf(Object.getPrototypeOf(Object.getPrototypeOf([].values()))) === Object.prototype", ) .unwrap(); assert!(result.as_bool()); } #[test] fn iterator_function_prototype_property() { // Iterator.prototype 属性绑定:=== %IteratorPrototype%([].values() 原型链中继), // 且 %IteratorPrototype% 自身可迭代。 let mut vm = Vm::new(); let cases = [ ("Iterator.prototype !== undefined", true), ("Object.getPrototypeOf(Object.getPrototypeOf([].values())) === Iterator.prototype", true), ( "Object.getPrototypeOf(Object.getPrototypeOf(new Map().values())) === Iterator.prototype", true, ), ("Iterator.prototype[Symbol.iterator]() === Iterator.prototype", true), ]; for (src, expected) in cases { let result = eval(&mut vm, src).unwrap(); assert_eq!(result.as_bool(), expected, "for {}", src); } } #[test] fn string_iterator_proto_layer() { // String 迭代器挂 %StringIteratorPrototype% 中继层:与 Array 迭代器原型不同, // 且经链到 %IteratorPrototype%(Iterator.prototype)。 let mut vm = Vm::new(); let cases = [ ( "Object.getPrototypeOf(Object.getPrototypeOf('a'[Symbol.iterator]())) === Iterator.prototype", true, ), ( "Object.getPrototypeOf('a'[Symbol.iterator]()) !== Object.getPrototypeOf([].values())", true, ), ]; for (src, expected) in cases { let result = eval(&mut vm, src).unwrap(); assert_eq!(result.as_bool(), expected, "for {}", src); } let out = eval(&mut vm, "[...'abc'].join('')").unwrap(); assert_eq!(to_str(&vm, out), "abc"); } #[test] fn iterator_next_on_prototypes() { // next 挂集合迭代器原型(%ArrayIteratorPrototype%.next 等),wrapper 无 own next。 let mut vm = Vm::new(); let cases = [ ("typeof Object.getPrototypeOf([].values()).next === 'function'", true), ("Object.getPrototypeOf([].values()).hasOwnProperty('next')", true), ("[].values().hasOwnProperty('next') === false", true), ("typeof Object.getPrototypeOf(new Map().values()).next === 'function'", true), ("new Map().values().hasOwnProperty('next') === false", true), ("typeof Object.getPrototypeOf(new Set().values()).next === 'function'", true), ("new Set().values().hasOwnProperty('next') === false", true), ("typeof Object.getPrototypeOf(new Uint8Array([1]).values()).next === 'function'", true), ("new Uint8Array([1]).values().hasOwnProperty('next') === false", true), ("typeof Object.getPrototypeOf('ab'.matchAll(/a/g)).next === 'function'", true), ("'ab'.matchAll(/a/g).hasOwnProperty('next') === false", true), ]; for (src, expected) in cases { let result = eval(&mut vm, src).unwrap(); assert_eq!(result.as_bool(), expected, "for {}", src); } } #[test] fn iterator_proto_next_consumption_unchanged() { // 行为回归:next 移上原型后各家族消费路径不变(迭代协议经原型链解析)。 let mut vm = Vm::new(); let str_cases = [ ("[...[1,2,3]].join(',')", "1,2,3"), ("var it = [1,2].values(); it.next().value + ',' + it.next().value", "1,2"), ("var it = ['x','y'].entries(); it.next().value.join(':')", "0:x"), ("[...new Map([['a',1]])][0].join(':')", "a:1"), ("var mk = new Map([['k',9]]); mk.keys().next().value", "k"), ("var se = new Set([7]); se.entries().next().value.join(':')", "7:7"), ("new Uint8Array([9]).entries().next().value.join(',')", "0,9"), ("[...'ab'].join('')", "ab"), ("'a\\u{1F600}b'[Symbol.iterator]().next().value", "a"), ("var mm = 'ab'.matchAll(/b/g); mm.next().value[0]", "b"), ]; for (src, expected) in str_cases { let result = eval(&mut vm, src).unwrap(); assert_eq!(to_str(&vm, result), expected, "for {}", src); } // 数值结果(含 int 0)用恒等比较断言,避免格式化路径差异。 let num_cases = [ ("[10,20].keys().next().value === 0", true), ("[1,2].values().next().value === 1", true), ("new Map([['a',1]]).values().next().value === 1", true), ("new Set([5,6]).keys().next().value === 5", true), ("new Uint8Array([5]).values().next().value === 5", true), ("new Uint8Array([7]).entries().next().value[1] === 7", true), ]; for (src, expected) in num_cases { let result = eval(&mut vm, src).unwrap(); assert_eq!(result.as_bool(), expected, "for {}", src); } // TA 迭代耗尽后恒 done(数组路径 target 置 undefined 防复活)。 let done = eval(&mut vm, "var it = new Uint8Array([1]).values(); it.next(); it.next().done").unwrap(); assert!(done.as_bool()); } #[test] fn iterator_function_name_length() { let mut vm = Vm::new(); let bool_cases = [ ("Iterator.name === 'Iterator'", true), ("Iterator.length === 0", true), ("Object.getOwnPropertyDescriptor(Iterator, 'name').writable === false", true), ("Object.getOwnPropertyDescriptor(Iterator, 'name').enumerable === false", true), ("Object.getOwnPropertyDescriptor(Iterator, 'name').configurable === true", true), ("Object.getOwnPropertyDescriptor(Iterator, 'length').writable === false", true), ("Object.getOwnPropertyDescriptor(Iterator, 'length').enumerable === false", true), ("Object.getOwnPropertyDescriptor(Iterator, 'length').configurable === true", true), ]; for (src, expected) in bool_cases { let result = eval(&mut vm, src).unwrap(); assert_eq!(result.as_bool(), expected, "for {}", src); } } #[test] fn iterator_prototype_constructor_accessor() { let mut vm = Vm::new(); let bool_cases = [ // getter 动态读 global 上的 Iterator 构造器。 ("Iterator.prototype.constructor === Iterator", true), ( "typeof Object.getOwnPropertyDescriptor(Iterator.prototype, 'constructor').get === 'function'", true, ), ( "typeof Object.getOwnPropertyDescriptor(Iterator.prototype, 'constructor').set === 'function'", true, ), ( "Object.getOwnPropertyDescriptor(Iterator.prototype, 'constructor').enumerable === false", true, ), ( "Object.getOwnPropertyDescriptor(Iterator.prototype, 'constructor').configurable === true", true, ), // getter 无参调用(this=undefined)仍返回 Iterator。 ( "Object.getOwnPropertyDescriptor(Iterator.prototype, 'constructor').get.call() === Iterator", true, ), ]; for (src, expected) in bool_cases { let result = eval(&mut vm, src).unwrap(); assert_eq!(result.as_bool(), expected, "for {}", src); } } #[test] fn iterator_prototype_constructor_setter_semantics() { let mut vm = Vm::new(); // home 对象赋值与原始值 this 抛 TypeError(SetterThatIgnoresPrototypeProperties)。 let throws = [ ( "try { Iterator.prototype.constructor = 'x'; false } catch (e) { e instanceof TypeError }", true, ), ( "try { var d = Object.getOwnPropertyDescriptor(Iterator.prototype, 'constructor'); \ d.set.call(undefined, 'x'); false } catch (e) { e instanceof TypeError }", true, ), ( "try { var d = Object.getOwnPropertyDescriptor(Iterator.prototype, 'constructor'); \ d.set.call(null, 'x'); false } catch (e) { e instanceof TypeError }", true, ), ( "try { var d = Object.getOwnPropertyDescriptor(Iterator.prototype, 'constructor'); \ d.set.call(true, 'x'); false } catch (e) { e instanceof TypeError }", true, ), ]; for (src, expected) in throws { let result = eval(&mut vm, src).unwrap(); assert_eq!(result.as_bool(), expected, "for {}", src); } // 非 home 对象:无 own 属性建 own,有 own 属性走 Set 覆盖。 let ok_cases = [ ( "var o = {}; var d = Object.getOwnPropertyDescriptor(Iterator.prototype, 'constructor'); \ d.set.call(o, 42); o.constructor === 42 && o.hasOwnProperty('constructor')", true, ), ( "var o = { constructor: 1 }; var d = Object.getOwnPropertyDescriptor(Iterator.prototype, 'constructor'); \ d.set.call(o, 2); o.constructor === 2", true, ), // home 不受污染。 ("Iterator.prototype.constructor === Iterator", true), ]; for (src, expected) in ok_cases { let result = eval(&mut vm, src).unwrap(); assert_eq!(result.as_bool(), expected, "for {}", src); } } #[test] fn iterator_prototype_to_string_tag_accessor() { let mut vm = Vm::new(); let bool_cases = [ ("Iterator.prototype[Symbol.toStringTag] === 'Iterator'", true), ( "typeof Object.getOwnPropertyDescriptor(Iterator.prototype, Symbol.toStringTag).get === 'function'", true, ), ( "typeof Object.getOwnPropertyDescriptor(Iterator.prototype, Symbol.toStringTag).set === 'function'", true, ), ( "Object.getOwnPropertyDescriptor(Iterator.prototype, Symbol.toStringTag).enumerable === false", true, ), ( "Object.getOwnPropertyDescriptor(Iterator.prototype, Symbol.toStringTag).configurable === true", true, ), ( "Object.getOwnPropertyDescriptor(Iterator.prototype, Symbol.toStringTag).get.call() === 'Iterator'", true, ), // home 赋值抛 TypeError。 ( "try { Iterator.prototype[Symbol.toStringTag] = 'x'; false } catch (e) { e instanceof TypeError }", true, ), // 普通对象 setter 建 own Symbol.toStringTag。 ( "var o = {}; var d = Object.getOwnPropertyDescriptor(Iterator.prototype, Symbol.toStringTag); \ d.set.call(o, 'tag'); o[Symbol.toStringTag] === 'tag'", true, ), // 原型继承场景:非 home 子对象赋值落到自身 own 属性(不污染祖先)。 ( "var p = Object.create(Iterator.prototype); p[Symbol.toStringTag] = 'sub'; \ p[Symbol.toStringTag] === 'sub' && Iterator.prototype[Symbol.toStringTag] === 'Iterator'", true, ), ]; for (src, expected) in bool_cases { let result = eval(&mut vm, src).unwrap(); assert_eq!(result.as_bool(), expected, "for {}", src); } } #[test] fn symbol_dispose_and_async_dispose_registered() { let mut vm = Vm::new(); // well-known symbol 以空对象表示,typeof 为 object;两符号互不相同。 let bool_cases = [ ("typeof Symbol.dispose === 'object'", true), ("typeof Symbol.asyncDispose === 'object'", true), ("Symbol.dispose !== Symbol.asyncDispose", true), ("Symbol.dispose !== Symbol.iterator", true), // 键可作属性名使用(well-known 键映射一致)。 ("({ [Symbol.dispose]: 1 })[Symbol.dispose] === 1", true), // getOwnPropertySymbols 能反解出同一符号对象。 ("Object.getOwnPropertySymbols({ [Symbol.dispose]: 1 })[0] === Symbol.dispose", true), ]; for (src, expected) in bool_cases { let result = eval(&mut vm, src).unwrap(); assert_eq!(result.as_bool(), expected, "for {}", src); } // 描述性名称经 String 构造器反射(well_known_symbol_name 表)。 let desc = eval(&mut vm, "String(Symbol.dispose)").unwrap(); assert_eq!(to_str(&vm, desc), "Symbol(Symbol.dispose)"); } #[test] fn iterator_prototype_symbol_dispose() { let mut vm = Vm::new(); // @@dispose 调用 this 的 return 方法并返回 undefined。 let bool_cases = [ ("typeof Iterator.prototype[Symbol.dispose] === 'function'", true), ("Iterator.prototype[Symbol.dispose].length === 0", true), ("Iterator.prototype[Symbol.dispose].name === '[Symbol.dispose]'", true), ( "var called = false; var it = { return: function() { called = true; return { done: true }; } }; \ var r = Iterator.prototype[Symbol.dispose].call(it); r === undefined && called === true", true, ), // 无 return 方法:GetMethod 返回 undefined,跳过调用。 ("Iterator.prototype[Symbol.dispose].call({ next: function() {} }) === undefined", true), // return 返回任意值不影响 @@dispose 的 undefined 返回值。 ( "var it = { return: function() { return 42; } }; \ Iterator.prototype[Symbol.dispose].call(it) === undefined", true, ), ]; for (src, expected) in bool_cases { let result = eval(&mut vm, src).unwrap(); assert_eq!(result.as_bool(), expected, "for {}", src); } // return 抛错经 @@dispose 透传(原值,不二次包装)。 let thrown = eval( &mut vm, "try { var it = { return: function() { throw new RangeError('boom'); } }; \ Iterator.prototype[Symbol.dispose].call(it); false } catch (e) { e instanceof RangeError }", ) .unwrap(); assert!(thrown.as_bool()); } #[test] fn iterator_helper_prototype_links_to_iterator_proto() { let vm = Vm::new(); // %IteratorHelperPrototype% 已建且链到 %IteratorPrototype%(链: // helper → %IteratorPrototype% → Object.prototype)。 let helper_ptr = vm.session().builtin_world().iterator_helper_proto.as_ptr() as *mut JsObject; let helper_proto_val = unsafe { &*helper_ptr }.proto(); let iter_ptr = vm.session().builtin_world().iterator_proto.as_ptr() as *mut JsObject; assert!(helper_proto_val.is_object()); assert!(std::ptr::eq(helper_proto_val.as_js_object_ptr(), iter_ptr)); } #[test] fn iterator_constructor_plain_call_throws() { let mut vm = Vm::new(); // 普通调用 `Iterator()` 抛 TypeError(emit 以 undefined 作 this)。 let bool_cases = [ ("try { Iterator(); false } catch (e) { e instanceof TypeError }", true), // call/apply 显式传对象 this 顶层调用同样抛(newTarget 槽为 undefined)。 ("try { Iterator.call({}); false } catch (e) { e instanceof TypeError }", true), ("try { Iterator.apply({}, []); false } catch (e) { e instanceof TypeError }", true), // 以 %IteratorPrototype% 为原型的对象顶层调用也抛(home 形态误判保护)。 ( "try { Iterator.call(Object.create(Iterator.prototype)); false } \ catch (e) { e instanceof TypeError }", true, ), ]; for (src, expected) in bool_cases { let result = eval(&mut vm, src).unwrap(); assert_eq!(result.as_bool(), expected, "for {}", src); } } #[test] fn iterator_constructor_subclassable() { let mut vm = Vm::new(); // `class X extends Iterator {}` 的 super() 经放行路径返回 undefined, // 实例原型按 newTarget 设为 X.prototype。 let bool_cases = [ ( "class TestIterator extends Iterator {} \ var it = new TestIterator(); \ it instanceof TestIterator && it instanceof Iterator", true, ), ( "class TestIterator extends Iterator {} \ Object.getPrototypeOf(new TestIterator()) === TestIterator.prototype", true, ), ( "class TestIterator extends Iterator {} \ Object.getPrototypeOf(TestIterator.prototype) === Iterator.prototype", true, ), // 显式 constructor 内 super() 与隐式等价。 ( "class TestIterator extends Iterator { constructor() { super(); } } \ new TestIterator() instanceof Iterator", true, ), // 多层继承:newTarget 跨层保持为最外层类。 ( "class A extends Iterator {} class B extends A {} \ var b = new B(); \ b instanceof B && b instanceof A && b instanceof Iterator", true, ), // 构造器 name 不受影响。 ("class TestIterator extends Iterator {} TestIterator.name === 'TestIterator'", true), ]; for (src, expected) in bool_cases { let result = eval(&mut vm, src).unwrap(); assert_eq!(result.as_bool(), expected, "for {}", src); } } #[test] fn iterator_constructor_subclass_still_rejects_new_after_reset() { let mut vm = Vm::new(); // dirty reset 后构造器经 bind_iterator_global 重绑,本体与 subclass 路径不变。 unsafe { &mut *(vm.session().builtin_world().iterator_proto.as_ptr() as *mut JsObject) }.bump_generation(); vm.full_reset(); let bool_cases = [ ("try { new Iterator(); false } catch (e) { e instanceof TypeError }", true), ( "class TestIterator extends Iterator {} \ new TestIterator() instanceof Iterator", true, ), ]; for (src, expected) in bool_cases { let result = eval(&mut vm, src).unwrap(); assert_eq!(result.as_bool(), expected, "for {}", src); } } #[test] fn iterator_prototype_rebound_after_full_reset() { let mut vm = Vm::new(); // 用户改写 %IteratorPrototype%:object 家族世代递增(global 未动)。 let old_iter_proto = vm.session().builtin_world().iterator_proto.as_ptr(); unsafe { &mut *(old_iter_proto as *mut JsObject) }.bump_generation(); vm.full_reset(); // %IteratorPrototype% 重建后访问器 / @@dispose 重绑正确,构造器 identity 保留。 let cases = [ ("Iterator.prototype.constructor === Iterator", true), ("Iterator.prototype[Symbol.toStringTag] === 'Iterator'", true), ("typeof Iterator.prototype[Symbol.dispose] === 'function'", true), ("Iterator.name === 'Iterator'", true), ("Iterator.length === 0", true), ("typeof Symbol.dispose === 'object'", true), ("typeof Symbol.asyncDispose === 'object'", true), ]; for (src, expected) in cases { let result = eval(&mut vm, src).unwrap(); assert_eq!(result.as_bool(), expected, "for {}", src); } // helper 原型随 object 家族重建并链到新 %IteratorPrototype%。 let helper_ptr = vm.session().builtin_world().iterator_helper_proto.as_ptr() as *mut JsObject; let helper_proto_val = unsafe { &*helper_ptr }.proto(); let new_iter_ptr = vm.session().builtin_world().iterator_proto.as_ptr() as *mut JsObject; assert!(std::ptr::eq(helper_proto_val.as_js_object_ptr(), new_iter_ptr)); // 迭代器家族整体功能完好。 let r = eval( &mut vm, "Iterator.prototype[Symbol.dispose].call({return: function(){return {done:true}}})", ) .unwrap(); assert!(r.is_undefined()); }