diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..e09b844 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +# This file is for unifying the coding style for different editors and IDEs +# editorconfig.org + +root = true + +[*] +end_of_line = lf +charset = utf-8 +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = space +indent_size = 2 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..91dfed8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.DS_Store +node_modules \ No newline at end of file diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..3009770 --- /dev/null +++ b/.npmignore @@ -0,0 +1,5 @@ +test +src +.travis.yml +Makefile +docs diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..244b7e8 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,3 @@ +language: node_js +node_js: + - '0.10' diff --git a/index.js b/index.js index 48c8c18..a556cba 100644 --- a/index.js +++ b/index.js @@ -1,287 +1,245 @@ -// Copyright (c) 2011 Jorge Chamorro Bieling -// MIT License - -var JASON = (function (exports) { - 'use strict'; - - var builtInObjects= []; - var builtInPaths= [ - 'Object', 'Object.prototype', - 'Array', 'Array.prototype', - 'Function', 'Function.prototype', - 'Date', 'Date.prototype', - 'RegExp', 'RegExp.prototype', - 'Number', 'Number.prototype', - 'Boolean', 'Boolean.prototype', - 'String', 'String.prototype', - 'Math', - 'JSON', - 'eval', - 'decodeURI', 'decodeURIComponent', - 'encodeURI', 'encodeURIComponent', - 'escape', 'unescape', - 'isFinite', 'isNaN', - 'parseFloat', 'parseInt', - 'Error', 'Error.prototype', - 'URIError', 'URIError.prototype', - 'TypeError', 'TypeError.prototype', - 'EvalError', 'EvalError.prototype', - 'RangeError', 'RangeError.prototype', - 'SyntaxError', 'SyntaxError.prototype', - 'ReferenceError', 'ReferenceError.prototype', - 'Map', 'Map.prototype', - 'Proxy', 'Proxy.prototype', - 'Set', 'Set.prototype', - 'WeakMap', 'WeakMap.prototype' - ].filter(function (v,i,o) { - 'use strict'; - try { - builtInObjects.push( eval(v) ); - //console.log("SI -> "+ v); - return true; - } - catch (e) { - //console.log("NO -> "+ v); - return false; +'use strict'; + +var builtInObjects = []; +var builtInPaths = [ + 'Object', 'Object.prototype', + 'Array', 'Array.prototype', + 'Function', 'Function.prototype', + 'Date', 'Date.prototype', + 'RegExp', 'RegExp.prototype', + 'Number', 'Number.prototype', + 'Boolean', 'Boolean.prototype', + 'String', 'String.prototype', + 'Math', + 'JSON', + 'eval', + 'decodeURI', 'decodeURIComponent', + 'encodeURI', 'encodeURIComponent', + 'escape', 'unescape', + 'isFinite', 'isNaN', + 'parseFloat', 'parseInt', + 'Error', 'Error.prototype', + 'URIError', 'URIError.prototype', + 'TypeError', 'TypeError.prototype', + 'EvalError', 'EvalError.prototype', + 'RangeError', 'RangeError.prototype', + 'SyntaxError', 'SyntaxError.prototype', + 'ReferenceError', 'ReferenceError.prototype', + 'Map', 'Map.prototype', + 'Proxy', 'Proxy.prototype', + 'Set', 'Set.prototype', + 'WeakMap', 'WeakMap.prototype' +].filter(function(v, i, o) { + try { + builtInObjects.push(eval(v)); + return true; + } catch (e) { + return false; + } +}); + +var seen, paths, i = 0; +while (i < builtInObjects.length) { + strfy(builtInObjects[i], builtInPaths[i], [], [], seen = [], paths = [], [], []); + + seen.forEach(function(v, i, o) { + var where = builtInObjects.indexOf(v); + if (where < 0) { + builtInPaths.push(paths[i]); + builtInObjects.push(seen[i]); } }); + i++; +} - var seen, paths, i= 0; - while (i < builtInObjects.length) { - //process.stdout.write('Analizando -> '+ builtInPaths[i]+ '\n'); - - strfy(builtInObjects[i], builtInPaths[i], [], [], seen= [], paths= [], [], []); - - //console.log(seen); - //console.log(paths); - - seen.forEach(function (v,i,o) { - 'use strict'; - //process.stdout.write('Comprobando -> '+ paths[i]+ '\n'); - var where= builtInObjects.indexOf(v); - if (where < 0) { - builtInPaths.push(paths[i]); - builtInObjects.push(seen[i]); - //process.stdout.write('Añadido -> '+ paths[i]+ '\n'); - } - }); - i++; +seen = paths = null; + +function strfy(o, path, builtInObjects, builtInPaths, seen, paths, cyclic, + ademas) { + + function isPrimitive(type, o) { + return ((type === 'number') || (type === 'string') || (type === + 'undefined') || (o === null) || (type === 'boolean')); } - //console.log('\n\n\n ************ \n\n\n'); - //console.log(builtInPaths); - //console.log('\n\n\n ************ \n\n\n'); - //console.log('DONE -> '+ builtInPaths.length); - - seen= paths= null; - - function stringify (o) { - 'use strict'; - var cyclic, ademas; - var r= stringify.this._strfy(o, 'o', stringify.this._builtInObjects, stringify.this._builtInPaths, [], [], cyclic= [], ademas= []); - - if (cyclic.length || ademas.length) { - - var txt= '(function(o){\n'; - - if (ademas.length) { - txt+= "/* Additional properties */\n"+ ademas.join(';\n')+ ';\n'; - } - - if (cyclic.length) { - txt+= "/* Cyclic properties */\n"+ cyclic.join(';\n')+ ';\n'; + var type = typeof o; + if (isPrimitive(type, o)) { + if (type === 'number') return '' + o; + if (type === 'undefined') return 'undefined'; + if (type === 'boolean') return o ? 'true' : 'false'; + if (type === 'string') { + var i = 0; + var oo = ''; + while (i < o.length) { + var char = o.charCodeAt(i); + if (char < 32) { + char = char.toString(16); + if (char.length < 2) char = '0' + char; + oo += '\\x' + char; + } else if (char === 34) { + oo += '\\"'; + } else if (char === 92) { + oo += '\\\\'; + } else { + oo += o[i]; + } + i++; } - - return txt+ 'return o;\n})('+ r+ ')'; + return '"' + oo + '"'; } - - return r; - } - - + return 'null'; + } + var where = builtInObjects.indexOf(o); + if (where >= 0) return builtInPaths[where]; - function strfy (o, path, builtInObjects, builtInPaths, seen, paths, cyclic, ademas) { - 'use strict'; + where = seen.indexOf(o); + if (where >= 0) { + cyclic.push(path + '= ' + paths[where]); + return '"&' + paths[where].replace(/\"/g, '') + '"'; + } else { + seen.push(o); + paths.push(path); + } - function isPrimitive (type, o) { - 'use strict'; - return ((type === 'number') || (type === 'string') || (type === 'undefined') || (o === null) || (type === 'boolean')); - } + //Arrays + if (Array.isArray(o)) { + var i = 0; + var t = []; + var defaultKeys = Object.getOwnPropertyNames([]); + var keys = Object.getOwnPropertyNames(o).filter(function(v, i, o) { + return (defaultKeys.indexOf(v) < 0); + }); - var type= typeof o; - if (isPrimitive(type, o)) { - if (type === 'number') return ''+ o; - if (type === 'undefined') return 'undefined'; - if (type === 'boolean') return o ? 'true' : 'false'; - if (type === 'string') { - var i= 0; - var oo= ''; - while (i < o.length) { - var char= o.charCodeAt(i); - if (char < 32) { - char= char.toString(16); - if (char.length < 2) char= '0'+ char; - oo+= '\\x'+ char; - } - else if (char === 34) { - oo+= '\\"'; - } - else if (char === 92) { - oo+= '\\\\'; - } - else { - oo+= o[i]; - } - i++; - } - return '"'+ oo+ '"'; + while (i < o.length) { + var where = keys.indexOf('' + i); + if (where >= 0) { + delete keys[where]; + t.push(strfy(o[i], path + '[' + i + ']', builtInObjects, + builtInPaths, seen, paths, cyclic, ademas)); + } else { + t.push(''); } - - return 'null'; + i++; } - var where= builtInObjects.indexOf(o); - if (where >= 0) return builtInPaths[where]; - - where= seen.indexOf(o); - if (where >= 0) { - //console.log('*** SEEN -> '+ paths[where]); - cyclic.push(path+ '= '+ paths[where]); - return '"&'+ paths[where].replace(/\"/g, '')+ '"'; - } - else { - //console.log('*** Unseen -> '+ path); - seen.push(o); - paths.push(path); + if (t.length && (t[t.length - 1] === '')) { + t.push(''); } - //Arrays - if (Array.isArray(o)) { - var i= 0; - var t= []; - var defaultKeys= Object.getOwnPropertyNames([]); - var keys= Object.getOwnPropertyNames(o).filter(function (v,i,o) { - return (defaultKeys.indexOf(v) < 0); - }); - - while (i < o.length) { - var where= keys.indexOf(''+ i); - if (where >= 0) { - delete keys[where]; - t.push(strfy(o[i], path+ '['+ i+ ']', builtInObjects, builtInPaths, seen, paths, cyclic, ademas)); - } - else { - t.push(''); - } - i++; - } + //Propiedades adicionales + keys.forEach(function(k) { + var p = '[\"' + k + '\"]'; + ademas.push(path + p + '= ' + strfy(o[k], path + p, builtInObjects, + builtInPaths, seen, paths, cyclic, ademas)); + }); + return '[' + t.join(',') + ']'; + } - if (t.length && (t[t.length- 1] === '')) { - t.push(''); - } + //functions + if (type === 'function') { + var defaultKeys = Object.getOwnPropertyNames(function() {}); + var nativeCode = (Object + '').replace('Object', ''); + var keys = Object.getOwnPropertyNames(o).filter(function(v, i, o) { + return (defaultKeys.indexOf(v) < 0); + }); - //Propiedades adicionales - keys.forEach(function (k) { - var p= '[\"'+ k+ '\"]'; - ademas.push(path+ p+ '= '+ strfy(o[k], path+ p, builtInObjects, builtInPaths, seen, paths, cyclic, ademas)); - }); - - - return '['+ t.join(',')+ ']'; - } + keys.forEach(function(k) { + var p = '[\"' + k + '\"]'; + ademas.push(path + p + '= ' + strfy(o[k], path + p, builtInObjects, + builtInPaths, seen, paths, cyclic, ademas)); + }); - //functions - if (type === 'function') { - var defaultKeys= Object.getOwnPropertyNames(function(){}); - var nativeCode = (Object+'').replace('Object',''); - var keys= Object.getOwnPropertyNames(o).filter(function (v,i,o) { - return (defaultKeys.indexOf(v) < 0); - }); - - keys.forEach(function (k) { - var p= '[\"'+ k+ '\"]'; - ademas.push(path+ p+ '= '+ strfy(o[k], path+ p, builtInObjects, builtInPaths, seen, paths, cyclic, ademas)); - }); - - if ((o+'').replace(o.name,'') === nativeCode) { - return (o+'').replace(/(\[native code\])/, '"$1";'); - } - - return (o.name) ? ''+ o : '('+ o+ ')'; + if ((o + '').replace(o.name, '') === nativeCode) { + return (o + '').replace(/(\[native code\])/, '"$1";'); } + return (o.name) ? '' + o : '(' + o + ')'; + } - //Dates - if (Date.prototype.isPrototypeOf(o)) { - var defaultKeys= Object.getOwnPropertyNames(new Date()); - var keys= Object.getOwnPropertyNames(o).filter(function (v,i,o) { - return (defaultKeys.indexOf(v) < 0); - }); - - keys.forEach(function (k) { - var p= '[\"'+ k+ '\"]'; - ademas.push(path+ p+ '= '+ strfy(o[k], path+ p, builtInObjects, builtInPaths, seen, paths, cyclic, ademas)); - }); - - return 'new Date('+ (+o)+ ')'; - } + //Dates + if (Date.prototype.isPrototypeOf(o)) { + var defaultKeys = Object.getOwnPropertyNames(new Date()); + var keys = Object.getOwnPropertyNames(o).filter(function(v, i, o) { + return (defaultKeys.indexOf(v) < 0); + }); - //Booleans - if (Boolean.prototype.isPrototypeOf(o)) { - var defaultKeys= Object.getOwnPropertyNames(new Boolean()); - var keys= Object.getOwnPropertyNames(o).filter(function (v,i,o) { - return (defaultKeys.indexOf(v) < 0); - }); - - keys.forEach(function (k) { - var p= '[\"'+ k+ '\"]'; - ademas.push(path+ p+ '= '+ strfy(o[k], path+ p, builtInObjects, builtInPaths, seen, paths, cyclic, ademas)); - }); - - return ''+ o; - } + keys.forEach(function(k) { + var p = '[\"' + k + '\"]'; + ademas.push(path + p + '= ' + strfy(o[k], path + p, builtInObjects, + builtInPaths, seen, paths, cyclic, ademas)); + }); + return 'new Date(' + (+o) + ')'; + } - //RegExps - if (RegExp.prototype.isPrototypeOf(o)) { - var defaultKeys= Object.getOwnPropertyNames(/a/); - var keys= Object.getOwnPropertyNames(o).filter(function (v,i,o) { - return (defaultKeys.indexOf(v) < 0); - }); - - keys.forEach(function (k) { - var p= '[\"'+ k+ '\"]'; - ademas.push(path+ p+ '= '+ strfy(o[k], path+ p, builtInObjects, builtInPaths, seen, paths, cyclic, ademas)); - }); - - return ''+ o; - } + //Booleans + if (Boolean.prototype.isPrototypeOf(o)) { + var defaultKeys = Object.getOwnPropertyNames(new Boolean()); + var keys = Object.getOwnPropertyNames(o).filter(function(v, i, o) { + return (defaultKeys.indexOf(v) < 0); + }); - //Objects - var defaultKeys= Object.getOwnPropertyNames({}); - var keys= Object.getOwnPropertyNames(o).filter(function (v,i,o) { + keys.forEach(function(k) { + var p = '[\"' + k + '\"]'; + ademas.push(path + p + '= ' + strfy(o[k], path + p, builtInObjects, + builtInPaths, seen, paths, cyclic, ademas)); + }); + return '' + o; + } + + //RegExps + if (RegExp.prototype.isPrototypeOf(o)) { + var defaultKeys = Object.getOwnPropertyNames(/a/); + var keys = Object.getOwnPropertyNames(o).filter(function(v, i, o) { return (defaultKeys.indexOf(v) < 0); }); - var t= []; - keys.forEach(function (k) { - t.push('\"'+ k+ '\":'+ strfy(o[k], path+ '[\"'+ k+ '\"]', builtInObjects, builtInPaths, seen, paths, cyclic, ademas)); + keys.forEach(function(k) { + var p = '[\"' + k + '\"]'; + ademas.push(path + p + '= ' + strfy(o[k], path + p, builtInObjects, + builtInPaths, seen, paths, cyclic, ademas)); }); - return '{'+ t.join(',')+ '}'; + return '' + o; } + //Objects + var defaultKeys = Object.getOwnPropertyNames({}); + var keys = Object.getOwnPropertyNames(o).filter(function(v, i, o) { + return (defaultKeys.indexOf(v) < 0); + }); + + var t = []; + keys.forEach(function(k) { + t.push('\"' + k + '\":' + strfy(o[k], path + '[\"' + k + '\"]', + builtInObjects, builtInPaths, seen, paths, cyclic, ademas)); + }); + return '{' + t.join(',') + '}'; +} + +module.exports.stringify = function (o) { + var cyclic, ademas; + var r = strfy(o, 'o', builtInObjects, builtInPaths, [], [], cyclic = [], + ademas = []); + + if (cyclic.length || ademas.length) { - function parse (t) { - var globalEval= eval; - return globalEval( '('+ t+ ')' ); + var txt = '(function(o){\n'; + + if (ademas.length) { + txt += "/* Additional properties */\n" + ademas.join(';\n') + ';\n'; + } + + if (cyclic.length) { + txt += "/* Cyclic properties */\n" + cyclic.join(';\n') + ';\n'; + } + + return txt + 'return o;\n})(' + r + ')'; } - - parse.this= exports; - stringify.this= exports; - exports._strfy= strfy; - exports._builtInPaths= builtInPaths; - exports._builtInObjects= builtInObjects; - exports.parse= parse; - exports.stringify= stringify; - return exports; -})(typeof exports !== 'undefined' ? exports : {}); \ No newline at end of file + + return r; +}; + +module.exports.parse = function (t) { + var globalEval = eval; + return globalEval('(' + t + ')'); +}; diff --git a/package.json b/package.json index 6124942..886563a 100644 --- a/package.json +++ b/package.json @@ -1,20 +1,28 @@ { "name": "JASON", - "version": "0.1.3", - "main": "index.js", "description": "JSON-like parser/formatter to pass objects between processes and/or threads", + "version": "0.1.3", "author": { - "name": "Jorge Chamorro Bieling", - "email": "jorge@jorgechamorro.com", + "name": "Jorge Chamorro Bieling", + "email": "jorge@jorgechamorro.com", "twitter": "@jorgechamorro" }, - "homepage": "https://github.com/xk/JASON", "bugs": { - "url": "http://github.com/xk/JASON/issues", - "email": "jorge@jorgechamorro.com" + "url": "http://github.com/xk/JASON/issues", + "email": "jorge@jorgechamorro.com" }, + "homepage": "https://github.com/xk/JASON", + "main": "index.js", "repository": { "type": "git", "url": "http://github.com/xk/JASON.git" + }, + "scripts": { + "test": "mocha" + }, + "devDependencies": { + "coffee-script": "^1.7.1", + "mocha": "^1.20.1", + "should": "^4.0.4" } } diff --git a/readme.md b/readme.md index 6f91d82..a23c69a 100644 --- a/readme.md +++ b/readme.md @@ -1,48 +1,45 @@ -## JASON +# JASON +JASON is just like JSON, but with an A. D'oh. -JASON is just like JSON, but with an A. D'oh. ^U +[![npm](http://img.shields.io/npm/v/JASON.svg?style=flat)](http://badge.fury.io/js/JASON) +[![tests](http://img.shields.io/travis/slang800/JASON/master.svg?style=flat)](https://travis-ci.org/slang800/JASON) JASON is just like JSON, but unlike JSON it can: -* serialize objects with methods -* serialize objects with cyclic references -* understand Dates, Regexps, Booleans, etc, and restore them with `.parse()` with their proper types/classes. -* understand and serialize all the JS primitives, including `undefined` -* properly recreate the holes in Arrays +- serialize objects with methods +- serialize objects with cyclic references +- understand Dates, Regexps, Booleans, etc, and restore them with `.parse()` with their proper types/classes. +- understand and serialize all the JS primitives, including `undefined` +- properly recreate the holes in Arrays JASON lets you pass objects as text between processes and/or threads. Warning: unlike JSON, JASON is *unsafe*. You should only use it in contexts where you have strong guarantees that the strings that you pass to the JASON parser have been produced by a JASON formatter from a trusted source. ## Syntax - -JASON syntax is just plain JavaScript (but not JSON). +JASON syntax is just plain JavaScript (but not JSON). The `stringify` function does the clever work of generating whatever Javascript is needed to recreate the object, and the `parse` function is just a call to `eval`. ## Examples - -See the `test/test01.js` file. +See `test/index.js`. ## API - -``` javascript +```javascript var JASON = require("JASON"); str = JASON.stringify(obj); obj = JASON.parse(str); ``` -# Installation - +## Installation The easiest way to install `JASON` is with NPM: ```sh npm install JASON ``` -# Caveats - +## Caveats JASON won't capture any free vars' values in the serializations. If a JASON serialization contains any functions or methods that contain references to free vars (any other than the built-in globals), most of the times bad things will happen, unless the referenced free vars exist as well and are in scope in the context in which the serialization is JASON.parse()d (the global context), or unless the serialization is eval()ed in a context in which said references exist and are in scope. For example here `pi` is a free var: @@ -69,5 +66,4 @@ JASON.parse(txt)(); ``` ## License - This work is licensed under the [MIT license](http://en.wikipedia.org/wiki/MIT_License). diff --git a/test/index.coffee b/test/index.coffee new file mode 100644 index 0000000..a6bb2f1 --- /dev/null +++ b/test/index.coffee @@ -0,0 +1,219 @@ +JASON = require '../' +should = require 'should' + +describe 'primitives', -> + it 'should handle ints', -> + o = 27 + stringified = JASON.stringify(o) + should(o).eql(JASON.parse(stringified)) + + it 'should handle null', -> + o = null + stringified = JASON.stringify(o) + should(o).eql(JASON.parse(stringified)) + + it 'should handle undefined', -> + o = undefined + stringified = JASON.stringify(o) + should(o).eql(JASON.parse(stringified)) + + it 'should handle strings', -> + o = "\\string\n\t\r\"" + stringified = JASON.stringify(o) + should(o).eql(JASON.parse(stringified)) + + it 'should handle boolean', -> + o = true + stringified = JASON.stringify(o) + should(o).eql(JASON.parse(stringified)) + + o = false + stringified = JASON.stringify(o) + should(o).eql(JASON.parse(stringified)) + +describe 'standard built-in types', -> + it 'should handle regexes', -> + o = /a/ + stringified = JASON.stringify(o) + should(o).eql(JASON.parse(stringified)) + + o = /a/g + stringified = JASON.stringify(o) + should(o).eql(JASON.parse(stringified)) + + it 'should handle regexes', -> + o = new Date() + stringified = JASON.stringify(o) + should(o).eql(JASON.parse(stringified)) + + it 'should handle objects', -> + o = {} + stringified = JASON.stringify(o) + stringified.should.eql '{}' + should(o).eql(JASON.parse(stringified)) + + it 'should handle arrays', -> + o = [] + stringified = JASON.stringify(o) + stringified.should.eql '[]' + should(o).eql(JASON.parse(stringified)) + + it 'should handle arrays w/ holes', -> + o = `[, ]` + stringified = JASON.stringify(o) + stringified.should.eql '[,]' + should(o).eql(JASON.parse(stringified)) + + o = `[,,]` + stringified = JASON.stringify(o) + stringified.should.eql '[,,]' + should(o).eql(JASON.parse(stringified)) + + it 'should handle mixed objects', -> + o = [ + 27 + null + undefined + "string" + true + false + {} + [] + `[,]` + `[,,]` + ] + stringified = JASON.stringify(o) + stringified.should.eql( + '[27,null,undefined,\"string\",true,false,{},[],[,],[,,]]' + ) + should(o).eql(JASON.parse(stringified)) + + it 'should handle anon functions', -> + o = (->) + stringified = JASON.stringify(o) + stringified.should.eql '(function () {})' + + it 'should handle named functions', -> + `function namedFunction() {}` + o = namedFunction + stringified = JASON.stringify(o) + stringified.should.eql 'function namedFunction() {}' + + it 'should handle named functions in various objects', -> + `function namedFunction() {}` + o = [namedFunction] + stringified = JASON.stringify(o) + stringified.should.eql '[function namedFunction() {}]' + + o = [namedFunction, ->] + stringified = JASON.stringify(o) + stringified.should.eql '[function namedFunction() {},(function () {})]' + + o = fn: namedFunction + stringified = JASON.stringify(o) + stringified.should.eql '{"fn":function namedFunction() {}}' + +describe 'cyclic', -> + it 'should handle multiple array keys referencing the same function', -> + `function fn() {}` + o = [fn, fn] + stringified = JASON.stringify(o) + stringified.should.eql """ + (function(o){ + /* Cyclic properties */ + o[1]= o[0]; + return o; + })([function fn() {},"&o[0]"]) + """ + parsed = JASON.parse(stringified) + parsed.should.be.instanceOf(Array) + parsed[0].should.eql parsed[1] + parsed[0].name.should.eql 'fn' + parsed[1].name.should.eql 'fn' + parsed[0].should.be.type 'function' + parsed[1].should.be.type 'function' + + it 'should handle multiple properties referencing the same function', -> + `function fn() {}` + o = + a: fn + b: fn + stringified = JASON.stringify(o) + stringified.should.eql """ + (function(o){ + /* Cyclic properties */ + o["b"]= o["a"]; + return o; + })({"a":function fn() {},"b":"&o[a]"}) + """ + parsed = JASON.parse(stringified) + parsed.should.be.type 'object' + parsed['a'].should.eql parsed['b'] + parsed['a'].name.should.eql 'fn' + parsed['b'].name.should.eql 'fn' + parsed['a'].should.be.type 'function' + parsed['b'].should.be.type 'function' + + + it 'should handle self-referencing objects', -> + `function fn() {}` + o = + a: fn + b: fn + o = [ + o + fn + [ + o + fn + ] + ] + stringified = JASON.stringify(o) + stringified.should.eql """ + (function(o){ + /* Cyclic properties */ + o[0]["b"]= o[0]["a"]; + o[1]= o[0]["a"]; + o[2][0]= o[0]; + o[2][1]= o[0]["a"]; + return o; + })([{"a":function fn() {},"b":"&o[0][a]"},"&o[0][a]",["&o[0]","&o[0][a]"]]) + """ + parsed = JASON.parse(stringified) + parsed.should.be.instanceOf(Array) + parsed[0].should.be.type 'object' + parsed[1].should.be.type 'function' + parsed[1].name.should.eql 'fn' + parsed[2].should.be.instanceOf(Array) + parsed[0].should.eql parsed[2][0] + parsed[1].should.eql parsed[2][1] + parsed[0].a.should.eql parsed[0].b + parsed[0].a.should.eql parsed[1] + parsed[0].a.should.eql parsed[2][1] + +describe 'built-ins', -> + it 'should handle built-ins', -> + o = [ + Object + Array + Object.prototype + Array::push + ] + stringified = JASON.stringify(o) + parsed = JASON.parse(stringified) + parsed.should.eql o + +describe 'misc', -> + it 'should reset additional properties between calls', -> + `function fn() {}` + fn.k = 27 + o = fn + stringified = JASON.stringify(o) + parsed = JASON.parse(stringified) + should(parsed.k).eql 27 + + delete fn.k + o = fn + stringified = JASON.stringify(o) + parsed = JASON.parse(stringified) + should(parsed.k).eql undefined diff --git a/test/mocha.opts b/test/mocha.opts new file mode 100644 index 0000000..2da4071 --- /dev/null +++ b/test/mocha.opts @@ -0,0 +1,2 @@ +--reporter spec +--compilers coffee:coffee-script/register diff --git a/test/test01.js b/test/test01.js deleted file mode 100644 index 03891f3..0000000 --- a/test/test01.js +++ /dev/null @@ -1,245 +0,0 @@ - - -var JASON = require('JASON'); - - -function assert (c,d) { - if (c === d || (d === undefined && !!c === true)) { - console.log("ASSERTION -> OK"); - - } else if (c !== d) { - var err = new Error("Assertion FAILED"); - var info = err.stack.split('\n').slice(2,3)[0].slice(0,-1).split(':').slice(-2); - err.stack = [ - ' '+{}.toString.call(c), - '____________________________________', - '', - c, - '', - '------------- NOT EQUAL ------------', - '', - d, - '', - '_____________________________________', - ' '+{}.toString.call(d), - '', - 'Line ' + info[0] + ', Column ' + info[1] - ].join('\n') - throw err; - } -} - - -//Primitives -console.log("\n\n******* Primitives\n\n"); - -var o= 27; -var oo= JASON.stringify(o); -console.log(o); -process.stdout.write(oo+ '\n'); -assert(o, JASON.parse(oo)); - - -o= null; -oo= JASON.stringify(o); -console.log(o); -process.stdout.write(oo+ '\n'); -assert(o, JASON.parse(oo)); - - -o= undefined; -oo= JASON.stringify(o); -console.log(o); -process.stdout.write(oo+ '\n'); -assert(o, JASON.parse(oo)); - - -o= '\\string\n\t\r"'; -oo= JASON.stringify(o); -console.log(o); -process.stdout.write(oo+ '\n'); -assert(o, JASON.parse(oo)); - - -o= true; -oo= JASON.stringify(o); -console.log(o); -process.stdout.write(oo+ '\n'); -assert(o, JASON.parse(oo)); - - -o= false; -oo= JASON.stringify(o); -console.log(o); -process.stdout.write(oo+ '\n'); -assert(o, JASON.parse(oo)); - - - - -//Standard built-in types -console.log("\n\n******* STANDARD BUILT-IN TYPES\n\n"); - -o= /a/; -oo= JASON.stringify(o); -console.log(o); -process.stdout.write(oo+ '\n'); -assert(o.toString(), (JASON.parse(oo)).toString()); - -o= /a/g; -oo= JASON.stringify(o); -console.log(o); -process.stdout.write(oo+ '\n'); -assert(o.toString(), (JASON.parse(oo)).toString()); - -o= new Date(); -oo= JASON.stringify(o); -console.log(o); -process.stdout.write(oo+ '\n'); -assert(+o, +JASON.parse(oo)); - -o= {}; -oo= JASON.stringify(o); -console.log(o); -process.stdout.write(oo+ '\n'); -assert(oo, '{}'); - -o= []; -oo= JASON.stringify(o); -console.log(o); -process.stdout.write(oo+ '\n'); -assert(oo, '[]'); - - -o= [,]; -oo= JASON.stringify(o); -console.log(o); -process.stdout.write(oo+ '\n'); -assert(oo, '[,]'); - - -o= [,,]; -oo= JASON.stringify(o); -console.log(o); -process.stdout.write(oo+ '\n'); -assert(oo, '[,,]'); - - -o= [27,null,undefined,"string",true,false,{},[],[,],[,,]]; -oo= JASON.stringify(o); -console.log(o); -process.stdout.write(oo+ '\n'); -assert(oo, '[27,null,undefined,"string",true,false,{},[],[,],[,,]]'); - - -o= function(){}; -oo= JASON.stringify(o); -console.log(o); -process.stdout.write(oo+ '\n'); -assert(oo, '(function (){})'); - -function ƒ (){}; -o= ƒ; -oo= JASON.stringify(o); -console.log(o); -process.stdout.write(oo+ '\n'); -assert(oo, 'function ƒ(){}'); - - -o= [ƒ,]; -oo= JASON.stringify(o); -console.log(o); -process.stdout.write(oo+ '\n'); -assert(oo, '[function ƒ(){}]'); - - -o= [ƒ,function(){},]; -oo= JASON.stringify(o); -console.log(o); -process.stdout.write(oo+ '\n'); -assert(oo, '[function ƒ(){},(function (){})]'); - - -o= {ƒ:ƒ}; -oo= JASON.stringify(o); -console.log(o); -process.stdout.write(oo+ '\n'); -assert(oo, '{"ƒ":function ƒ(){}}'); - -//Cyclic -console.log("\n\n******* CYCLIC\n\n"); - -o= [ƒ,ƒ]; -oo= JASON.stringify(o); -console.log(o); -process.stdout.write(oo+ '\n'); -//assert(oo, '(function(o){\n//Cyclic properties\no[1]= o[0]\nreturn o;\n})([function ƒ(){},"o[0]"])'); -oo= JASON.parse(oo); -assert(Array.isArray(oo)); -assert(typeof oo[0] === 'function'); -assert(oo[0].name, "ƒ"); -assert(oo[0], oo[1]); - -o= {a:ƒ, b:ƒ}; -oo= JASON.stringify(o); -console.log(o); -process.stdout.write(oo+ '\n'); -//assert(oo, '(function(o){\n//Cyclic properties\no["a"]= o["b"]\nreturn o;\n})({"b":function ƒ(){},"a":"o[b]"})'); -oo= JASON.parse(oo); -assert(typeof oo === 'object'); -assert(oo.a, oo.b); -assert(typeof oo.a === 'function'); -assert(oo.a.name, "ƒ"); - - -o= {a:ƒ, b:ƒ}; -o= [o,ƒ,[o,ƒ]]; -oo= JASON.stringify(o); -console.log(o); -process.stdout.write(oo+ '\n'); -oo= JASON.parse(oo); -assert(Array.isArray(oo)); -assert(typeof oo[0] === 'object'); -assert(typeof oo[1] === 'function'); -assert(oo[1].name, "ƒ"); -assert(Array.isArray(oo[2])); -assert(oo[0], oo[2][0]); -assert(oo[1], oo[2][1]); -assert((oo[0].a === oo[0].b) && (oo[0].a === oo[1]) && (oo[0].a, oo[2][1])); - -//Built-ins -console.log("\n\n******* BUILT-INS\n\n"); - -o= [Object, Array, Object.prototype, Array.prototype.push]; -oo= JASON.stringify(o); -console.log(o); -process.stdout.write(oo+ '\n'); -oo= JASON.parse(oo); -assert(oo[0], Object); -assert(oo[1], Array); -assert(oo[2], Object.prototype); -assert(oo[3], Array.prototype.push); - -//Additional properties - -//Issue #1 BEGIN -console.log("\n\n******* Issue #1\n\n"); - -ƒ.k= 27; -o= ƒ; -oo= JASON.stringify(o); -console.log(o); -process.stdout.write(oo+ '\n'); -oo= JASON.parse(oo); -assert(oo.k, 27); - - -delete ƒ.k; -o= ƒ; -oo= JASON.stringify(o); -console.log(o); -process.stdout.write(oo+ '\n'); -oo= JASON.parse(oo); -assert(oo.k, undefined); - -//Issue #1 END