You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
403 lines
12 KiB
403 lines
12 KiB
var escapeJsStr = require('./escapeJsStr'); |
|
var type = require('./type'); |
|
var toStr = require('./toStr'); |
|
var endWith = require('./endWith'); |
|
var toSrc = require('./toSrc'); |
|
var keys = require('./keys'); |
|
var each = require('./each'); |
|
var Class = require('./Class'); |
|
var getProto = require('./getProto'); |
|
var difference = require('./difference'); |
|
var extend = require('./extend'); |
|
var isPromise = require('./isPromise'); |
|
var filter = require('./filter'); |
|
var now = require('./now'); |
|
var allKeys = require('./allKeys'); |
|
var contain = require('./contain'); |
|
var isObj = require('./isObj'); |
|
var isMiniProgram = require('./isMiniProgram'); |
|
var create = require('./create'); |
|
var startWith = require('./startWith'); |
|
var safeSet = require('./safeSet'); |
|
var defineProp = require('./defineProp'); |
|
var pick = require('./pick'); |
|
var isArrLike = require('./isArrLike'); |
|
exports = function(obj) { |
|
var _ref = |
|
arguments.length > 1 && arguments[1] !== undefined |
|
? arguments[1] |
|
: {}, |
|
self = _ref.self, |
|
_ref$startTime = _ref.startTime, |
|
startTime = _ref$startTime === void 0 ? now() : _ref$startTime, |
|
_ref$timeout = _ref.timeout, |
|
timeout = _ref$timeout === void 0 ? 0 : _ref$timeout, |
|
_ref$depth = _ref.depth, |
|
depth = _ref$depth === void 0 ? 0 : _ref$depth, |
|
_ref$curDepth = _ref.curDepth, |
|
curDepth = _ref$curDepth === void 0 ? 1 : _ref$curDepth, |
|
_ref$visitor = _ref.visitor, |
|
visitor = _ref$visitor === void 0 ? new Visitor() : _ref$visitor, |
|
_ref$unenumerable = _ref.unenumerable, |
|
unenumerable = _ref$unenumerable === void 0 ? false : _ref$unenumerable, |
|
_ref$symbol = _ref.symbol, |
|
symbol = _ref$symbol === void 0 ? false : _ref$symbol, |
|
_ref$accessGetter = _ref.accessGetter, |
|
accessGetter = _ref$accessGetter === void 0 ? false : _ref$accessGetter, |
|
_ref$ignore = _ref.ignore, |
|
ignore = _ref$ignore === void 0 ? [] : _ref$ignore; |
|
var json = ''; |
|
var options = { |
|
visitor: visitor, |
|
unenumerable: unenumerable, |
|
symbol: symbol, |
|
accessGetter: accessGetter, |
|
depth: depth, |
|
curDepth: curDepth + 1, |
|
timeout: timeout, |
|
startTime: startTime, |
|
ignore: ignore |
|
}; |
|
var t = type(obj, false); |
|
if (t === 'String') { |
|
json = wrapStr(obj); |
|
} else if (t === 'Number') { |
|
json = toStr(obj); |
|
if (endWith(json, 'Infinity')) { |
|
json = '{"value":"'.concat(json, '","type":"Number"}'); |
|
} |
|
} else if (t === 'NaN') { |
|
json = '{"value":"NaN","type":"Number"}'; |
|
} else if (t === 'Boolean') { |
|
json = obj ? 'true' : 'false'; |
|
} else if (t === 'Null') { |
|
json = 'null'; |
|
} else if (t === 'Undefined') { |
|
json = '{"type":"Undefined"}'; |
|
} else if (t === 'Symbol') { |
|
var val = 'Symbol'; |
|
try { |
|
val = toStr(obj); |
|
} catch (e) {} |
|
json = '{"value":'.concat(wrapStr(val), ',"type":"Symbol"}'); |
|
} else { |
|
if (timeout && now() - startTime > timeout) { |
|
return wrapStr('Timeout'); |
|
} |
|
if (depth && curDepth > depth) { |
|
return wrapStr('{...}'); |
|
} |
|
json = '{'; |
|
var parts = []; |
|
var visitedObj = visitor.get(obj); |
|
var id; |
|
if (visitedObj) { |
|
id = visitedObj.id; |
|
parts.push('"reference":'.concat(id)); |
|
} else { |
|
id = visitor.set(obj); |
|
parts.push('"id":'.concat(id)); |
|
} |
|
parts.push('"type":"'.concat(t, '"')); |
|
if (endWith(t, 'Function')) { |
|
parts.push('"value":'.concat(wrapStr(toSrc(obj)))); |
|
} else if (t === 'RegExp') { |
|
parts.push('"value":'.concat(wrapStr(obj))); |
|
} |
|
if (!visitedObj) { |
|
var enumerableKeys = keys(obj); |
|
if (enumerableKeys.length) { |
|
parts.push( |
|
iterateObj( |
|
'enumerable', |
|
enumerableKeys, |
|
self || obj, |
|
options |
|
) |
|
); |
|
} |
|
if (unenumerable) { |
|
var unenumerableKeys = difference( |
|
allKeys(obj, { |
|
prototype: false, |
|
unenumerable: true |
|
}), |
|
enumerableKeys |
|
); |
|
if (unenumerableKeys.length) { |
|
parts.push( |
|
iterateObj( |
|
'unenumerable', |
|
unenumerableKeys, |
|
self || obj, |
|
options |
|
) |
|
); |
|
} |
|
} |
|
if (symbol) { |
|
var symbolKeys = filter( |
|
allKeys(obj, { |
|
prototype: false, |
|
symbol: true |
|
}), |
|
function(key) { |
|
return typeof key === 'symbol'; |
|
} |
|
); |
|
if (symbolKeys.length) { |
|
parts.push( |
|
iterateObj('symbol', symbolKeys, self || obj, options) |
|
); |
|
} |
|
} |
|
var prototype = getProto(obj); |
|
if (prototype && !contain(ignore, prototype)) { |
|
var proto = '"proto":'.concat( |
|
exports( |
|
prototype, |
|
extend(options, { |
|
self: self || obj |
|
}) |
|
) |
|
); |
|
parts.push(proto); |
|
} |
|
} |
|
json += parts.join(',') + '}'; |
|
} |
|
return json; |
|
}; |
|
function iterateObj(name, keys, obj, options) { |
|
var parts = []; |
|
each(keys, function(key) { |
|
var val; |
|
var descriptor = Object.getOwnPropertyDescriptor(obj, key); |
|
var hasGetter = descriptor && descriptor.get; |
|
var hasSetter = descriptor && descriptor.set; |
|
if (!options.accessGetter && hasGetter) { |
|
val = '(...)'; |
|
} else { |
|
try { |
|
val = obj[key]; |
|
if (contain(options.ignore, val)) { |
|
return; |
|
} |
|
if (isPromise(val)) { |
|
val.catch(function() {}); |
|
} |
|
} catch (e) { |
|
val = e.message; |
|
} |
|
} |
|
parts.push(''.concat(wrapKey(key), ':').concat(exports(val, options))); |
|
if (hasGetter) { |
|
parts.push( |
|
'' |
|
.concat(wrapKey('get ' + toStr(key)), ':') |
|
.concat(exports(descriptor.get, options)) |
|
); |
|
} |
|
if (hasSetter) { |
|
parts.push( |
|
'' |
|
.concat(wrapKey('set ' + toStr(key)), ':') |
|
.concat(exports(descriptor.set, options)) |
|
); |
|
} |
|
}); |
|
return '"'.concat(name, '":{') + parts.join(',') + '}'; |
|
} |
|
function wrapKey(key) { |
|
return '"'.concat(escapeJsonStr(key), '"'); |
|
} |
|
function wrapStr(str) { |
|
return '"'.concat(escapeJsonStr(toStr(str)), '"'); |
|
} |
|
function escapeJsonStr(str) { |
|
return escapeJsStr(str) |
|
.replace(/\\'/g, "'") |
|
.replace(/\t/g, '\\t'); |
|
} |
|
var Visitor = Class({ |
|
initialize: function() { |
|
this.id = 1; |
|
this.visited = []; |
|
}, |
|
set: function(val) { |
|
var visited = this.visited, |
|
id = this.id; |
|
var obj = { |
|
id: id, |
|
val: val |
|
}; |
|
visited.push(obj); |
|
this.id++; |
|
return id; |
|
}, |
|
get: function(val) { |
|
var visited = this.visited; |
|
for (var i = 0, len = visited.length; i < len; i++) { |
|
var obj = visited[i]; |
|
if (val === obj.val) return obj; |
|
} |
|
return false; |
|
} |
|
}); |
|
exports.parse = function(str) { |
|
var map = {}; |
|
var obj = parse(JSON.parse(str), { |
|
map: map |
|
}); |
|
correctReference(map); |
|
return obj; |
|
}; |
|
function correctReference(map) { |
|
each(map, function(obj) { |
|
var enumerableKeys = keys(obj); |
|
for (var i = 0, len = enumerableKeys.length; i < len; i++) { |
|
var key = enumerableKeys[i]; |
|
if (isObj(obj[key])) { |
|
var reference = obj[key].reference; |
|
if (reference && map[reference]) { |
|
obj[key] = map[reference]; |
|
} |
|
} |
|
} |
|
var proto = getProto(obj); |
|
if (proto && proto.reference) { |
|
if (map[proto.reference]) { |
|
Object.setPrototypeOf(obj, map[proto.reference]); |
|
} |
|
} |
|
}); |
|
} |
|
function parse(obj, options) { |
|
var map = options.map; |
|
if (!isObj(obj)) { |
|
return obj; |
|
} |
|
var id = obj.id, |
|
type = obj.type, |
|
value = obj.value, |
|
proto = obj.proto, |
|
reference = obj.reference; |
|
var enumerable = obj.enumerable, |
|
unenumerable = obj.unenumerable; |
|
if (reference) { |
|
return obj; |
|
} |
|
if (type === 'Number') { |
|
if (value === 'Infinity') { |
|
return Number.POSITIVE_INFINITY; |
|
} else if (value === '-Infinity') { |
|
return Number.NEGATIVE_INFINITY; |
|
} |
|
return NaN; |
|
} else if (type === 'Undefined') { |
|
return undefined; |
|
} |
|
var newObj; |
|
if (type === 'Function') { |
|
newObj = function() {}; |
|
newObj.toString = function() { |
|
return value; |
|
}; |
|
if (proto) { |
|
Object.setPrototypeOf(newObj, parse(proto, options)); |
|
} |
|
} else if (type === 'RegExp') { |
|
newObj = strToRegExp(value); |
|
} else { |
|
if (type !== 'Object') { |
|
var Fn; |
|
if (!isMiniProgram) { |
|
Fn = new Function(type, ''); |
|
} else { |
|
Fn = function() {}; |
|
} |
|
if (proto) { |
|
Fn.prototype = parse(proto, options); |
|
} |
|
newObj = new Fn(); |
|
} else { |
|
if (proto) { |
|
newObj = create(parse(proto, options)); |
|
} else { |
|
newObj = create(null); |
|
} |
|
} |
|
} |
|
var defineProps = {}; |
|
if (enumerable) { |
|
var len; |
|
if (isArrLike(enumerable)) { |
|
len = enumerable.length; |
|
delete enumerable.length; |
|
} |
|
enumerable = pick(enumerable, function(value, key) { |
|
return !handleGetterSetter(enumerable, value, key); |
|
}); |
|
each(enumerable, function(value, key) { |
|
var defineProp = defineProps[key] || {}; |
|
if (!defineProp.get) { |
|
newObj[key] = parse(value, options); |
|
} |
|
}); |
|
if (len) { |
|
newObj.length = len; |
|
} |
|
} |
|
if (unenumerable) { |
|
unenumerable = pick(unenumerable, function(value, key) { |
|
return !handleGetterSetter(unenumerable, value, key); |
|
}); |
|
each(unenumerable, function(value, key) { |
|
var defineProp = defineProps[key] || {}; |
|
if (!defineProp.get) { |
|
value = parse(value, options); |
|
if (isObj(value) && value.reference) { |
|
var _reference = value.reference; |
|
value = function() { |
|
return map[_reference]; |
|
}; |
|
defineProp.get = value; |
|
} else { |
|
defineProp.value = value; |
|
} |
|
} |
|
defineProp.enumerable = false; |
|
defineProps[key] = defineProp; |
|
}); |
|
} |
|
defineProp(newObj, defineProps); |
|
function handleGetterSetter(obj, val, key) { |
|
key = toStr(key); |
|
var isGetterAndSetter = false; |
|
each(['get', 'set'], function(type) { |
|
if (startWith(key, type + ' ')) { |
|
var realKey = key.replace(type + ' ', ''); |
|
if (obj[realKey]) { |
|
val = parse(val, options); |
|
if (val === 'Timeout') { |
|
val = retTimeout; |
|
} |
|
safeSet(defineProps, [realKey, type], val); |
|
isGetterAndSetter = true; |
|
} |
|
} |
|
}); |
|
return isGetterAndSetter; |
|
} |
|
map[id] = newObj; |
|
return newObj; |
|
} |
|
function retTimeout() { |
|
return 'Timeout'; |
|
} |
|
function strToRegExp(str) { |
|
var lastSlash = str.lastIndexOf('/'); |
|
return new RegExp(str.slice(1, lastSlash), str.slice(lastSlash + 1)); |
|
} |
|
|
|
module.exports = exports;
|
|
|