Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions script/core/command/reloadFFIMeta.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
local config = require 'config'
local ws = require 'workspace'
local fs = require 'bee.filesystem'
local scope = require 'workspace.scope'
local SDBMHash = require 'SDBMHash'
local searchCode = require 'plugins.ffi.searchCode'
local cdefRerence = require 'plugins.ffi.cdefRerence'
local ffi = require 'plugins.ffi'

local function createDir(uri)
local dir = scope.getScope(uri).uri or 'default'
local fileDir = fs.path(METAPATH) / ('%08x'):format(SDBMHash():hash(dir))
if fs.exists(fileDir) then
return fileDir, true
end
fs.create_directories(fileDir)
return fileDir
end

---@async
return function (uri)
if config.get(uri, 'Lua.runtime.version') ~= 'LuaJIT' then
return
end

ws.awaitReady(uri)

local fileDir, exists = createDir(uri)

local refs = cdefRerence()
if not refs or #refs == 0 then
return
end

for i, v in ipairs(refs) do
local target_uri = v.uri
local codes = searchCode(refs, target_uri)
if not codes then
return
end

ffi.build_single(codes, fileDir, target_uri)
end

if not exists then
local client = require 'client'
client.setConfig {
{
key = 'Lua.workspace.library',
action = 'add',
value = tostring(fileDir),
uri = uri,
}
}
end
end
2 changes: 1 addition & 1 deletion script/plugins/ffi/cdefRerence.lua
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ end
return function ()
local ffi_state
for uri in files.eachFile() do
if find(uri, "/ffi.lua", 0, true) then
if find(uri, "ffi.lua", 0, true) and find(uri, "lua-language-server", 0, true) then
ffi_state = files.getState(uri)
break
end
Expand Down
147 changes: 63 additions & 84 deletions script/plugins/ffi/init.lua
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
local searchCode = require 'plugins.ffi.searchCode'
local cdefRerence = require 'plugins.ffi.cdefRerence'
local cdriver = require 'plugins.ffi.c-parser.cdriver'
local util = require 'plugins.ffi.c-parser.util'
local utility = require 'utility'
local SDBMHash = require 'SDBMHash'
local ws = require 'workspace'
local files = require 'files'
local await = require 'await'
local config = require 'config'
local fs = require 'bee.filesystem'
local scope = require 'workspace.scope'
local searchCode = require 'plugins.ffi.searchCode'
local cdefRerence = require 'plugins.ffi.cdefRerence'
local cdriver = require 'plugins.ffi.c-parser.cdriver'
local util = require 'plugins.ffi.c-parser.util'
local utility = require 'utility'
local SDBMHash = require 'SDBMHash'
local config = require 'config'
local fs = require 'bee.filesystem'
local scope = require 'workspace.scope'

local namespace <const> = 'ffi.namespace*.'
local namespace <const> = 'ffi.namespace*.'

