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 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
|
------------------------------------------------------------------------------
--
-- lgi - handling of structs and unions
--
-- Copyright (c) 2010, 2011,2013 Pavel Holejsovsky
-- Licensed under the MIT license:
-- http://www.opensource.org/licenses/mit-license.php
--
------------------------------------------------------------------------------
local rawget, assert, select, pairs, type, error, setmetatable
= rawget, assert, select, pairs, type, error, setmetatable
-- Require core lgi utilities, used during bootstrap.
local core = require 'lgi.core'
local gi = core.gi
local component = require 'lgi.component'
-- Implementation of record_mt, which is inherited from component
-- and provides customizations for structures and unions.
local record = {
struct_mt = component.mt:clone('struct', { '_method', '_field' }),
}
-- Checks whether given argument is type of this class.
function record.struct_mt:is_type_of(instance)
if type(instance) == 'userdata' then
local instance_type = core.record.query(instance, 'repo')
while instance_type do
if instance_type == self then return true end
instance_type = rawget(instance_type, '_parent')
end
end
return false
end
-- Resolver for records, recursively resolves also all parents.
function record.struct_mt:_resolve(recursive)
-- Resolve itself using inherited implementation.
component.mt._resolve(self)
-- Go to parent and resolve it too.
if recursive and self._parent then
self._parent:_resolve(recursive)
end
return self
end
function record.struct_mt:_element(instance, symbol)
-- First of all, try normal inherited functionality.
local element, category = component.mt._element(self, instance, symbol)
if element then
if category == '_field' then
if type(element) == 'table' and element.ret then
category = '_cbkfield'
local ffi = require 'lgi.ffi'
element = {
name = element.name,
ptrfield = { element.offset, 0, ffi.types.ptr },
callable = element
}
elseif gi.isinfo(element) and element.is_field then
local ii = element.typeinfo.interface
if ii and ii.type == 'callback' then
category = '_cbkfield'
local ffi = require 'lgi.ffi'
element = {
name = element.name,
ptrfield = { element.offset, 0, ffi.types.ptr },
callable = ii
}
end
end
end
return element, category
end
-- Special handling of '_native' attribute.
if symbol == '_native' then return symbol, '_internal'
elseif symbol == '_type' then return symbol, '_internal'
elseif symbol == '_refsink' then return symbol, '_internal'
end
-- If the record has parent struct, try it there.
local parent = rawget(self, '_parent')
if parent then
return parent:_element(instance, symbol)
end
end
-- Add accessor for handling fields.
function record.struct_mt:_access_field(instance, element, ...)
-- Check whether we are marshalling subrecord
local subrecord
if select('#', ...) > 0 then
if gi.isinfo(element) and element.is_field then
local ii = element.typeinfo.interface
if ii and (ii.type == 'struct' or ii.type == 'union') then
subrecord = true
end
else
if type(element) == 'table' and (element[2] == 1
or element[2] == 2) then
subrecord = true
end
end
end
if subrecord then
-- Write to nested structure, handle assignment to it by
-- assigning separate fields.
subrecord = core.record.field(instance, element)
for name, value in pairs(...) do
subrecord[name] = value
end
else
-- In other cases, just access the instance using given info.
return core.record.field(instance, element, ...)
end
end
-- Add accessor for handling fields containing callbacks
local guards_station = setmetatable({}, { __mode = 'k' })
function record.struct_mt:_access_cbkfield(instance, element, ...)
if select('#', ...) == 0 then
-- Reading callback field, get pointer and wrap it in proper
-- callable, so that caller can actually call it.
local addr = core.record.field(instance, element.ptrfield)
return core.callable.new(element.callable, addr)
else
local target = ...
if type(target) ~= 'userdata' then
-- Create closure over Lua target, keep guard stored.
local guard
guard, target = core.marshal.callback(element.callable, target)
local guards = guards_station[instance]
if not guards then
guards = {}
guards_station[instance] = guards
end
guards[element.name] = guard
end
core.record.field(instance, element.ptrfield, target)
end
end
-- Add accessor for 'internal' fields handling.
function record.struct_mt:_access_internal(instance, element, ...)
if select('#', ...) ~= 0 then return end
if element == '_native' then
return core.record.query(instance, 'addr')
elseif element == '_type' then
return core.record.query(instance, 'repo')
end
end
function record.struct_mt:_index_internal(element)
return nil
end
-- Create structure instance and initialize it with given fields.
function record.struct_mt:_new(param, owns)
local struct
if type(param) == 'userdata' or type(param) == 'number' then
-- Wrap existing pointer.
struct = core.record.new(self, param, owns)
else
-- Check that we are allowed to create the record.
if not self._size then
error(("%s: not directly instantiable"):format(self._name), 2)
end
-- Create the structure instance.
struct = core.record.new(self)
-- Set values of fields.
for name, value in pairs(param or {}) do
struct[name] = value
end
end
return struct
end
-- Loads structure information into table representing the structure
function record.load(info)
local record = component.create(
info, info.is_struct and record.struct_mt or record.union_mt)
record._size = info.size
record._method = component.get_category(info.methods, core.callable.new)
record._field = component.get_category(info.fields)
-- Check, whether global namespace contains 'constructor' method,
-- i.e. method which has the same name as our record type (except
-- that type is in CamelCase, while method is
-- under_score_delimited). If not found, check for 'new' method.
local func = core.downcase(info.name:gsub('([%l%d])([%u])', '%1_%2'))
local ctor = gi[info.namespace][func] or gi[info.namespace][func .. '_new']
if not ctor then ctor = info.methods.new end
-- Check, whether ctor is valid. In order to be valid, it must
-- return instance of this record.
if (ctor and ctor.type == 'function'
and ctor.return_type.tag =='interface'
and ctor.return_type.interface == info) then
ctor = core.callable.new(ctor)
record._new = function(typetable, ...) return ctor(...) end
end
return record
end
-- Union metatable is the same as struct one, but has different name
-- to differentiate unions.
record.union_mt = record.struct_mt:clone('union')
return record
|