Allow renderer to get object in browser for arbitrary times.
Now, when creating a remote object in renderer, the browser will reference the corresponding object by storing it in a strong map. And when the remote object in renderer is GCed, the corresponding object will be dereferenced in browser.
This commit is contained in:
parent
536b523232
commit
da3d4c8408
3 changed files with 50 additions and 35 deletions
|
@ -1,44 +1,58 @@
|
||||||
IDWeakMap = require 'id_weak_map'
|
IDWeakMap = require 'id_weak_map'
|
||||||
|
|
||||||
globalStore = {}
|
class ObjectsStore
|
||||||
globalMap = new IDWeakMap
|
@stores = {}
|
||||||
|
|
||||||
addObjectToWeakMap = (obj) ->
|
constructor: ->
|
||||||
id = globalMap.add obj
|
@nextId = 0
|
||||||
|
@objects = []
|
||||||
|
|
||||||
|
getNextId: ->
|
||||||
|
++@nextId
|
||||||
|
|
||||||
|
add: (obj) ->
|
||||||
|
id = @getNextId()
|
||||||
|
@objects[id] = obj
|
||||||
|
id
|
||||||
|
|
||||||
|
has: (id) ->
|
||||||
|
@objects[id]?
|
||||||
|
|
||||||
|
remove: (id) ->
|
||||||
|
delete @objects[id]
|
||||||
|
|
||||||
|
get: (id) ->
|
||||||
|
throw new Error("Invalid key #{id} for ObjectsStore") unless @has id
|
||||||
|
@objects[id]
|
||||||
|
|
||||||
|
@forRenderView: (process_id, routing_id) ->
|
||||||
|
key = "#{process_id}_#{routing_id}"
|
||||||
|
@stores[key] = new ObjectsStore unless @stores[key]?
|
||||||
|
@stores[key]
|
||||||
|
|
||||||
|
objectsWeakMap = new IDWeakMap
|
||||||
|
objectsWeakMap.add = (obj) ->
|
||||||
|
id = IDWeakMap::add.call this, obj
|
||||||
Object.defineProperty obj, 'id',
|
Object.defineProperty obj, 'id',
|
||||||
enumerable: true, writable: false, value: id
|
enumerable: true, writable: false, value: id
|
||||||
id
|
id
|
||||||
|
|
||||||
getStoreForRenderView = (process_id, routing_id) ->
|
|
||||||
key = "#{process_id}_#{routing_id}"
|
|
||||||
globalStore[key] = {} unless globalStore[key]?
|
|
||||||
globalStore[key]
|
|
||||||
|
|
||||||
process.on 'ATOM_BROWSER_INTERNAL_NEW', (obj) ->
|
process.on 'ATOM_BROWSER_INTERNAL_NEW', (obj) ->
|
||||||
# For objects created in browser scripts, keep a weak reference here.
|
# It's possible that user created a object in browser side and then want to
|
||||||
addObjectToWeakMap obj
|
# get it in renderer via remote.getObject. So we must add every native object
|
||||||
|
# created in browser to the weak map.
|
||||||
|
objectsWeakMap.add obj
|
||||||
|
|
||||||
exports.add = (process_id, routing_id, obj) ->
|
exports.add = (process_id, routing_id, obj) ->
|
||||||
# Some native types may already been added to globalMap, in that case we
|
# Some native types may already been added to objectsWeakMap, in that case we
|
||||||
# don't add it twice.
|
# don't add it twice.
|
||||||
if obj.id?
|
objectsWeakMap.add obj unless obj.id?
|
||||||
id = obj.id
|
|
||||||
else
|
|
||||||
id = addObjectToWeakMap obj
|
|
||||||
|
|
||||||
store = getStoreForRenderView process_id, routing_id
|
store = ObjectsStore.forRenderView process_id, routing_id
|
||||||
|
store.add obj
|
||||||
# It's possible that a render view may want to get the same remote object
|
|
||||||
# twice, since we only allow one reference of one object per render view,
|
|
||||||
# we throw when the object is already referenced.
|
|
||||||
throw new Error("Object #{id} is already referenced") if store[id]?
|
|
||||||
|
|
||||||
store[id] = obj
|
|
||||||
id
|
|
||||||
|
|
||||||
exports.get = (id) ->
|
exports.get = (id) ->
|
||||||
globalMap.get id
|
objectsWeakMap.get id
|
||||||
|
|
||||||
exports.remove = (process_id, routing_id, id) ->
|
exports.remove = (process_id, routing_id, storeId) ->
|
||||||
store = getStoreForRenderView process_id, routing_id
|
ObjectsStore.forRenderView(process_id, routing_id).remove storeId
|
||||||
delete store[id]
|
|
||||||
|
|
|
@ -13,7 +13,8 @@ class PlainObject
|
||||||
@members.push new PlainObject(process_id, routing_id, el) for el in value
|
@members.push new PlainObject(process_id, routing_id, el) for el in value
|
||||||
else if @type is 'object' or @type is 'function'
|
else if @type is 'object' or @type is 'function'
|
||||||
@name = value.constructor.name
|
@name = value.constructor.name
|
||||||
@id = objectsRegistry.add process_id, routing_id, value
|
@storeId = objectsRegistry.add process_id, routing_id, value
|
||||||
|
@id = value.id
|
||||||
|
|
||||||
@members = []
|
@members = []
|
||||||
@members.push { name: prop, type: typeof field } for prop, field of value
|
@members.push { name: prop, type: typeof field } for prop, field of value
|
||||||
|
@ -67,12 +68,12 @@ ipc.on 'ATOM_INTERNAL_MEMBER_GET', (event, process_id, routing_id, id, name) ->
|
||||||
catch e
|
catch e
|
||||||
event.result = type: 'error', value: e.message
|
event.result = type: 'error', value: e.message
|
||||||
|
|
||||||
ipc.on 'ATOM_INTERNAL_GET_OBJECT', (event, process_id, routing_id, id) ->
|
ipc.on 'ATOM_INTERNAL_REFERENCE', (event, process_id, routing_id, id) ->
|
||||||
try
|
try
|
||||||
obj = objectsRegistry.get id
|
obj = objectsRegistry.get id
|
||||||
event.result = new PlainObject(process_id, routing_id, obj)
|
event.result = new PlainObject(process_id, routing_id, obj)
|
||||||
catch e
|
catch e
|
||||||
event.result = type: 'error', value: e.message
|
event.result = type: 'error', value: e.message
|
||||||
|
|
||||||
ipc.on 'ATOM_INTERNAL_DESTROY', (process_id, routing_id, id) ->
|
ipc.on 'ATOM_INTERNAL_DEREFERENCE', (process_id, routing_id, storeId) ->
|
||||||
objectsRegistry.remove process_id, routing_id, id
|
objectsRegistry.remove process_id, routing_id, storeId
|
||||||
|
|
|
@ -48,7 +48,7 @@ generateFromPainObject = (plain) ->
|
||||||
# Track delegate object's life time, and tell the browser to clean up
|
# Track delegate object's life time, and tell the browser to clean up
|
||||||
# when the object is GCed.
|
# when the object is GCed.
|
||||||
v8_util.setDestructor ret, ->
|
v8_util.setDestructor ret, ->
|
||||||
ipc.sendChannel 'ATOM_INTERNAL_DESTROY', plain.id
|
ipc.sendChannel 'ATOM_INTERNAL_DEREFERENCE', plain.storeId
|
||||||
|
|
||||||
ret
|
ret
|
||||||
|
|
||||||
|
@ -59,5 +59,5 @@ exports.require = (module) ->
|
||||||
|
|
||||||
# Get object with specified id.
|
# Get object with specified id.
|
||||||
exports.getObject = (id) ->
|
exports.getObject = (id) ->
|
||||||
plain = ipc.sendChannelSync 'ATOM_INTERNAL_GET_OBJECT', id
|
plain = ipc.sendChannelSync 'ATOM_INTERNAL_REFERENCE', id
|
||||||
generateFromPainObject plain
|
generateFromPainObject plain
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue