michaeljclark / crefl

crefl is a runtime library and compiler plug-in to support reflection in C.
38 stars 1 forks source link

Crefl

Crefl - a C-type-reflection-API and clang plug-in to write reflection metadata.

The Crefl API and plugin provides access to runtime reflection metadata for C interface declarations with support for arbitrarily nested combinations of: intrinsic, set, enum, struct, union, field, array, constant, and function.

Crefl addresses the following three areas:

crefl


Crefl C example

This example has an outer-loop iterating through struct types and an inner-loop iterating through struct fields.

#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <assert.h>

#include <crefl/model.h>
#include <crefl/db.h>

int main(int argc, const char **argv)
{
    if (argc != 2) {
        fprintf(stderr, "error: usage: %s <filename>\n", argv[0]);
        exit(1);
    }

    decl_db *db = crefl_db_new();
    crefl_db_read_file(db, argv[1]);

    size_t ntypes = 0;
    crefl_source_decls(crefl_root(db), NULL, &ntypes);
    decl_ref *_types = calloc(ntypes, sizeof(decl_ref));
    assert(_types);
    crefl_source_decls(crefl_root(db), _types, &ntypes);

    for (size_t i = 0; i < ntypes; i++) {
        size_t nfields = 0;
        if (crefl_is_struct(_types[i])) {
            printf("%s %s : %zu\n",
                crefl_tag_name(crefl_decl_tag(_types[i])),
                crefl_decl_name(_types[i]),
                crefl_type_width(_types[i]));

            crefl_struct_fields(_types[i], NULL, &nfields);
            decl_ref *_fields = calloc(nfields, sizeof(decl_ref));
            assert(_fields);
            crefl_struct_fields(_types[i], _fields, &nfields);
            for (size_t j = 0; j < nfields; j++) {
                printf("\t%s %s : %zu\n",
                    crefl_tag_name(crefl_decl_tag(_fields[j])),
                    crefl_decl_name(_fields[j]),
                    crefl_type_width(_fields[j]));
            }
        }
    }

    crefl_db_destroy(db);
}

Crefl model

Crefl implements a data model based on the description of the C data types in ISO/IEC 9899:9999 with minor changes. The following sections describe:

Crefl variations from the C standard:

primary types

The Crefl API uses a small number of primary data types for the reflection graph database, the declaration graph nodes and their properties.

Type Description
decl_node graph database declaration node type
decl_db graph database containing the declarations
decl_ref reference to a single declaration graph node
decl_tag enumeration indicating graph node type
decl_id indice of a graph node in the graph database
decl_sz size type used for array size and bit widths
decl_set type used to indicate many-of set enumerations
decl_raw raw value used to store constants

decl node

The Crefl data structure consists of an array of decl nodes which have a type tag, a set of properties, an interned name, a link to the next item in a list, a link to a child item and a link to an optional attribute list.

Type Name Description
decl_tag tag tagged union node type
decl_set props type specific properties
decl_id name link to node name
decl_id next link to next item (south)
decl_id link link to child item (east)
decl_id attr link to attribute list (south-east)

The semantic topology of the graph links (south, south-east, east) are used to think about graph layout. On a board, the next node would be south, or below the current node. A child or link node would increment to the east (field type, function parameters, etc). Attributes can exist on any node type and thus are semantically referred to as south-east.

decl subtypes

The decl node contains a union with type specific properties such as a count, a width or a size. Not all types use the link field or the quantifer. The type none is used in the link field of an empty struct or union, in the next field to indicate the end of lists, and in the attr field to indicate the absense of attributes.

This table table lists the properties used by each subtype:

Type Link Properties Description
none empty type
typedef alias to another type definition
intrinsic sz width machine type quantified with width in bits
set sz width machine type with many-of sequence of masks
enum sz width machine type with one-of sequence of integers
struct sequence of non-overlapping types
union sequence of overlapping types
field sz width named field within struct or union
array sz count sequence of one type
pointer sz width pointer type
constant sz value named constant
function sz addr function with input and output parameter list
param named parameter with link to next
qualifier qualifiers for intrinsic types
attribute custom attribute name
value custom attribute value
archive collection of source objects
source maps to translation unit
alias aliases node overriding its next link

type hashes

Crefl contains a work-in-progress experimental model for C linkage that composes the identity of function interfaces and their associated types using Merkle Tree cryptographic hash sums. Merkel Tree style hash sums are composed from the hierachical node properties of an abstract type tree, a portion of the abstract syntax tree containing only the function interfaces and type information. All functions and their associated types are assigned hashes with the following constraints and properties:

crefltool output

This example invocation of crefltool shows the extended reflection meta-data generated with ./scripts/run_tests.py --dump-all test/adjacent-decls-3.h:

crefltool


Crefl implementation notes

The Crefl implementation is currently alpha software.

Crefl features


Crefl build instructions

crefl has been tested on ubuntu 20.04 LTS.

install dependencies

ubuntu 20.04 LTS:

sudo apt-get install llvm libclang-dev

building crefl

ubuntu 20.04 LTS:

cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo
cmake --build build -- --verbose

invoking plugin

to run the crefl plugin and dump the reflection table to stdout:

clang test/simple-struct-1.h \
      -Xclang -load -Xclang build/libcrefl.so \
      -Xclang -plugin -Xclang crefl \
      -Xclang -plugin-arg-crefl -Xclang -dump

to run the crefl plugin and write the reflection data to a file:

clang test/simple-struct-1.h \
      -Xclang -load -Xclang build/libcrefl.so \
      -Xclang -plugin -Xclang crefl \
      -Xclang -plugin-arg-crefl -Xclang -o \
      -Xclang -plugin-arg-crefl -Xclang tmp/simple-struct-1.refl

to enable crefl plugin debugging, add the following option:

      -Xclang -plugin-arg-crefl -Xclang -debug