view lua/luadyncall/src/dynport.lua @ 37:8c8f848131c6

- version bump - better doc - made calling convention mode reset by default, as only way to specify convention used is via signature string
author Tassilo Philipp
date Mon, 13 Apr 2020 20:08:54 +0200
parents 0cfcc391201f
children
line wrap: on
line source

require "dynload"
require "dyncall"
require "path"

local function makewrapper(addr, signature)
  return function(...) return dyncall(addr, signature, ...) end
end

loaded = { }

--- The dynport path is initialized by LDP_PATH environment.
-- @usage Defaults to dynport_syspath.
dynport_path = pathinit("LDP_PATH","?.dynport;/usr/local/share/dynport/?.dynport;/opt/local/share/dynport/?.dynport")

local function find(name)
  return pathfind( dynport_path, name, io.open )
end

--[[ 
local LDP_PATH = "?.dynport;/usr/local/share/dynport/?.dynport;/opt/local/share/dynport/?.dynport"
local path = os.getenv("LDP_PATH") or LDP_PATH

local function find(name)  
  local replaced = path:gsub("?", name)
  local f = nil
  local hist = {}
  for filename in replaced:gmatch("([^;]+)") do
    f = io.open(filename)
    if f then break else
      table.insert(hist, "\tno file '")
      table.insert(hist, filename)
      table.insert(hist, "'\n")
    end
  end
  if f then
    return f
  else
    error("dynport '"..name.."' not found:\n"..table.concat(hist), 3)
  end
end
]]


--- Process dynport files.
-- Files will be opened and processed according to the dynport format.
-- Function wrappers and constants will be installed.
-- Structure/Union type information have not been implemented yet.
-- @param name dynport name to lookup. 
-- @unit table to use for import.
-- @field _dynport_libs contains.
-- @return unit table with imports.

function dynport_NEW(portname, t)
  local t = t or _G
  local port = loaded[portname]
  if port then return port end
  local file, errmsg = searchpath(portname, path)
  if not file then error(errmsg) end
end

function dynportImport(name, unit)

  local file = find(name)

  if not unit._dynport_libs then
    unit._dynport_libs = { }
  end

  local cached = unit._dynport_libs[name]
  if cached then return unit end
 
  if not file then
    error("dynport "..name.. " not found")
  end

  local iter = file:lines()
  
  local libs = { }
  
  function dolib()
    local libnames = ""
    for line in iter do
      if line == "." then break end
      libnames = line
    end
    libs[#libs+1] = dynload(libnames)
  end

  function dofun()
    local index = 1
    local unresolved = {}
    for line in iter do

      if line == "." then break end

      local pos       = line:find("[(]")
      local symbol    = line:sub(1, pos-1)
      local stop      = line:find("[;]", pos+1)
      local signature = line:sub(pos+1,stop-1)

      local addr      = dynsym(libs[#libs], symbol)

      if type(addr) == "userdata" then
        rawset(unit, symbol, makewrapper(addr, signature) )
        -- module[symbol] = makewrapper(addr, signature)
      else
        unresolved[#unresolved] = symbol
      end

    end

    if #unresolved ~= 0 then
      print("unresolved symbols:")
      print(table.concat(unresolved,"\n"))
    end
  end

  function doconst() 
    for line in iter do
      if line == "." then break end
      local pos = line:find("=")
      local key = line:sub(1, pos-1)
      local value = line:sub(pos+1)
      -- module[key] = tonumber(value)
      rawset( unit, key, tonumber(value) )
    end
  end

  function dostruct()
    for line in iter do
      if line == "." then break end
    end
  end

  function dounion()
    for line in iter do
      if line == "." then break end
    end
  end
  
  for line in iter do
    if line == "." then break 
    elseif line == ":lib" then dolib() 
    elseif line == ":fun" then dofun() 
    elseif line == ":const" then doconst() 
    elseif line == ":struct" then dostruct()
    elseif line == ":union" then dounion()
    end
  end
  
  unit._dynport_libs[name] = libs

  return unit

end

--- Dynamic bind C library Interfaces.
-- @param name dynport name which will be searched by
function dynport(portname)
  return dynportImport(portname, _G)
end