-- lib/supple/request.lua -- -- Sandbox (for) Untrusted Procedure Partitioning (in) Lua Engine -- -- Contextual object storage and identification. Wrapping and unwrapping. -- -- Copyright 2012 Daniel Silverstone -- -- For licence terms, see COPYING -- local gc = collectgarbage 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 clean_down(call_other_end) -- And force a full GC gc "collect" gc "collect" gc "collect" -- Call the other end if needed if call_other_end then proc_call("supple:clean_down", "__call") end -- And forget all our local objects my_objects_by_obj = {} my_objects_by_tag = {} end local function set_name(newname) my_name = newname end local function get_name() return my_name end local function set_proc_call(pc) proc_call = pc end local integral = { ["nil"] = true, boolean = true, string = true, number = true, } local function new_tag(special_tag) local retstr = special_tag if not retstr then if not my_name:match("%%d") then retstr = ("%s:%d"):format(my_name, my_counter) else retstr = my_name:format(my_counter) end my_counter = my_counter + 1 end return retstr end local function give(obj, special_tag) -- 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 = new_tag(special_tag) 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) assert(capi.type(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] = "__len" 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(mobj, ...) return proc_call(tag, name, ...) end mt[name] = meta_func end -- And return the proxy object return proxy end local function forget_mine(tag) local obj = my_objects_by_tag[tag] my_objects_by_tag[tag] = nil my_objects_by_obj[obj] = nil end local function find_tag(obj) if my_objects_by_obj[obj] then return "my", my_objects_by_obj[obj] end if their_objects_by_obj[obj] then return "their", their_objects_by_obj[obj] end end return { set_name = set_name, get_name = get_name, set_proc_call = set_proc_call, give = give, receive = receive, clean_down = clean_down, forget_mine = forget_mine, find_tag = find_tag, }