Make the RPC stuff code more understandable.

This commit is contained in:
Cheng Zhao 2013-04-26 23:58:49 +08:00
parent da3d4c8408
commit b35946381b
3 changed files with 47 additions and 33 deletions

View file

@ -19,6 +19,7 @@ class ObjectsStore
@objects[id]? @objects[id]?
remove: (id) -> remove: (id) ->
throw new Error("Invalid key #{id} for ObjectsStore") unless @has id
delete @objects[id] delete @objects[id]
get: (id) -> get: (id) ->
@ -30,6 +31,8 @@ class ObjectsStore
@stores[key] = new ObjectsStore unless @stores[key]? @stores[key] = new ObjectsStore unless @stores[key]?
@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 = new IDWeakMap
objectsWeakMap.add = (obj) -> objectsWeakMap.add = (obj) ->
id = IDWeakMap::add.call this, obj id = IDWeakMap::add.call this, obj
@ -40,14 +43,18 @@ objectsWeakMap.add = (obj) ->
process.on 'ATOM_BROWSER_INTERNAL_NEW', (obj) -> process.on 'ATOM_BROWSER_INTERNAL_NEW', (obj) ->
# It's possible that user created a object in browser side and then want to # 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 # 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 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 objectsWeakMap, in that case we # Some native objects may already been added to objectsWeakMap, be care not
# don't add it twice. # to add it twice.
objectsWeakMap.add obj unless obj.id? 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 = ObjectsStore.forRenderView process_id, routing_id
store.add obj store.add obj

View file

@ -2,7 +2,9 @@ ipc = require 'ipc'
path = require 'path' path = require 'path'
objectsRegistry = require './objects_registry.js' 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) -> constructor: (process_id, routing_id, value) ->
@type = typeof value @type = typeof value
@type = 'value' if value is null @type = 'value' if value is null
@ -10,9 +12,13 @@ class PlainObject
if @type is 'array' if @type is 'array'
@members = [] @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' else if @type is 'object' or @type is 'function'
@name = value.constructor.name @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 @storeId = objectsRegistry.add process_id, routing_id, value
@id = value.id @id = value.id
@ -24,7 +30,7 @@ class PlainObject
ipc.on 'ATOM_INTERNAL_REQUIRE', (event, process_id, routing_id, module) -> ipc.on 'ATOM_INTERNAL_REQUIRE', (event, process_id, routing_id, module) ->
try try
event.result = new PlainObject(process_id, routing_id, require(module)) event.result = new Meta(process_id, routing_id, require(module))
catch e catch e
event.result = type: 'error', value: e.message event.result = type: 'error', value: e.message
@ -34,7 +40,7 @@ ipc.on 'ATOM_INTERNAL_CONSTRUCTOR', (event, process_id, routing_id, id, args) ->
# Call new with array of arguments. # Call new with array of arguments.
# http://stackoverflow.com/questions/1606797/use-of-apply-with-new-operator-is-this-possible # http://stackoverflow.com/questions/1606797/use-of-apply-with-new-operator-is-this-possible
obj = new (Function::bind.apply(constructor, [null].concat(args))) 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 catch e
event.result = type: 'error', value: e.message event.result = type: 'error', value: e.message
@ -42,7 +48,7 @@ ipc.on 'ATOM_INTERNAL_FUNCTION_CALL', (event, process_id, routing_id, id, args)
try try
func = objectsRegistry.get id func = objectsRegistry.get id
ret = func.apply global, args 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 catch e
event.result = type: 'error', value: e.message event.result = type: 'error', value: e.message
@ -50,7 +56,7 @@ ipc.on 'ATOM_INTERNAL_MEMBER_CALL', (event, process_id, routing_id, id, method,
try try
obj = objectsRegistry.get id obj = objectsRegistry.get id
ret = obj[method].apply(obj, args) 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 catch e
event.result = type: 'error', value: e.message event.result = type: 'error', value: e.message
@ -64,14 +70,14 @@ ipc.on 'ATOM_INTERNAL_MEMBER_SET', (event, process_id, routing_id, id, name, val
ipc.on 'ATOM_INTERNAL_MEMBER_GET', (event, process_id, routing_id, id, name) -> ipc.on 'ATOM_INTERNAL_MEMBER_GET', (event, process_id, routing_id, id, name) ->
try try
obj = objectsRegistry.get id 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 catch e
event.result = type: 'error', value: e.message event.result = type: 'error', value: e.message
ipc.on 'ATOM_INTERNAL_REFERENCE', (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 Meta(process_id, routing_id, obj)
catch e catch e
event.result = type: 'error', value: e.message event.result = type: 'error', value: e.message

View file

@ -1,63 +1,64 @@
ipc = require 'ipc' ipc = require 'ipc'
v8_util = process.atom_binding 'v8_util' v8_util = process.atom_binding 'v8_util'
generateFromPainObject = (plain) -> # Transform the description of value into a value or delegate object.
switch plain.type metaToValue = (meta) ->
when 'error' then throw new Error(plain.value) switch meta.type
when 'value' then plain.value when 'error' then throw new Error(meta.value)
when 'array' then (generateFromPainObject(el) for el in plain.members) when 'value' then meta.value
when 'array' then (metaToValue(el) for el in meta.members)
else else
if plain.type is 'function' if meta.type is 'function'
# A shadow class to represent the remote function object. # A shadow class to represent the remote function object.
ret = ret =
class RemoteFunction class RemoteFunction
constructor: -> constructor: ->
if @constructor == RemoteFunction if @constructor == RemoteFunction
# Constructor call. # 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 # Returning object in constructor will replace constructed object
# with the returned object. # with the returned object.
# http://stackoverflow.com/questions/1978049/what-values-can-a-constructor-return-to-avoid-returning-this # http://stackoverflow.com/questions/1978049/what-values-can-a-constructor-return-to-avoid-returning-this
return generateFromPainObject obj return metaToValue obj
else else
# Function call. # Function call.
ret = ipc.sendChannelSync 'ATOM_INTERNAL_FUNCTION_CALL', plain.id, Array::slice.call(arguments) ret = ipc.sendChannelSync 'ATOM_INTERNAL_FUNCTION_CALL', meta.id, Array::slice.call(arguments)
return generateFromPainObject ret return metaToValue ret
else else
ret = v8_util.createObjectWithName plain.name ret = v8_util.createObjectWithName meta.name
# Polulate delegate members. # Polulate delegate members.
for member in plain.members for member in meta.members
do (member) -> do (member) ->
if member.type is 'function' if member.type is 'function'
ret[member.name] = -> ret[member.name] = ->
# Call member function. # Call member function.
ret = ipc.sendChannelSync 'ATOM_INTERNAL_MEMBER_CALL', plain.id, member.name, Array::slice.call(arguments) ret = ipc.sendChannelSync 'ATOM_INTERNAL_MEMBER_CALL', meta.id, member.name, Array::slice.call(arguments)
generateFromPainObject ret metaToValue ret
else else
ret.__defineSetter__ member.name, (value) -> ret.__defineSetter__ member.name, (value) ->
# Set member data. # 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, -> ret.__defineGetter__ member.name, ->
# Get member data. # Get member data.
ret = ipc.sendChannelSync 'ATOM_INTERNAL_MEMBER_GET', plain.id, member.name ret = ipc.sendChannelSync 'ATOM_INTERNAL_MEMBER_GET', meta.id, member.name
generateFromPainObject ret metaToValue ret
# 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_DEREFERENCE', plain.storeId ipc.sendChannel 'ATOM_INTERNAL_DEREFERENCE', meta.storeId
ret ret
# Get remote module. # Get remote module.
exports.require = (module) -> exports.require = (module) ->
plain = ipc.sendChannelSync 'ATOM_INTERNAL_REQUIRE', module meta = ipc.sendChannelSync 'ATOM_INTERNAL_REQUIRE', module
generateFromPainObject plain metaToValue meta
# Get object with specified id. # Get object with specified id.
exports.getObject = (id) -> exports.getObject = (id) ->
plain = ipc.sendChannelSync 'ATOM_INTERNAL_REFERENCE', id meta = ipc.sendChannelSync 'ATOM_INTERNAL_REFERENCE', id
generateFromPainObject plain metaToValue meta