mirror of
https://gitlab.gnome.org/jwestman/blueprint-compiler.git
synced 2025-05-04 15:59:08 -04:00
Use typelib instead of XML
For normal compilation, use .typelib files rather than .gir XML files. This is much faster. Rather than using libgirepository, which would try to actually load the libraries, we use a custom parser. The language server will still read XML because it needs to access documentation, which is not in the typelib, but that's generally fine because it's a long lived process and only has to do that once.
This commit is contained in:
parent
e78fae4f12
commit
06f54c8ff8
10 changed files with 647 additions and 170 deletions
292
blueprintcompiler/typelib.py
Normal file
292
blueprintcompiler/typelib.py
Normal file
|
@ -0,0 +1,292 @@
|
|||
# typelib.py
|
||||
#
|
||||
# Copyright 2022 James Westman <james@jwestman.net>
|
||||
#
|
||||
# This file is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Lesser General Public License as
|
||||
# published by the Free Software Foundation; either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This file is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
|
||||
import typing as T
|
||||
import math
|
||||
from ctypes import *
|
||||
import mmap, os
|
||||
|
||||
from .errors import CompilerBugError
|
||||
|
||||
|
||||
BLOB_TYPE_ENUM = 5
|
||||
BLOB_TYPE_FLAGS = 6
|
||||
BLOB_TYPE_OBJECT = 7
|
||||
BLOB_TYPE_INTERFACE = 8
|
||||
|
||||
TYPE_VOID = 0
|
||||
TYPE_BOOLEAN = 1
|
||||
TYPE_INT8 = 2
|
||||
TYPE_UINT8 = 3
|
||||
TYPE_INT16 = 4
|
||||
TYPE_UINT16 = 5
|
||||
TYPE_INT32 = 6
|
||||
TYPE_UINT32 = 7
|
||||
TYPE_INT64 = 8
|
||||
TYPE_UINT64 = 9
|
||||
TYPE_FLOAT = 10
|
||||
TYPE_DOUBLE = 11
|
||||
TYPE_GTYPE = 12
|
||||
TYPE_UTF8 = 13
|
||||
TYPE_FILENAME = 14
|
||||
TYPE_ARRAY = 15
|
||||
TYPE_INTERFACE = 16
|
||||
TYPE_GLIST = 17
|
||||
TYPE_GSLIST = 18
|
||||
TYPE_GHASH = 19
|
||||
TYPE_ERROR = 20
|
||||
TYPE_UNICHAR = 21
|
||||
|
||||
|
||||
class Field:
|
||||
def __init__(self, offset, type, shift=0, mask=None):
|
||||
self._offset = offset
|
||||
self._type = type
|
||||
self._shift = shift
|
||||
self._mask = (1 << mask) - 1 if mask else None
|
||||
self._name = f"{offset}__{type}__{shift}__{mask}"
|
||||
|
||||
def __get__(self, typelib, _objtype=None):
|
||||
if typelib is None:
|
||||
return self
|
||||
|
||||
def shift_mask(n):
|
||||
n = n >> self._shift
|
||||
if self._mask:
|
||||
n = n & self._mask
|
||||
return n
|
||||
|
||||
tl = typelib[self._offset]
|
||||
if self._type == "u8":
|
||||
return shift_mask(tl.u8)
|
||||
elif self._type == "u16":
|
||||
return shift_mask(tl.u16)
|
||||
elif self._type == "u32":
|
||||
return shift_mask(tl.u32)
|
||||
elif self._type == "i8":
|
||||
return shift_mask(tl.i8)
|
||||
elif self._type == "i16":
|
||||
return shift_mask(tl.i16)
|
||||
elif self._type == "i32":
|
||||
return shift_mask(tl.i32)
|
||||
elif self._type == "pointer":
|
||||
return tl.header[tl.u32]
|
||||
elif self._type == "offset":
|
||||
return tl
|
||||
elif self._type == "string":
|
||||
return tl.string
|
||||
elif self._type == "dir_entry":
|
||||
return tl.header.dir_entry(tl.u16)
|
||||
else:
|
||||
raise CompilerBugError(self._type)
|
||||
|
||||
|
||||
class Typelib:
|
||||
AS_DIR_ENTRY = Field(0, "dir_entry")
|
||||
|
||||
HEADER_N_ENTRIES = Field(0x14, "u16")
|
||||
HEADER_N_LOCAL_ENTRIES = Field(0x16, "u16")
|
||||
HEADER_DIRECTORY = Field(0x18, "pointer")
|
||||
HEADER_N_ATTRIBUTES = Field(0x1C, "u32")
|
||||
HEADER_ATTRIBUTES = Field(0x20, "pointer")
|
||||
|
||||
HEADER_DEPENDENCIES = Field(0x24, "pointer")
|
||||
|
||||
HEADER_NAMESPACE = Field(0x2C, "string")
|
||||
HEADER_NSVERSION = Field(0x30, "string")
|
||||
|
||||
HEADER_ENTRY_BLOB_SIZE = Field(0x3C, "u16")
|
||||
HEADER_FUNCTION_BLOB_SIZE = Field(0x3E, "u16")
|
||||
HEADER_CALLBACK_BLOB_SIZE = Field(0x40, "u16")
|
||||
HEADER_SIGNAL_BLOB_SIZE = Field(0x42, "u16")
|
||||
HEADER_PROPERTY_BLOB_SIZE = Field(0x48, "u16")
|
||||
HEADER_FIELD_BLOB_SIZE = Field(0x4A, "u16")
|
||||
HEADER_VALUE_BLOB_SIZE = Field(0x4C, "u16")
|
||||
HEADER_ATTRIBUTE_BLOB_SIZE = Field(0x4E, "u16")
|
||||
HEADER_ENUM_BLOB_SIZE = Field(0x56, "u16")
|
||||
HEADER_OBJECT_BLOB_SIZE = Field(0x5A, "u16")
|
||||
HEADER_INTERFACE_BLOB_SIZE = Field(0x5C, "u16")
|
||||
|
||||
DIR_ENTRY_BLOB_TYPE = Field(0x0, "u16")
|
||||
DIR_ENTRY_LOCAL = Field(0x2, "u16", 0, 1)
|
||||
DIR_ENTRY_NAME = Field(0x4, "string")
|
||||
DIR_ENTRY_OFFSET = Field(0x8, "pointer")
|
||||
DIR_ENTRY_NAMESPACE = Field(0x8, "string")
|
||||
|
||||
ATTR_OFFSET = Field(0x0, "u32")
|
||||
ATTR_NAME = Field(0x0, "string")
|
||||
ATTR_VALUE = Field(0x0, "string")
|
||||
|
||||
INTERFACE_TYPE_INTERFACE = Field(0x2, "dir_entry")
|
||||
|
||||
BLOB_NAME = Field(0x4, "string")
|
||||
|
||||
ENUM_GTYPE_NAME = Field(0x8, "string")
|
||||
ENUM_N_VALUES = Field(0x10, "u16")
|
||||
ENUM_N_METHODS = Field(0x12, "u16")
|
||||
ENUM_VALUES = Field(0x18, "offset")
|
||||
|
||||
INTERFACE_GTYPE_NAME = Field(0x8, "string")
|
||||
INTERFACE_N_PREREQUISITES = Field(0x12, "u16")
|
||||
INTERFACE_N_PROPERTIES = Field(0x14, "u16")
|
||||
INTERFACE_N_METHODS = Field(0x16, "u16")
|
||||
INTERFACE_N_SIGNALS = Field(0x18, "u16")
|
||||
INTERFACE_N_VFUNCS = Field(0x1A, "u16")
|
||||
INTERFACE_N_CONSTANTS = Field(0x1C, "u16")
|
||||
INTERFACE_PREREQUISITES = Field(0x28, "offset")
|
||||
|
||||
OBJ_DEPRECATED = Field(0x02, "u16", 0, 1)
|
||||
OBJ_ABSTRACT = Field(0x02, "u16", 1, 1)
|
||||
OBJ_FUNDAMENTAL = Field(0x02, "u16", 2, 1)
|
||||
OBJ_FINAL = Field(0x02, "u16", 3, 1)
|
||||
OBJ_GTYPE_NAME = Field(0x08, "string")
|
||||
OBJ_PARENT = Field(0x10, "dir_entry")
|
||||
OBJ_GTYPE_STRUCT = Field(0x14, "string")
|
||||
OBJ_N_INTERFACES = Field(0x14, "u16")
|
||||
OBJ_N_FIELDS = Field(0x16, "u16")
|
||||
OBJ_N_PROPERTIES = Field(0x18, "u16")
|
||||
OBJ_N_METHODS = Field(0x1A, "u16")
|
||||
OBJ_N_SIGNALS = Field(0x1C, "u16")
|
||||
OBJ_N_VFUNCS = Field(0x1E, "u16")
|
||||
OBJ_N_CONSTANTS = Field(0x20, "u16")
|
||||
OBJ_N_FIELD_CALLBACKS = Field(0x22, "u16")
|
||||
|
||||
PROP_NAME = Field(0x0, "string")
|
||||
PROP_DEPRECATED = Field(0x4, "u32", 0, 1)
|
||||
PROP_READABLE = Field(0x4, "u32", 1, 1)
|
||||
PROP_WRITABLE = Field(0x4, "u32", 2, 1)
|
||||
PROP_CONSTRUCT = Field(0x4, "u32", 3, 1)
|
||||
PROP_CONSTRUCT_ONLY = Field(0x4, "u32", 4, 1)
|
||||
PROP_TYPE = Field(0xC, "u32")
|
||||
|
||||
VALUE_NAME = Field(0x4, "string")
|
||||
VALUE_VALUE = Field(0x8, "i32")
|
||||
|
||||
def __init__(self, typelib_file, offset):
|
||||
self._typelib_file = typelib_file
|
||||
self._offset = offset
|
||||
|
||||
def __getitem__(self, index):
|
||||
return Typelib(self._typelib_file, self._offset + index)
|
||||
|
||||
def attr(self, name):
|
||||
return self.header.attr(self._offset, name)
|
||||
|
||||
@property
|
||||
def header(self):
|
||||
return TypelibHeader(self._typelib_file)
|
||||
|
||||
@property
|
||||
def u8(self):
|
||||
"""Gets the 8-bit unsigned int at this location."""
|
||||
return self._int(1, False)
|
||||
|
||||
@property
|
||||
def u16(self):
|
||||
"""Gets the 16-bit unsigned int at this location."""
|
||||
return self._int(2, False)
|
||||
|
||||
@property
|
||||
def u32(self):
|
||||
"""Gets the 32-bit unsigned int at this location."""
|
||||
return self._int(4, False)
|
||||
|
||||
@property
|
||||
def i8(self):
|
||||
"""Gets the 8-bit unsigned int at this location."""
|
||||
return self._int(1, True)
|
||||
|
||||
@property
|
||||
def i16(self):
|
||||
"""Gets the 16-bit unsigned int at this location."""
|
||||
return self._int(2, True)
|
||||
|
||||
@property
|
||||
def i32(self):
|
||||
"""Gets the 32-bit unsigned int at this location."""
|
||||
return self._int(4, True)
|
||||
|
||||
@property
|
||||
def string(self) -> T.Optional[str]:
|
||||
"""Interprets the 32-bit unsigned int at this location as a pointer
|
||||
within the typelib file, and returns the null-terminated string at that
|
||||
pointer."""
|
||||
|
||||
loc = self.u32
|
||||
if loc == 0:
|
||||
return None
|
||||
|
||||
end = loc
|
||||
while self._typelib_file[end] != 0:
|
||||
end += 1
|
||||
return self._typelib_file[loc:end].decode("utf-8")
|
||||
|
||||
def _int(self, size, signed):
|
||||
return int.from_bytes(self._typelib_file[self._offset:self._offset + size], 'little')
|
||||
|
||||
|
||||
class TypelibHeader(Typelib):
|
||||
def __init__(self, typelib_file):
|
||||
super().__init__(typelib_file, 0)
|
||||
|
||||
def dir_entry(self, index):
|
||||
if index == 0:
|
||||
return None
|
||||
else:
|
||||
return self.HEADER_DIRECTORY[(index - 1) * self.HEADER_ENTRY_BLOB_SIZE]
|
||||
|
||||
def attr(self, offset, name):
|
||||
lower = 0
|
||||
upper = self.HEADER_N_ATTRIBUTES
|
||||
attr_size = self.HEADER_ATTRIBUTE_BLOB_SIZE
|
||||
attrs = self.HEADER_ATTRIBUTES
|
||||
mid = 0
|
||||
|
||||
while lower <= upper:
|
||||
mid = math.floor((upper + lower) / 2)
|
||||
attr = attrs[mid * attr_size]
|
||||
if attr.ATTR_OFFSET < offset:
|
||||
lower = mid + 1
|
||||
elif attr.ATTR_OFFSET > offset:
|
||||
upper = mid - 1
|
||||
else:
|
||||
while mid >= 0 and attrs[(mid - 1) * attr_size].ATTR_OFFSET == offset:
|
||||
mid -= 1
|
||||
break
|
||||
if attrs[mid * attr_size].ATTR_OFFSET != offset:
|
||||
# no match found
|
||||
return None
|
||||
while attrs[mid * attr_size].ATTR_OFFSET == offset:
|
||||
if attrs[mid * attr_size].ATTR_NAME == name:
|
||||
return attrs[mid * attr_size].ATTR_VALUE
|
||||
mid += 1
|
||||
return None
|
||||
|
||||
def attr_by_index(self, index):
|
||||
pass
|
||||
|
||||
@property
|
||||
def dir_entries(self):
|
||||
return [self.dir_entry(i) for i in range(self[0x16].u16)]
|
||||
|
||||
|
||||
def load_typelib(path: str) -> Typelib:
|
||||
with open(path, "rb") as f:
|
||||
return Typelib(f.read(), 0)
|
Loading…
Add table
Add a link
Reference in a new issue