Graphical Remote Lua Debugger, a debugger for the lua programming language.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

219 lines
5.7 KiB

-- see copyright notice in grldc.h
local socket = require( "grldc.socket" )
local assert = assert
local setmetatable = setmetatable
local print = print
local error = error
local type = type
local tonumber = tonumber
local tostring = tostring
local loadstring = loadstring
local string = string
local table = table
local pairs = pairs
local globals = _G
module( "grldc.net" )
local listenerMeta = { __index = {} }
local connectionMeta = { __index = {} }
local function debugPrint( ... )
--print( ... )
end
function serialize( value )
local t = type( value )
if t == "number" then
local res = tostring( value )
if string.find( value, "[^%.,%-0-9]" ) ~= nil then
res = "function() return 0/0 end"
end
return res
elseif t == "boolean" then
if value then return "true" end
return "false"
elseif t == "string" then
return string.format( "%q", value )
elseif t == "table" then
local res = "{ "
for k, v in pairs( value ) do
res = res.."["..serialize( k ).."] = "..serialize( v )..", "
end
res = res.." }"
return res
else
error( "Can't serialize a value of type "..t )
end
end
local function fixUp( value )
local t = type( value )
if t == "table" then
local res = {}
for k, v in pairs( value ) do
res[fixUp(k)] = fixUp(v)
end
return res
elseif t == "function" then
return value()
else
return value
end
end
function deserialize( str )
local f = loadstring( "return "..str )
if f then
local res = f()
return fixUp( res )
else
error( "Unable to parse serialized value: "..tostring(str) )
end
end
function bind( address, port )
local self = { listener = socket.bind( address, port ) }
setmetatable( self, listenerMeta )
return self
end
function listenerMeta.__index:accept()
local res = self.listener:accept()
if res ~= nil then
res = { connection = res }
setmetatable( res, connectionMeta )
res:init_()
end
return res
end
function connect( address, port )
local self = { connection = socket.connect( address, port ) }
print( "Connected to "..address..":"..port )
setmetatable( self, connectionMeta )
self:init_()
return self
end
function connectionMeta.__index:init_()
self.received_ = {}
end
function connectionMeta.__index:getpeername()
return self.connection:getpeername()
end
function connectionMeta.__index:send( data, channel )
channel = channel or "default"
debugPrint( "Sending "..tostring( data ).." on channel "..channel )
local sdata = serialize( data )
local packet = channel.."\n"..(#sdata).."\n"..sdata
local res, msg = self.connection:send( packet )
if res == nil then
error( msg, 0 )
else
return res
end
end
function connectionMeta.__index:waitData()
local needResumeHook = false
if globals.grldc.suspendHook ~= nil then
-- the grldc module is loaded, we need to avoid the debug hook to be called from the receive function (because the hook internally uses receive too)
globals.grldc.suspendHook()
needResumeHook = true
end
local hasData = false
for _, received in pairs( self.received_ ) do
if #received > 0 then
hasData = true
break
end
end
if hasData then
if needResumeHook then globals.grldc.resumeHook() end
return
end
debugPrint( "updating channels..." )
self:updateChannels_( nil )
if needResumeHook then globals.grldc.resumeHook() end
end
function connectionMeta.__index:receive( channel )
channel = channel or "default"
debugPrint( "Blocking receive on channel "..channel )
local data = self:popReceived_( channel )
while data == nil do
self:updateChannels_( nil )
data = self:popReceived_( channel )
end
debugPrint( "received "..tostring(data).." on channel "..channel )
return data
end
function connectionMeta.__index:tryReceive( channel )
channel = channel or "default"
local data = self:popReceived_( channel )
if data == nil then
self:updateChannels_( 0 )
data = self:popReceived_( channel )
end
if data ~= nil then
debugPrint( "received "..tostring(data).." on channel "..channel )
end
return data
end
function connectionMeta.__index:updateChannels_( timeout )
local needResumeHook = false
if globals.grldc.suspendHook ~= nil then
-- the grldc module is loaded, we need to avoid the debug hook to be called from the receive function (because the hook internally uses receive too)
globals.grldc.suspendHook()
needResumeHook = true
end
self.connection:settimeout( timeout )
local channel, msg = self.connection:receive()
self.connection:settimeout( nil )
if channel == nil and msg == "timeout" then
if needResumeHook then globals.grldc.resumeHook() end
return
end
local size
if channel ~= nil then
debugPrint( "receiving..." )
size, msg = self.connection:receive()
end
local data
if size ~= nil then
size = assert( tonumber( size ) )
data, msg = self.connection:receive( size )
end
if data == nil then
if needResumeHook then globals.grldc.resumeHook() end
error( msg, 0 )
end
if channel == "ka" then
-- special channel keepalive, we simply ignore data received on this channel
else
local received = self.received_[channel]
if received == nil then
received = {}
self.received_[channel] = received
end
table.insert( received, deserialize( data ) )
debugPrint( "queued "..tostring(data).." on channel "..channel )
end
if needResumeHook then globals.grldc.resumeHook() end
end
function connectionMeta.__index:popReceived_( channel )
local received = self.received_[channel]
if received == nil then return nil end
local res = received[1]
if res == nil then return nil end
table.remove( received, 1 )
return res
end