--TODO:supprot 32bit ffi, need config
local knownTypes = {
local knownTypes = {
["bool"] = 'boolean',
["char"] = 'integer',
["short"] = 'integer',
Expand Down Expand Up @@ -63,10 +60,33 @@ local knownTypes = {
["signedlong"] = 'integer',
}

local constName <const> = 'm'
local blackKeyWord <const> = {
['and'] = "_and",
['do'] = "_do",
['elseif'] = "_elseif",
['end'] = "_end",
['false'] = "_false",
['function'] = "_function",
['in'] = "_in",
['local'] = "_local",
['nil'] = "_nil",
['not'] = "_not",
['or'] = "_or",
['repeat'] = "_repeat",
['then'] = "_then",
['true'] = "_true",
}

local invaildKeyWord <const> = {
const = true,
restrict = true,
volatile = true,
}

local constName <const> = 'm'

---@class ffi.builder
local builder = { switch_ast = utility.switch() }
local builder = { switch_ast = utility.switch() }

function builder:getTypeAst(name)
for i, asts in ipairs(self.globalAsts) do
Expand Down Expand Up @@ -98,14 +118,22 @@ function builder:getType(name)
if type(name) == 'table' then
local t = ""
local isStruct
if name.type then
t = t .. name.type .. "@"
name = name.name
end
for _, n in ipairs(name) do
if type(n) == 'table' then
n = n.full_name
end
if invaildKeyWord[n] then
goto continue
end
if not isStruct then
isStruct = self:needDeref(self:getTypeAst(n))
end
t = t .. n
::continue::
end
-- deref 一级指针
if isStruct and t:sub(#t) == '*' then
Expand Down Expand Up @@ -145,11 +173,15 @@ local function getArrayType(arr)
return res
end

local function getValidName(name)
return blackKeyWord[name] or name
end

function builder:buildStructOrUnion(lines, tt, name)
lines[#lines+1] = '---@class ' .. self:getType(name)
for _, field in ipairs(tt.fields or {}) do
if field.name and field.type then
lines[#lines+1] = ('---@field %s %s%s'):format(field.name, self:getType(field.type),
lines[#lines+1] = ('---@field %s %s%s'):format(getValidName(field.name), self:getType(field.type),
getArrayType(field.isarray))
end
end
Expand All @@ -158,8 +190,9 @@ end
function builder:buildFunction(lines, tt, name)
local param_names = {}
for i, param in ipairs(tt.params or {}) do
lines[#lines+1] = ('---@param %s %s%s'):format(param.name, self:getType(param.type), getArrayType(param.idxs))
param_names[#param_names+1] = param.name
local param_name = getValidName(param.name)
lines[#lines+1] = ('---@param %s %s%s'):format(param_name, self:getType(param.type), getArrayType(param.idxs))
param_names[#param_names+1] = param_name
end
if tt.vararg then
param_names[#param_names+1] = '...'
Expand All @@ -178,7 +211,7 @@ function builder:buildTypedef(lines, tt, name)
-- 这个时候没有主类型,只有一个别名,直接创建一个别名结构体
self.switch_ast(def.type, self, lines, def, name)
else
lines[#lines+1] = ('---@alias %s %s'):format(name, self:getType(def))
lines[#lines+1] = ('---@alias %s %s'):format(self:getType(name), self:getType(def))
end
end

Expand Down Expand Up @@ -322,72 +355,18 @@ function m.compileCodes(codes)
return lines
end

local function createDir(uri)
local dir = scope.getScope(uri).uri or 'default'
local fileDir = fs.path(METAPATH) / ('%08x'):format(SDBMHash():hash(dir))
fs.create_directories(fileDir)
return fileDir
end

local builder
function m.initBuilder(fileDir)
fileDir = fileDir or createDir()
---@async
return function (uri)
local refs = cdefRerence()
if not refs or #refs == 0 then
return
end

local codes = searchCode(refs, uri)
if not codes then
return
end

local texts = m.compileCodes(codes)
if not texts then
return
end
local hash = ('%08x'):format(SDBMHash():hash(uri))
local encoding = config.get(nil, 'Lua.runtime.fileEncoding')
local filePath = fileDir / table.concat({ hash, encoding }, '_')

utility.saveFile(tostring(filePath) .. '.d.lua', table.concat(texts, '\n'))
function m.build_single(codes, fileDir, uri)
local texts = m.compileCodes(codes)
if not texts then
return
end
end

files.watch(function (ev, uri)
if ev == 'compiler' or ev == 'update' then
if builder then
await.call(function () ---@async
builder(uri)
end)
end
end
end)
local hash = ('%08x'):format(SDBMHash():hash(uri))
local encoding = config.get(nil, 'Lua.runtime.fileEncoding')
local filePath = fileDir / table.concat({ hash, encoding }, '_')

ws.watch(function (ev, uri)
-- TODO
do return end
if ev == 'startReload' then
if config.get(uri, 'Lua.runtime.version') ~= 'LuaJIT' then
return
end
await.call(function () ---@async
ws.awaitReady(uri)
local fileDir = createDir(uri)
builder = m.initBuilder(fileDir)
local client = require 'client'
client.setConfig {
{
key = 'Lua.workspace.library',
action = 'add',
value = tostring(fileDir),
uri = uri,
}
}
end)
end
end)
utility.saveFile(tostring(filePath) .. '.d.lua', table.concat(texts, '\n'))
return true
end

return m
5 changes: 5 additions & 0 deletions script/provider/provider.lua
Original file line number Diff line number Diff line change
Expand Up @@ -982,6 +982,11 @@ m.register 'workspace/executeCommand' {
elseif command == 'lua.exportDocument' then
local core = require 'core.command.exportDocument'
core(params.arguments)
elseif command == 'lua.reloadFFIMeta' then
local core = require 'core.command.reloadFFIMeta'
for _, scp in ipairs(workspace.folders) do
core(scp.uri)
end
end
end
}
Expand Down
40 changes: 37 additions & 3 deletions test/plugins/ffi/builder.lua
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,44 @@ function TEST(wanted)
end
end

TEST [[
---@alias ffi.namespace*.EVP_MD ffi.namespace*.struct@env_md_st

---@return ffi.namespace*.EVP_MD
function m.EVP_md5() end
]] [[
typedef struct env_md_st EVP_MD;
const EVP_MD *EVP_md5(void);
]]

TEST [[
---@class ffi.namespace*.struct@a
---@field _in integer
]] [[
struct a {
int in;
};
]]

TEST [[
---@param _in integer
function m.test(_in) end
]] [[
void test(int in);
]]

TEST [[
---@alias ffi.namespace*.ENGINE ffi.namespace*.struct@engine_st
---@alias ffi.namespace*.ENGINE1 ffi.namespace*.enum@engine_st1
]] [[
typedef struct engine_st ENGINE;
typedef enum engine_st1 ENGINE1;
]]

TEST [[
---@param a integer[][]
function m.test(a) end
]][[
]] [[
void test(int a[][]);
]]

Expand Down Expand Up @@ -76,7 +110,7 @@ TEST [[
m.A = 1
m.C = 5
---@alias ffi.namespace*.enum@a 1 | 2 | 'B' | 'A' | 5 | 'C'
]][[
]] [[
enum a {
A = 1,
B = 2,
Expand Down Expand Up @@ -165,7 +199,7 @@ TEST [[
]]

TEST [[
---@alias H ffi.namespace*.void
---@alias ffi.namespace*.H ffi.namespace*.void

function m.test() end
]] [[
Expand Down
7 changes: 3 additions & 4 deletions test/plugins/ffi/test.lua
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,15 @@ local template = require 'config.template'

template['Lua.runtime.version'].default = 'LuaJIT'


---@async
local function TestBuilder()
local builder = require 'plugins.ffi'.initBuilder()
local builder = require 'core.command.reloadFFIMeta'
files.setText(TESTURI, [[
local ffi = require 'ffi'
ffi.cdef 'void test();'
]])

builder(TESTURI)
local uri = ws.getFirstScope().uri
builder(uri)
end

---@async
Expand Down