summaryrefslogtreecommitdiff
path: root/lib/supple/objects.lua
blob: 6c28cae046dc388ca2e483daa8505e6fe90f64e4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
-- lib/supple/request.lua
--
-- Sandbox (for) Untrusted Procedure Partitioning (in) Lua Engine
--
-- Contextual object storage and identification.  Wrapping and unwrapping.
--
-- Copyright 2012 Daniel Silverstone <dsilvers@digital-scurf.org>
--
-- For licence terms, see COPYING
--

local capi = require 'supple.capi'

local my_objects_by_obj = {}
local my_objects_by_tag = {}
local my_name = "UNSET"
local my_counter = 0
local their_objects_by_tag = setmetatable({}, { __mode="v" })
local their_objects_by_obj = setmetatable({}, { __mode="k" })
local proc_call = nil

local type = capi.rawtype

local function set_name(newname)
   my_name = newname
end

local function set_proc_call(pc)
   proc_call = pc
end

local integral = {
   ["nil"] = true,
   boolean = true,
   string = true,
   number = true,
}

local function give(obj)
   -- If the object is integral, return it directly
   if integral[type(obj)] then
      return obj
   end
   -- If the passed object is one of their objects then "unwrap" it by
   -- returning their tag.
   local tag = their_objects_by_obj[obj];
   if tag then
      return { tag = tag }
   end
   -- If it's one of our objects which has already been wrapped, return our
   -- tag
   local tag = my_objects_by_obj[obj]
   if tag then
      return { tag = tag }
   end
   -- otherwise wrap it freshly for us and return that.
   local tag = ("%s:%d"):format(my_name, my_counter)
   local expn = capi.explain(obj, tag)
   my_objects_by_obj[obj] = tag
   my_objects_by_tag[tag] = obj
   return expn
end

local function receive(obj)
   -- If the object is integral, return it directly
   if integral[type(obj)] then
      return obj
   end
   -- It's not integral, so it must be a tagged object
   assert(type(obj) == "table")
   assert(type(obj.tag) == "string")
   local tag = obj.tag
   -- First up, is it one of our objects?
   local ret = my_objects_by_tag[tag]
   if ret then
      return ret
   end
   -- Now is it a known one of their objects?
   ret = their_objects_by_tag[tag]
   if ret then
      return ret
   end
   -- Okay, prepare a proxy?
   assert(type(obj.type) == "string")
   local proxy, mt = capi.new_proxy(obj.type)
   their_objects_by_tag[tag] = proxy
   their_objects_by_obj[proxy] = tag
   -- Fill out the metatable
   obj.methods = obj.methods or {}
   obj.methods[#obj.methods+1] = "__gc"
   if obj.type == "function" then
      obj.methods[#obj.methods+1] = "__call"
   end
   if obj.type == "table" then
      obj.methods[#obj.methods+1] = "__index"
      obj.methods[#obj.methods+1] = "__newindex"
   end
   for _, name in ipairs(obj.methods or {}) do
      local function meta_func(...)
	 return proc_call(tag, name, ...)
      end
      mt[name] = meta_func
   end
   -- And return the proxy object
   return proxy
end

return {
   set_name = set_name,
   set_proc_call = set_proc_call,
   give = give,
   receive = receive,
}