/* * Test cases for extended JSON formats JX and JC * (DUK_USE_JX, DUK_USE_JC). */ /*--- { "custom": true } ---*/ function encJx(val) { return Duktape.enc('jx', val); } function decJx(val) { return Duktape.dec('jx', val); } function safedecJx(val) { try { return decJx(val); } catch (e) { return e.name; } } function encJc(val) { return Duktape.enc('jc', val); } function decJc(val) { return Duktape.dec('jc', val); } function safedecJc(val) { try { return decJc(val); } catch (e) { return e.name; } } /*=== primitive type encode undefined undefined {"_undef":true} object null null boolean true true boolean false false number 123 123 number -0 -0 number 0 0 number NaN {"_nan":true} number -Infinity {"_ninf":true} number Infinity {"_inf":true} string "foo" "foo" object [1,2,3] [1,2,3] object {test_key:123,"foo bar":321} {"test_key":123,"foo bar":321} buffer |deadbeef| {"_buf":"deadbeef"} object |deadbeef| {"_buf":"deadbeef"} pointer (null) {"_ptr":"null"} object (null) {"_ptr":"null"} function {_func:true} {"_func":true} pointer jx non-null ptr true jc non-null ptr true object jx non-null ptr true jc non-null ptr true primitive type decode jx undefined undefined object null boolean true boolean false number 123 number NaN number -Infinity number Infinity string foo object [1,2,3] object {"test_key":"bar","foo bar":"quux"} buffer deadbeef pointer nonnull pointer null object functable jc object {"_undef":true} object null boolean true boolean false number 123 object {"_nan":true} object {"_ninf":true} object {"_inf":true} string foo object [1,2,3] object {"test_key":"bar","foo bar":"quux"} object {"_buf":"deadbeef"} object {"_ptr":"(0x12345678)"} object {"_ptr":"(null)"} object functable ===*/ function typeEncodeTest() { var tmp; var ptr; var m_ptr_nonnull_jx = /^\([0-9a-fA-Fx:]+\)$/; // e.g. (0x1345890) var m_ptr_nonnull_jc = /^\{\"_ptr\":\"[0-9a-fA-Fx:]+\"\}$/; // e.g. {"_ptr":"0x1345890"} var func = function() { print('hello world'); }; var values = [ undefined, null, true, false, 123.0, -0, +0, 0 / 0, -1 / 0, 1 / 0, 'foo', [1,2,3], { test_key: 123, 'foo bar': 321 }, Duktape.dec('hex', 'deadbeef'), // plain buf new Duktape.Buffer(Duktape.dec('hex', 'deadbeef')), // object buf Duktape.Pointer(), // plain ptr; null pointer has a deterministic representation new Duktape.Pointer(), // object ptr function myfunc() {} ]; // Test all primitive types except pointer, whose representation // is platform specific, and function, whose representation may // vary from version to version. values.forEach(function(v) { print(typeof v, encJx(v), encJc(v)); }); // pointer: match against a pattern since the contents are otherwise // platform dependent ptr = Duktape.Pointer('dummy'); // non-null plain ptr print(typeof ptr); tmp = encJx(ptr); print('jx non-null ptr', m_ptr_nonnull_jx.test(tmp)); tmp = encJc(ptr); print('jc non-null ptr', m_ptr_nonnull_jc.test(tmp)); ptr = new Duktape.Pointer('dummy'); // non-null object ptr print(typeof ptr); tmp = encJx(ptr); print('jx non-null ptr', m_ptr_nonnull_jx.test(tmp)); tmp = encJc(ptr); print('jc non-null ptr', m_ptr_nonnull_jc.test(tmp)); } function typeDecodeTest(dec) { function dval(t) { if (typeof t == 'buffer') { print(typeof t, Duktape.enc('hex', t)); } else if (typeof t == 'pointer') { if (t === Duktape.Pointer()) { print(typeof t, 'null'); } else { print(typeof t, 'nonnull'); } } else if (typeof t == 'object' && t !== null && t._func) { print(typeof t, 'functable'); // function always decodes back as a table } else if (typeof t == 'object' && t !== null) { // although pointer values encode to platform dependent JC objects, // they decode back as plain JSON objects without any interpretation, // so we can print them also here. print(typeof t, JSON.stringify(t)); } else { print(typeof t, String(t)); } } var jxValues = [ 'undefined', 'null', 'true', 'false', '123', 'NaN', '-Infinity', 'Infinity', '"foo"', '[1,2,3]', '{test_key:"bar","foo bar":"quux"}', '|deadbeef|', encJx(Duktape.Pointer('dummy')), // pointer format is platform specific so use a pointer generated // by Duktape; this is obviously not the best idea for testing '(null)', '{_func:true}' ]; var jcValues = [ '{"_undef":true}', 'null', 'true', 'false', '123', '{"_nan":true}', '{"_ninf":true}', '{"_inf":true}', '"foo"', '[1,2,3]', '{"test_key":"bar","foo bar":"quux"}', '{"_buf":"deadbeef"}', '{"_ptr":"(0x12345678)"}', // because this decodes into a normal object without // pointer parsing, a specific value can be used here '{"_ptr":"(null)"}', '{"_func":true}' ]; print('jx'); jxValues.forEach(function(v) { try { dval(decJx(v)); } catch (e) { print(e); } }); print('jc'); jcValues.forEach(function(v) { try { dval(decJc(v)); } catch (e) { print(e); } }); } print('primitive type encode'); try { typeEncodeTest(); } catch (e) { print(e); } print('primitive type decode'); try { typeDecodeTest(); } catch (e) { print(e); } /*=== signed zero 0 0 -0 0 -0 0 number 0 neg number 0 pos number 0 neg number 0 pos number 0 neg number 0 pos ===*/ /* Although JSON syntax allows negative zero, the standard algorithm serializes * negative zero as '0'. Curiously, the standard algorithm still parses a '-0' * back as a negative zero. * * JX/JC serialize a negative zero as '-0' and should parse it back also * without losing the sign. */ function signedZeroTest() { function prVal(v) { print(typeof v, v, (1 / v > 0 ? 'pos' : 'neg')); } print(JSON.stringify(-0)); print(JSON.stringify(+0)); print(Duktape.enc('jx', -0)); print(Duktape.enc('jx', +0)); print(Duktape.enc('jc', -0)); print(Duktape.enc('jc', +0)); prVal(JSON.parse('-0')); prVal(JSON.parse('0')); prVal(Duktape.dec('jx', '-0')); prVal(Duktape.dec('jx', '0')); prVal(Duktape.dec('jc', '-0')); prVal(Duktape.dec('jc', '0')); } print('signed zero'); try { signedZeroTest(); } catch (e) { print(e); } /*=== character escape encode "foo" "foo" "\xfc" "\u00fc" "\uabcd" "\uabcd" "\U0010fedc" "U+0010fedc" "\Udeadbeef" "U+deadbeef" character escape decode jx 0a0a0a0a0a0a0a SyntaxError jx c3bec3bec3bec3bec3bec3be SyntaxError jx e18aafe18aafe18aafe18aaf SyntaxError jx fe839eab9bbbaffe839eab9bbbaf SyntaxError ===*/ /* In addition to the standard '\uNNNN' format, the extended format supports * '\xNN' and '\UNNNNNNNN'. The encoding is shortest possible while any * format is accepted for decoding. */ function characterEscapeEncodeTest() { function mk(hex) { return String(Duktape.dec('hex', hex)); } var values = [ '666f6f', // foo 'c3bc', // U+00FC 'eaaf8d', // U+ABCD 'f48fbb9c', // U+0010FEDC 'fe839eab9bbbaf' // U+DEADBEEF ]; values.forEach(function (v) { var t = mk(v); print(encJx(t), encJc(t)); }); } function characterEscapeDecodeTest() { function dump(val) { return String(Duktape.enc('hex', val)); } var values = [ '"\\n\\x0a\\x0A\\u000a\\u000A\\U0000000a\\U0000000A"', '"\\xfe\\xFE\\u00fe\\u00FE\\U000000fe\\U000000FE"', '"\\u12aF\\u12Af\\U000012aF\\U000012Af"', '"\\UDeAdBeEf\\UdEaDbEeF"' ]; // only makes sense for JX, but check the JC SyntaxError also values.forEach(function (v) { try { print('jx', dump(decJx(v))); } catch (e) { print(e.name); } try { print('jc', dump(decJc(v))); } catch (e) { print(e.name); } }); } print('character escape encode'); try { characterEscapeEncodeTest(); } catch (e) { print(e); } print('character escape decode'); try { characterEscapeDecodeTest(); } catch (e) { print(e); } /*=== ascii only output jx "\x00\x01\x02\x03\x04\x05\x06\x07\b\t\n\x0b\f\r\x0e\x0f" jc "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f" json "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f" jx "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" jc "\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f" json "\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f" jx " !\"#$%&'()*+,-./" jc " !\"#$%&'()*+,-./" json " !\"#$%&'()*+,-./" jx "0123456789:;<=>?" jc "0123456789:;<=>?" json "0123456789:;<=>?" jx "@ABCDEFGHIJKLMNO" jc "@ABCDEFGHIJKLMNO" json "@ABCDEFGHIJKLMNO" jx "PQRSTUVWXYZ[\\]^_" jc "PQRSTUVWXYZ[\\]^_" json "PQRSTUVWXYZ[\\]^_" jx "`abcdefghijklmno" jc "`abcdefghijklmno" json "`abcdefghijklmno" jx "pqrstuvwxyz{|}~\x7f" jc "pqrstuvwxyz{|}~\u007f" jx "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" jc "\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008a\u008b\u008c\u008d\u008e\u008f" jx "\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" jc "\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009a\u009b\u009c\u009d\u009e\u009f" jx "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf" jc "\u00a0\u00a1\u00a2\u00a3\u00a4\u00a5\u00a6\u00a7\u00a8\u00a9\u00aa\u00ab\u00ac\u00ad\u00ae\u00af" jx "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" jc "\u00b0\u00b1\u00b2\u00b3\u00b4\u00b5\u00b6\u00b7\u00b8\u00b9\u00ba\u00bb\u00bc\u00bd\u00be\u00bf" jx "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" jc "\u00c0\u00c1\u00c2\u00c3\u00c4\u00c5\u00c6\u00c7\u00c8\u00c9\u00ca\u00cb\u00cc\u00cd\u00ce\u00cf" jx "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" jc "\u00d0\u00d1\u00d2\u00d3\u00d4\u00d5\u00d6\u00d7\u00d8\u00d9\u00da\u00db\u00dc\u00dd\u00de\u00df" jx "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef" jc "\u00e0\u00e1\u00e2\u00e3\u00e4\u00e5\u00e6\u00e7\u00e8\u00e9\u00ea\u00eb\u00ec\u00ed\u00ee\u00ef" jx "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" jc "\u00f0\u00f1\u00f2\u00f3\u00f4\u00f5\u00f6\u00f7\u00f8\u00f9\u00fa\u00fb\u00fc\u00fd\u00fe\u00ff" jx "\u0100\u0101\u0102\u0103\u0104\u0105\u0106\u0107\u0108\u0109\u010a\u010b\u010c\u010d\u010e\u010f" jc "\u0100\u0101\u0102\u0103\u0104\u0105\u0106\u0107\u0108\u0109\u010a\u010b\u010c\u010d\u010e\u010f" jx "\u0110\u0111\u0112\u0113\u0114\u0115\u0116\u0117\u0118\u0119\u011a\u011b\u011c\u011d\u011e\u011f" jc "\u0110\u0111\u0112\u0113\u0114\u0115\u0116\u0117\u0118\u0119\u011a\u011b\u011c\u011d\u011e\u011f" jx "\u0120\u0121\u0122\u0123\u0124\u0125\u0126\u0127\u0128\u0129\u012a\u012b\u012c\u012d\u012e\u012f" jc "\u0120\u0121\u0122\u0123\u0124\u0125\u0126\u0127\u0128\u0129\u012a\u012b\u012c\u012d\u012e\u012f" jx "\u0130\u0131\u0132\u0133\u0134\u0135\u0136\u0137\u0138\u0139\u013a\u013b\u013c\u013d\u013e\u013f" jc "\u0130\u0131\u0132\u0133\u0134\u0135\u0136\u0137\u0138\u0139\u013a\u013b\u013c\u013d\u013e\u013f" jx "\u0140\u0141\u0142\u0143\u0144\u0145\u0146\u0147\u0148\u0149\u014a\u014b\u014c\u014d\u014e\u014f" jc "\u0140\u0141\u0142\u0143\u0144\u0145\u0146\u0147\u0148\u0149\u014a\u014b\u014c\u014d\u014e\u014f" jx "\u0150\u0151\u0152\u0153\u0154\u0155\u0156\u0157\u0158\u0159\u015a\u015b\u015c\u015d\u015e\u015f" jc "\u0150\u0151\u0152\u0153\u0154\u0155\u0156\u0157\u0158\u0159\u015a\u015b\u015c\u015d\u015e\u015f" jx "\u0160\u0161\u0162\u0163\u0164\u0165\u0166\u0167\u0168\u0169\u016a\u016b\u016c\u016d\u016e\u016f" jc "\u0160\u0161\u0162\u0163\u0164\u0165\u0166\u0167\u0168\u0169\u016a\u016b\u016c\u016d\u016e\u016f" jx "\u0170\u0171\u0172\u0173\u0174\u0175\u0176\u0177\u0178\u0179\u017a\u017b\u017c\u017d\u017e\u017f" jc "\u0170\u0171\u0172\u0173\u0174\u0175\u0176\u0177\u0178\u0179\u017a\u017b\u017c\u017d\u017e\u017f" jx "\u0180\u0181\u0182\u0183\u0184\u0185\u0186\u0187\u0188\u0189\u018a\u018b\u018c\u018d\u018e\u018f" jc "\u0180\u0181\u0182\u0183\u0184\u0185\u0186\u0187\u0188\u0189\u018a\u018b\u018c\u018d\u018e\u018f" jx "\u0190\u0191\u0192\u0193\u0194\u0195\u0196\u0197\u0198\u0199\u019a\u019b\u019c\u019d\u019e\u019f" jc "\u0190\u0191\u0192\u0193\u0194\u0195\u0196\u0197\u0198\u0199\u019a\u019b\u019c\u019d\u019e\u019f" jx "\u01a0\u01a1\u01a2\u01a3\u01a4\u01a5\u01a6\u01a7\u01a8\u01a9\u01aa\u01ab\u01ac\u01ad\u01ae\u01af" jc "\u01a0\u01a1\u01a2\u01a3\u01a4\u01a5\u01a6\u01a7\u01a8\u01a9\u01aa\u01ab\u01ac\u01ad\u01ae\u01af" jx "\u01b0\u01b1\u01b2\u01b3\u01b4\u01b5\u01b6\u01b7\u01b8\u01b9\u01ba\u01bb\u01bc\u01bd\u01be\u01bf" jc "\u01b0\u01b1\u01b2\u01b3\u01b4\u01b5\u01b6\u01b7\u01b8\u01b9\u01ba\u01bb\u01bc\u01bd\u01be\u01bf" jx "\u01c0\u01c1\u01c2\u01c3\u01c4\u01c5\u01c6\u01c7\u01c8\u01c9\u01ca\u01cb\u01cc\u01cd\u01ce\u01cf" jc "\u01c0\u01c1\u01c2\u01c3\u01c4\u01c5\u01c6\u01c7\u01c8\u01c9\u01ca\u01cb\u01cc\u01cd\u01ce\u01cf" jx "\u01d0\u01d1\u01d2\u01d3\u01d4\u01d5\u01d6\u01d7\u01d8\u01d9\u01da\u01db\u01dc\u01dd\u01de\u01df" jc "\u01d0\u01d1\u01d2\u01d3\u01d4\u01d5\u01d6\u01d7\u01d8\u01d9\u01da\u01db\u01dc\u01dd\u01de\u01df" jx "\u01e0\u01e1\u01e2\u01e3\u01e4\u01e5\u01e6\u01e7\u01e8\u01e9\u01ea\u01eb\u01ec\u01ed\u01ee\u01ef" jc "\u01e0\u01e1\u01e2\u01e3\u01e4\u01e5\u01e6\u01e7\u01e8\u01e9\u01ea\u01eb\u01ec\u01ed\u01ee\u01ef" jx "\u01f0\u01f1\u01f2\u01f3\u01f4\u01f5\u01f6\u01f7\u01f8\u01f9\u01fa\u01fb\u01fc\u01fd\u01fe\u01ff" jc "\u01f0\u01f1\u01f2\u01f3\u01f4\u01f5\u01f6\u01f7\u01f8\u01f9\u01fa\u01fb\u01fc\u01fd\u01fe\u01ff" jx "\u0567" jc "\u0567" jx "\u1234" jc "\u1234" jx "\uffff" jc "\uffff" ===*/ function asciiOnlyTest() { var i, j; var tmp; // go through first 512 codepoints exhaustively for (i = 0; i < 32; i++) { tmp = ''; for (j = 0; j < 16; j++) { tmp += String.fromCharCode(i*16 + j); } print('jx', encJx(tmp)); print('jc', encJc(tmp)); if (i < 7) { // ASCII range, does not contain 0x7f (which standard JSON doesn't escape) print('json ', JSON.stringify(tmp)); } } // then sample a few codepoints above (these are already tested to // some extent by the character escape test case above) [ 0x0567, 0x1234, 0xffff ].forEach(function (cp) { var t = String.fromCharCode(cp); print('jx', encJx(t)); print('jc', encJc(t)); }); } print('ascii only output'); try { asciiOnlyTest(); } catch (e) { print(e); } /*=== avoid key quotes jx {$:true,_:true,a:true,z:true,A:true,Z:true,"0":false,"9":false,"":false} jc {"$":true,"_":true,"a":true,"z":true,"A":true,"Z":true,"0":false,"9":false,"":false} jx {$$:true,__:true,a$:true,z_:true,A0:true,Z9:true,"0$":false,"1_":false,"2a":false,"3z":false,"4A":false,"5A":false,"60":false,"79":false} jc {"$$":true,"__":true,"a$":true,"z_":true,"A0":true,"Z9":true,"0$":false,"1_":false,"2a":false,"3z":false,"4A":false,"5A":false,"60":false,"79":false} jx {test:true,test_key:true,_test_key:true,$test_key:true} jc {"test":true,"test_key":true,"_test_key":true,"$test_key":true} jx {"%foo":false,"foo%":false,"foo-bar":false,"foo bar":false} jc {"%foo":false,"foo%":false,"foo-bar":false,"foo bar":false} 1 2 3 4 5 6 SyntaxError 1 2 3 4 SyntaxError jx SyntaxError jc SyntaxError jx SyntaxError jc SyntaxError jx SyntaxError jc SyntaxError jx SyntaxError jc SyntaxError ===*/ function avoidKeyQuotesTest() { var t, inp; var obj1 = { $: true, _: true, a: true, z: true, A: true, Z: true, '0': false, '9': false, '': false // empty is specifically not accepted, although it would be unambiguous }; var obj2 = { $$: true, __: true, a$: true, z_: true, A0: true, Z9: true, '0$': false, '1_': false, '2a': false, '3z': false, '4A': false, '5A': false, '60': false, '79': false }; var obj3 = { test: true, test_key: true, _test_key: true, $test_key: true, }; var obj4 = { '%foo': false, 'foo%': false, 'foo-bar': false, 'foo bar': false }; // encode tests [ obj1, obj2, obj3, obj4 ].forEach(function (v) { print('jx', encJx(v)); print('jc', encJc(v)); }); // decode tests, only make sense for JX but JC is also // tried to display a SyntaxError. inp = '{ $: 1, _: 2, a: 3, z: 4, A: 5, Z: 6 }'; t = safedecJx(inp); print(t['$'], t['_'], t['a'], t['z'], t['A'], t['Z']); print(safedecJc(inp)); inp = '{ test: 1, test_key: 2, _test_key: 3, $test_key: 4 }'; t = safedecJx(inp); print(t['test'], t['test_key'], t['_test_key'], t['$test_key']); print(safedecJc(inp)); [ '{ %foo: 1 }', '{ foo%: 1 }', '{ foo-bar: 1 }', '{ foo bar: 1 }' ].forEach(function (v) { print('jx', safedecJx(v)); print('jc', safedecJc(v)); }); } print('avoid key quotes'); try { avoidKeyQuotesTest(); } catch (e) { print(e); } /*=== non-default encoding options { foo: "BAR", bar: |deadbeef|, quux: [ 123, NaN, Infinity, -Infinity ], baz: {_func:true} } { ->foo: "bar", ->bar: |deadbeef|, ->quux: [ ->->123, ->->NaN, ->->Infinity, ->->-Infinity ->] } { "foo": "BAR", "bar": {"_buf":"deadbeef"}, "quux": [ 123, {"_nan":true}, {"_inf":true}, {"_ninf":true} ], "baz": {"_func":true} } { ->"foo": "bar", ->"bar": {"_buf":"deadbeef"}, ->"quux": [ ->->123, ->->{"_nan":true}, ->->{"_inf":true}, ->->{"_ninf":true} ->] } ===*/ /* Test that non-default encoding options still work with the custom * formats. */ function nonDefaultEncodingTest() { var val = { foo: 'bar', bar: Duktape.dec('hex', 'deadbeef'), quux: [ 123, 0 / 0, 1 / 0, -1 / 0 ], baz: function() {} }; function ucStrings(k,v) { if (typeof v === 'string') { return v.toUpperCase() } else { return v } } print(Duktape.enc('jx', val, ucStrings /*replacer*/, 4 /*space*/)); print(Duktape.enc('jx', val, [ 'foo', 'bar', 'quux' ] /*replacer*/, '->' /*space*/)); // drop 'baz', use weird space print(Duktape.enc('jc', val, ucStrings /*replacer*/, 4 /*space*/)); print(Duktape.enc('jc', val, [ 'foo', 'bar', 'quux' ] /*replacer*/, '->' /*space*/)); // drop 'baz', use weird space } print('non-default encoding options'); try { nonDefaultEncodingTest(); } catch (e) { print(e); } /*=== non-default decoding options foo,bar,quux BAR foo,bar,quux BAR ===*/ /* Test that non-default decoding options still work with the custom * formats. */ function nonDefaultDecodingTest() { var val = { foo: 'bar', bar: Duktape.dec('hex', 'deadbeef'), quux: [ 123, 0 / 0, 1 / 0, -1 / 0 ], baz: function() {} }; var enc, dec; function revive(k,v) { if (k === 'baz') { return undefined; // delete 'baz' } if (typeof v === 'string') { return v.toUpperCase(); // uppercase strings } return v; } enc = encJx(val); dec = Duktape.dec('jx', enc, revive); print(Object.getOwnPropertyNames(dec)); print(dec.foo); enc = encJc(val); dec = Duktape.dec('jc', enc, revive); print(Object.getOwnPropertyNames(dec)); print(dec.foo); } print('non-default decoding options'); try { nonDefaultDecodingTest(); } catch (e) { print(e); } /*=== invalid xutf-8 e188 json 22c3a1c28822 jx "\xe1\x88" jc "\u00e1\u0088" ff41 json 22c3bf4122 jx "\xffA" jc "\u00ffA" c080 json 220022 jx "\x00" jc "\u0000" ===*/ /* Test invalid XUTF-8 handling in all modes, including standard JSON. This * behavior is always outside the scope of Ecmascript because all valid * Ecmascript strings are valid CESU-8. * * Because the XUTF-8 decoding is now lenient (it does not, for instance, * check continuation bytes at all), this test is now focused on testing * invalid initial characters or end-of-buffer conditions. */ function invalidXutf8Test() { var values = [ 'e188', // last byte missing from U+1234 encoding (e188b4) 'ff41', // first byte is an invalid initial byte 'c080', // non-shortest encoding for U+0000 ]; // Because standard JSON does not escape non-ASCII codepoints, hex // encode its output values.forEach(function (v) { var t = String(Duktape.dec('hex', v)); print(v); print('json ', Duktape.enc('hex', JSON.stringify(t))); print('jx', encJx(t)); print('jc', encJc(t)); }); } print('invalid xutf-8'); try { invalidXutf8Test(); } catch (e) { print(e); }