Maintain visted objects as a stack for cycle detection

This commit is contained in:
Kevin Sawicki 2016-07-11 10:28:01 -07:00
parent 71a8bac12a
commit 564b0cace5

View file

@ -5,32 +5,41 @@ const {ipcRenderer, isPromise, CallbacksRegistry} = require('electron')
const callbacksRegistry = new CallbacksRegistry() const callbacksRegistry = new CallbacksRegistry()
var remoteObjectCache = v8Util.createIDWeakMap() const remoteObjectCache = v8Util.createIDWeakMap()
// Check for circular reference. // Check for circular reference.
var isCircular = function (field, visited) { const isCircular = function (field, visited) {
if (visited.has(field)) {
return true
}
if (typeof field === 'object') { if (typeof field === 'object') {
if (visited.includes(field)) { visited.add(field)
return true
}
visited.push(field)
} }
return false return false
} }
// Convert the arguments object into an array of meta data. // Convert the arguments object into an array of meta data.
var wrapArgs = function (args, visited) { const wrapArgs = function (args, visited) {
var valueToMeta
if (visited == null) { if (visited == null) {
visited = [] visited = new Set()
} }
valueToMeta = function (value) {
var field, prop, ret const valueToMeta = function (value) {
if (Array.isArray(value)) { if (isCircular(value, visited)) {
return { return {
type: 'array', type: 'value',
value: isCircular(value, visited) ? [] : wrapArgs(value, visited) value: null
} }
}
if (Array.isArray(value)) {
let meta = {
type: 'array',
value: wrapArgs(value, visited)
}
visited.delete(value)
return meta
} else if (Buffer.isBuffer(value)) { } else if (Buffer.isBuffer(value)) {
return { return {
type: 'buffer', type: 'buffer',
@ -56,19 +65,19 @@ var wrapArgs = function (args, visited) {
} }
} }
ret = { let meta = {
type: 'object', type: 'object',
name: value.constructor != null ? value.constructor.name : '', name: value.constructor != null ? value.constructor.name : '',
members: [] members: []
} }
for (prop in value) { for (let prop in value) {
field = value[prop] meta.members.push({
ret.members.push({
name: prop, name: prop,
value: valueToMeta(isCircular(field, visited) ? null : field) value: valueToMeta(value[prop])
}) })
} }
return ret visited.delete(value)
return meta
} else if (typeof value === 'function' && v8Util.getHiddenValue(value, 'returnValue')) { } else if (typeof value === 'function' && v8Util.getHiddenValue(value, 'returnValue')) {
return { return {
type: 'function-with-return-value', type: 'function-with-return-value',
@ -93,7 +102,7 @@ var wrapArgs = function (args, visited) {
// Populate object's members from descriptors. // Populate object's members from descriptors.
// The |ref| will be kept referenced by |members|. // The |ref| will be kept referenced by |members|.
// This matches |getObjectMemebers| in rpc-server. // This matches |getObjectMemebers| in rpc-server.
let setObjectMembers = function (ref, object, metaId, members) { const setObjectMembers = function (ref, object, metaId, members) {
for (let member of members) { for (let member of members) {
if (object.hasOwnProperty(member.name)) continue if (object.hasOwnProperty(member.name)) continue
@ -140,7 +149,7 @@ let setObjectMembers = function (ref, object, metaId, members) {
// Populate object's prototype from descriptor. // Populate object's prototype from descriptor.
// This matches |getObjectPrototype| in rpc-server. // This matches |getObjectPrototype| in rpc-server.
let setObjectPrototype = function (ref, object, metaId, descriptor) { const setObjectPrototype = function (ref, object, metaId, descriptor) {
if (descriptor === null) return if (descriptor === null) return
let proto = {} let proto = {}
setObjectMembers(ref, proto, metaId, descriptor.members) setObjectMembers(ref, proto, metaId, descriptor.members)
@ -149,7 +158,7 @@ let setObjectPrototype = function (ref, object, metaId, descriptor) {
} }
// Convert meta data from browser into real value. // Convert meta data from browser into real value.
let metaToValue = function (meta) { const metaToValue = function (meta) {
var el, i, len, ref1, results, ret var el, i, len, ref1, results, ret
switch (meta.type) { switch (meta.type) {
case 'value': case 'value':
@ -218,7 +227,7 @@ let metaToValue = function (meta) {
} }
// Construct a plain object from the meta. // Construct a plain object from the meta.
var metaToPlainObject = function (meta) { const metaToPlainObject = function (meta) {
var i, len, obj, ref1 var i, len, obj, ref1
obj = (function () { obj = (function () {
switch (meta.type) { switch (meta.type) {
@ -290,8 +299,7 @@ exports.__defineGetter__('process', function () {
// Create a funtion that will return the specifed value when called in browser. // Create a funtion that will return the specifed value when called in browser.
exports.createFunctionWithReturnValue = function (returnValue) { exports.createFunctionWithReturnValue = function (returnValue) {
var func const func = function () {
func = function () {
return returnValue return returnValue
} }
v8Util.setHiddenValue(func, 'returnValue', true) v8Util.setHiddenValue(func, 'returnValue', true)
@ -300,7 +308,6 @@ exports.createFunctionWithReturnValue = function (returnValue) {
// Get the guest WebContents from guestInstanceId. // Get the guest WebContents from guestInstanceId.
exports.getGuestWebContents = function (guestInstanceId) { exports.getGuestWebContents = function (guestInstanceId) {
var meta const meta = ipcRenderer.sendSync('ELECTRON_BROWSER_GUEST_WEB_CONTENTS', guestInstanceId)
meta = ipcRenderer.sendSync('ELECTRON_BROWSER_GUEST_WEB_CONTENTS', guestInstanceId)
return metaToValue(meta) return metaToValue(meta)
} }