@objects[id]?
remove: (id) ->
+ throw new Error("Invalid key #{id} for ObjectsStore") unless @has id
delete @objects[id]
get: (id) ->
@stores[key] = new ObjectsStore unless @stores[key]?
@stores[key]
+# Objects in weak map will be not referenced (so we won't leak memory), and
+# every object created in browser will have a unique id in weak map.
objectsWeakMap = new IDWeakMap
objectsWeakMap.add = (obj) ->
id = IDWeakMap::add.call this, obj
process.on 'ATOM_BROWSER_INTERNAL_NEW', (obj) ->
# It's possible that user created a object in browser side and then want to
# get it in renderer via remote.getObject. So we must add every native object
- # created in browser to the weak map.
+ # created in browser to the weak map even it may not be referenced by the
+ # renderer.
objectsWeakMap.add obj
exports.add = (process_id, routing_id, obj) ->
- # Some native types may already been added to objectsWeakMap, in that case we
- # don't add it twice.
+ # Some native objects may already been added to objectsWeakMap, be care not
+ # to add it twice.
objectsWeakMap.add obj unless obj.id?
+ # Store and reference the object, then return the storeId which points to
+ # where the object is stored. The caller can later dereference the object
+ # with the storeId.
store = ObjectsStore.forRenderView process_id, routing_id
store.add obj
path = require 'path'
objectsRegistry = require './objects_registry.js'
-class PlainObject
+# Convert a real value into a POD structure which carries information of this
+# value.
+class Meta
constructor: (process_id, routing_id, value) ->
@type = typeof value
@type = 'value' if value is null
if @type is 'array'
@members = []
- @members.push new PlainObject(process_id, routing_id, el) for el in value
+ @members.push new Meta(process_id, routing_id, el) for el in value
else if @type is 'object' or @type is 'function'
@name = value.constructor.name
+
+ # Reference the original value if it's an object, because when it's
+ # passed to renderer we would assume the renderer keeps a reference of
+ # it.
@storeId = objectsRegistry.add process_id, routing_id, value
@id = value.id
ipc.on 'ATOM_INTERNAL_REQUIRE', (event, process_id, routing_id, module) ->
try
- event.result = new PlainObject(process_id, routing_id, require(module))
+ event.result = new Meta(process_id, routing_id, require(module))
catch e
event.result = type: 'error', value: e.message
# Call new with array of arguments.
# http://stackoverflow.com/questions/1606797/use-of-apply-with-new-operator-is-this-possible
obj = new (Function::bind.apply(constructor, [null].concat(args)))
- event.result = new PlainObject(process_id, routing_id, obj)
+ event.result = new Meta(process_id, routing_id, obj)
catch e
event.result = type: 'error', value: e.message
try
func = objectsRegistry.get id
ret = func.apply global, args
- event.result = new PlainObject(process_id, routing_id, ret)
+ event.result = new Meta(process_id, routing_id, ret)
catch e
event.result = type: 'error', value: e.message
try
obj = objectsRegistry.get id
ret = obj[method].apply(obj, args)
- event.result = new PlainObject(process_id, routing_id, ret)
+ event.result = new Meta(process_id, routing_id, ret)
catch e
event.result = type: 'error', value: e.message
ipc.on 'ATOM_INTERNAL_MEMBER_GET', (event, process_id, routing_id, id, name) ->
try
obj = objectsRegistry.get id
- event.result = new PlainObject(process_id, routing_id, obj[name])
+ event.result = new Meta(process_id, routing_id, obj[name])
catch e
event.result = type: 'error', value: e.message
ipc.on 'ATOM_INTERNAL_REFERENCE', (event, process_id, routing_id, id) ->
try
obj = objectsRegistry.get id
- event.result = new PlainObject(process_id, routing_id, obj)
+ event.result = new Meta(process_id, routing_id, obj)
catch e
event.result = type: 'error', value: e.message
ipc = require 'ipc'
v8_util = process.atom_binding 'v8_util'
-generateFromPainObject = (plain) ->
- switch plain.type
- when 'error' then throw new Error(plain.value)
- when 'value' then plain.value
- when 'array' then (generateFromPainObject(el) for el in plain.members)
+# Transform the description of value into a value or delegate object.
+metaToValue = (meta) ->
+ switch meta.type
+ when 'error' then throw new Error(meta.value)
+ when 'value' then meta.value
+ when 'array' then (metaToValue(el) for el in meta.members)
else
- if plain.type is 'function'
+ if meta.type is 'function'
# A shadow class to represent the remote function object.
ret =
class RemoteFunction
constructor: ->
if @constructor == RemoteFunction
# Constructor call.
- obj = ipc.sendChannelSync 'ATOM_INTERNAL_CONSTRUCTOR', plain.id, Array::slice.call(arguments)
+ obj = ipc.sendChannelSync 'ATOM_INTERNAL_CONSTRUCTOR', meta.id, Array::slice.call(arguments)
# Returning object in constructor will replace constructed object
# with the returned object.
# http://stackoverflow.com/questions/1978049/what-values-can-a-constructor-return-to-avoid-returning-this
- return generateFromPainObject obj
+ return metaToValue obj
else
# Function call.
- ret = ipc.sendChannelSync 'ATOM_INTERNAL_FUNCTION_CALL', plain.id, Array::slice.call(arguments)
- return generateFromPainObject ret
+ ret = ipc.sendChannelSync 'ATOM_INTERNAL_FUNCTION_CALL', meta.id, Array::slice.call(arguments)
+ return metaToValue ret
else
- ret = v8_util.createObjectWithName plain.name
+ ret = v8_util.createObjectWithName meta.name
# Polulate delegate members.
- for member in plain.members
+ for member in meta.members
do (member) ->
if member.type is 'function'
ret[member.name] = ->
# Call member function.
- ret = ipc.sendChannelSync 'ATOM_INTERNAL_MEMBER_CALL', plain.id, member.name, Array::slice.call(arguments)
- generateFromPainObject ret
+ ret = ipc.sendChannelSync 'ATOM_INTERNAL_MEMBER_CALL', meta.id, member.name, Array::slice.call(arguments)
+ metaToValue ret
else
ret.__defineSetter__ member.name, (value) ->
# Set member data.
- ipc.sendChannelSync 'ATOM_INTERNAL_MEMBER_SET', plain.id, member.name, value
+ ipc.sendChannelSync 'ATOM_INTERNAL_MEMBER_SET', meta.id, member.name, value
ret.__defineGetter__ member.name, ->
# Get member data.
- ret = ipc.sendChannelSync 'ATOM_INTERNAL_MEMBER_GET', plain.id, member.name
- generateFromPainObject ret
+ ret = ipc.sendChannelSync 'ATOM_INTERNAL_MEMBER_GET', meta.id, member.name
+ metaToValue ret
# Track delegate object's life time, and tell the browser to clean up
# when the object is GCed.
v8_util.setDestructor ret, ->
- ipc.sendChannel 'ATOM_INTERNAL_DEREFERENCE', plain.storeId
+ ipc.sendChannel 'ATOM_INTERNAL_DEREFERENCE', meta.storeId
ret
# Get remote module.
exports.require = (module) ->
- plain = ipc.sendChannelSync 'ATOM_INTERNAL_REQUIRE', module
- generateFromPainObject plain
+ meta = ipc.sendChannelSync 'ATOM_INTERNAL_REQUIRE', module
+ metaToValue meta
# Get object with specified id.
exports.getObject = (id) ->
- plain = ipc.sendChannelSync 'ATOM_INTERNAL_REFERENCE', id
- generateFromPainObject plain
+ meta = ipc.sendChannelSync 'ATOM_INTERNAL_REFERENCE', id
+ metaToValue meta