Skip to content

Commit

Permalink
wrap JSON.stringify instead of adding .toJSON to `Symbol.prototyp…
Browse files Browse the repository at this point in the history
…e` - little more correct
  • Loading branch information
zloirock committed Oct 1, 2015
1 parent 0258003 commit 1ff357a
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 21 deletions.
4 changes: 4 additions & 0 deletions fn/json/stringify.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
var core = require('../../modules/$.core');
module.exports = function stringify(it){
return (core.JSON && core.JSON.stringify || JSON.stringify).apply(JSON, arguments);
};
4 changes: 4 additions & 0 deletions library/fn/json/stringify.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
var core = require('../../modules/$.core');
module.exports = function stringify(it){
return (core.JSON && core.JSON.stringify || JSON.stringify).apply(JSON, arguments);
};
44 changes: 36 additions & 8 deletions library/modules/es6.symbol.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ var $ = require('./$')
, keyOf = require('./$.keyof')
, $names = require('./$.get-names')
, enumKeys = require('./$.enum-keys')
, isArray = require('./$.is-array')
, isObject = require('./$.is-object')
, anObject = require('./$.an-object')
, toIObject = require('./$.to-iobject')
Expand All @@ -23,6 +24,8 @@ var $ = require('./$')
, _create = $.create
, getNames = $names.get
, $Symbol = global.Symbol
, $JSON = global.JSON
, _stringify = $JSON && $JSON.stringify
, setter = false
, HIDDEN = wks('_hidden')
, isEnum = $.isEnum
Expand Down Expand Up @@ -56,6 +59,10 @@ var wrap = function(tag){
return sym;
};

var isSymbol = function(it){
return typeof it == 'symbol';
};

var $defineProperty = function defineProperty(it, key, D){
if(D && has(AllSymbols, key)){
if(!D.enumerable){
Expand Down Expand Up @@ -105,17 +112,42 @@ var $getOwnPropertySymbols = function getOwnPropertySymbols(it){
while(names.length > i)if(has(AllSymbols, key = names[i++]))result.push(AllSymbols[key]);
return result;
};
var $stringify = function stringify(it){
var args = [it]
, i = 1
, replacer, $replacer;
while(arguments.length > i)args.push(arguments[i++]);
replacer = args[1];
if(typeof replacer == 'function')$replacer = replacer;
if($replacer || !isArray(replacer))replacer = function(key, value){
if($replacer)value = $replacer.call(this, key, value);
if(!isSymbol(value))return value;
}
args[1] = replacer;
return _stringify.apply($JSON, args);
};
var buggyJSON = $fails(function(){
var S = $Symbol();
// MS Edge converts symbol values to JSON as {}
// WebKit converts symbol values to JSON as null
// V8 throws on boxed symbols
return _stringify([S]) != '[null]' || _stringify({a: S}) != '{}' || _stringify(Object(S)) != '{}';
});

// 19.4.1.1 Symbol([description])
if(!useNative){
$Symbol = function Symbol(){
if(this instanceof $Symbol)throw TypeError('Symbol is not a constructor');
if(isSymbol(this))throw TypeError('Symbol is not a constructor');
return wrap(uid(arguments[0]));
};
$redef($Symbol.prototype, 'toString', function toString(){
return this._k;
});

isSymbol = function(it){
return it instanceof $Symbol;
};

$.create = $create;
$.isEnum = $propertyIsEnumerable;
$.getDesc = $getOwnPropertyDescriptor;
Expand All @@ -129,13 +161,6 @@ if(!useNative){
}
}

// MS Edge converts symbol values to JSON as {}
if(!useNative || $fails(function(){
return JSON.stringify([$Symbol()]) != '[null]';
}))$redef($Symbol.prototype, 'toJSON', function toJSON(){
if(useNative && isObject(this))return this;
});

var symbolStatics = {
// 19.4.2.1 Symbol.for(key)
'for': function(key){
Expand Down Expand Up @@ -191,6 +216,9 @@ $def($def.S + $def.F * !useNative, 'Object', {
getOwnPropertySymbols: $getOwnPropertySymbols
});

// 24.3.2 JSON.stringify(value [, replacer [, space]])
$JSON && $def($def.S + $def.F * (!useNative || buggyJSON), 'JSON', {stringify: $stringify});

// 19.4.3.5 Symbol.prototype[@@toStringTag]
setTag($Symbol, 'Symbol');
// 20.2.1.9 Math[@@toStringTag]
Expand Down
44 changes: 36 additions & 8 deletions modules/es6.symbol.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ var $ = require('./$')
, keyOf = require('./$.keyof')
, $names = require('./$.get-names')
, enumKeys = require('./$.enum-keys')
, isArray = require('./$.is-array')
, isObject = require('./$.is-object')
, anObject = require('./$.an-object')
, toIObject = require('./$.to-iobject')
Expand All @@ -23,6 +24,8 @@ var $ = require('./$')
, _create = $.create
, getNames = $names.get
, $Symbol = global.Symbol
, $JSON = global.JSON
, _stringify = $JSON && $JSON.stringify
, setter = false
, HIDDEN = wks('_hidden')
, isEnum = $.isEnum
Expand Down Expand Up @@ -56,6 +59,10 @@ var wrap = function(tag){
return sym;
};

var isSymbol = function(it){
return typeof it == 'symbol';
};

var $defineProperty = function defineProperty(it, key, D){
if(D && has(AllSymbols, key)){
if(!D.enumerable){
Expand Down Expand Up @@ -105,17 +112,42 @@ var $getOwnPropertySymbols = function getOwnPropertySymbols(it){
while(names.length > i)if(has(AllSymbols, key = names[i++]))result.push(AllSymbols[key]);
return result;
};
var $stringify = function stringify(it){
var args = [it]
, i = 1
, replacer, $replacer;
while(arguments.length > i)args.push(arguments[i++]);
replacer = args[1];
if(typeof replacer == 'function')$replacer = replacer;
if($replacer || !isArray(replacer))replacer = function(key, value){
if($replacer)value = $replacer.call(this, key, value);
if(!isSymbol(value))return value;
}
args[1] = replacer;
return _stringify.apply($JSON, args);
};
var buggyJSON = $fails(function(){
var S = $Symbol();
// MS Edge converts symbol values to JSON as {}
// WebKit converts symbol values to JSON as null
// V8 throws on boxed symbols
return _stringify([S]) != '[null]' || _stringify({a: S}) != '{}' || _stringify(Object(S)) != '{}';
});

// 19.4.1.1 Symbol([description])
if(!useNative){
$Symbol = function Symbol(){
if(this instanceof $Symbol)throw TypeError('Symbol is not a constructor');
if(isSymbol(this))throw TypeError('Symbol is not a constructor');
return wrap(uid(arguments[0]));
};
$redef($Symbol.prototype, 'toString', function toString(){
return this._k;
});

isSymbol = function(it){
return it instanceof $Symbol;
};

$.create = $create;
$.isEnum = $propertyIsEnumerable;
$.getDesc = $getOwnPropertyDescriptor;
Expand All @@ -129,13 +161,6 @@ if(!useNative){
}
}

// MS Edge converts symbol values to JSON as {}
if(!useNative || $fails(function(){
return JSON.stringify([$Symbol()]) != '[null]';
}))$redef($Symbol.prototype, 'toJSON', function toJSON(){
if(useNative && isObject(this))return this;
});

var symbolStatics = {
// 19.4.2.1 Symbol.for(key)
'for': function(key){
Expand Down Expand Up @@ -191,6 +216,9 @@ $def($def.S + $def.F * !useNative, 'Object', {
getOwnPropertySymbols: $getOwnPropertySymbols
});

// 24.3.2 JSON.stringify(value [, replacer [, space]])
$JSON && $def($def.S + $def.F * (!useNative || buggyJSON), 'JSON', {stringify: $stringify});

// 19.4.3.5 Symbol.prototype[@@toStringTag]
setTag($Symbol, 'Symbol');
// 20.2.1.9 Math[@@toStringTag]
Expand Down
7 changes: 4 additions & 3 deletions tests/library/es6.symbol.ls
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{module, test} = QUnit
module \ES6

{Symbol} = core
{Symbol, JSON} = core
{defineProperty, getOwnPropertyDescriptor, create} = core.Object
isFunction = -> typeof! it is \Function
isNative = -> /\[native code\]\s*\}\s*$/.test it
Expand Down Expand Up @@ -53,9 +53,10 @@ test 'Object.getOwnPropertySymbols' (assert)->
if JSON?
test 'Symbols & JSON.stringify' (assert)->
assert.strictEqual JSON.stringify([1, Symbol(\foo), no, Symbol(\bar), {}]), '[1,null,false,null,{}]', 'array value'
# early WebKit implementation returns '{"foo":null}', it can't be shimmed w/o completely replacement JSON.stringify, but already fixed in dev versions, temporary disable this test
# assert.strictEqual JSON.stringify({foo: Symbol \foo}), '{}', 'object value'
assert.strictEqual JSON.stringify({foo: Symbol \foo}), '{}', 'object value'
if descriptors => assert.strictEqual JSON.stringify({(Symbol(\foo)): 1, bar: 2}), '{"bar":2}', 'object key'
assert.strictEqual JSON.stringify(Symbol \foo), void, 'symbol value'
if typeof Symbol! is \symbol => assert.strictEqual JSON.stringify(Object Symbol \foo), '{}', 'boxed symbol'

if descriptors
test 'Symbols & descriptors' (assert)->
Expand Down
5 changes: 3 additions & 2 deletions tests/tests/es6.symbol.ls
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,10 @@ test 'Object.getOwnPropertySymbols' (assert)->
if JSON?
test 'Symbols & JSON.stringify' (assert)->
assert.strictEqual JSON.stringify([1, Symbol(\foo), no, Symbol(\bar), {}]), '[1,null,false,null,{}]', 'array value'
# early WebKit implementation returns '{"foo":null}', it can't be shimmed w/o completely replacement JSON.stringify, but already fixed in dev versions, temporary disable this test
# assert.strictEqual JSON.stringify({foo: Symbol \foo}), '{}', 'object value'
assert.strictEqual JSON.stringify({foo: Symbol \foo}), '{}', 'object value'
if descriptors => assert.strictEqual JSON.stringify({(Symbol(\foo)): 1, bar: 2}), '{"bar":2}', 'object key'
assert.strictEqual JSON.stringify(Symbol \foo), void, 'symbol value'
if typeof Symbol! is \symbol => assert.strictEqual JSON.stringify(Object Symbol \foo), '{}', 'boxed symbol'

if descriptors
test 'Symbols & descriptors' (assert)->
Expand Down

0 comments on commit 1ff357a

Please sign in to comment.