1
0
Fork 0
Aegisub/subprojects/luajit/src/lj_cparse.c

1882 lines
55 KiB
C

/*
** C declaration parser.
** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h
*/
#include "lj_obj.h"
#if LJ_HASFFI
#include "lj_gc.h"
#include "lj_err.h"
#include "lj_str.h"
#include "lj_ctype.h"
#include "lj_cparse.h"
#include "lj_frame.h"
#include "lj_vm.h"
#include "lj_char.h"
#include "lj_strscan.h"
/*
** Important note: this is NOT a validating C parser! This is a minimal
** C declaration parser, solely for use by the LuaJIT FFI.
**
** It ought to return correct results for properly formed C declarations,
** but it may accept some invalid declarations, too (and return nonsense).
** Also, it shows rather generic error messages to avoid unnecessary bloat.
** If in doubt, please check the input against your favorite C compiler.
*/
/* -- C lexer ------------------------------------------------------------- */
/* C lexer token names. */
static const char *const ctoknames[] = {
#define CTOKSTR(name, str) str,
CTOKDEF(CTOKSTR)
#undef CTOKSTR
NULL
};
/* Forward declaration. */
LJ_NORET static void cp_err(CPState *cp, ErrMsg em);
static const char *cp_tok2str(CPState *cp, CPToken tok)
{
lua_assert(tok < CTOK_FIRSTDECL);
if (tok > CTOK_OFS)
return ctoknames[tok-CTOK_OFS-1];
else if (!lj_char_iscntrl(tok))
return lj_str_pushf(cp->L, "%c", tok);
else
return lj_str_pushf(cp->L, "char(%d)", tok);
}
/* End-of-line? */
static LJ_AINLINE int cp_iseol(CPChar c)
{
return (c == '\n' || c == '\r');
}
/* Peek next raw character. */
static LJ_AINLINE CPChar cp_rawpeek(CPState *cp)
{
return (CPChar)(uint8_t)(*cp->p);
}
static LJ_NOINLINE CPChar cp_get_bs(CPState *cp);
/* Get next character. */
static LJ_AINLINE CPChar cp_get(CPState *cp)
{
cp->c = (CPChar)(uint8_t)(*cp->p++);
if (LJ_LIKELY(cp->c != '\\')) return cp->c;
return cp_get_bs(cp);
}
/* Transparently skip backslash-escaped line breaks. */
static LJ_NOINLINE CPChar cp_get_bs(CPState *cp)
{
CPChar c2, c = cp_rawpeek(cp);
if (!cp_iseol(c)) return cp->c;
cp->p++;
c2 = cp_rawpeek(cp);
if (cp_iseol(c2) && c2 != c) cp->p++;
cp->linenumber++;
return cp_get(cp);
}
/* Grow save buffer. */
static LJ_NOINLINE void cp_save_grow(CPState *cp, CPChar c)
{
MSize newsize;
if (cp->sb.sz >= CPARSE_MAX_BUF/2)
cp_err(cp, LJ_ERR_XELEM);
newsize = cp->sb.sz * 2;
lj_str_resizebuf(cp->L, &cp->sb, newsize);
cp->sb.buf[cp->sb.n++] = (char)c;
}
/* Save character in buffer. */
static LJ_AINLINE void cp_save(CPState *cp, CPChar c)
{
if (LJ_UNLIKELY(cp->sb.n + 1 > cp->sb.sz))
cp_save_grow(cp, c);
else
cp->sb.buf[cp->sb.n++] = (char)c;
}
/* Skip line break. Handles "\n", "\r", "\r\n" or "\n\r". */
static void cp_newline(CPState *cp)
{
CPChar c = cp_rawpeek(cp);
if (cp_iseol(c) && c != cp->c) cp->p++;
cp->linenumber++;
}
LJ_NORET static void cp_errmsg(CPState *cp, CPToken tok, ErrMsg em, ...)
{
const char *msg, *tokstr;
lua_State *L;
va_list argp;
if (tok == 0) {
tokstr = NULL;
} else if (tok == CTOK_IDENT || tok == CTOK_INTEGER || tok == CTOK_STRING ||
tok >= CTOK_FIRSTDECL) {
if (cp->sb.n == 0) cp_save(cp, '$');
cp_save(cp, '\0');
tokstr = cp->sb.buf;
} else {
tokstr = cp_tok2str(cp, tok);
}
L = cp->L;
va_start(argp, em);
msg = lj_str_pushvf(L, err2msg(em), argp);
va_end(argp);
if (tokstr)
msg = lj_str_pushf(L, err2msg(LJ_ERR_XNEAR), msg, tokstr);
if (cp->linenumber > 1)
msg = lj_str_pushf(L, "%s at line %d", msg, cp->linenumber);
lj_err_callermsg(L, msg);
}
LJ_NORET LJ_NOINLINE static void cp_err_token(CPState *cp, CPToken tok)
{
cp_errmsg(cp, cp->tok, LJ_ERR_XTOKEN, cp_tok2str(cp, tok));
}
LJ_NORET LJ_NOINLINE static void cp_err_badidx(CPState *cp, CType *ct)
{
GCstr *s = lj_ctype_repr(cp->cts->L, ctype_typeid(cp->cts, ct), NULL);
cp_errmsg(cp, 0, LJ_ERR_FFI_BADIDX, strdata(s));
}
LJ_NORET LJ_NOINLINE static void cp_err(CPState *cp, ErrMsg em)
{
cp_errmsg(cp, 0, em);
}
/* -- Main lexical scanner ------------------------------------------------ */
/* Parse number literal. Only handles int32_t/uint32_t right now. */
static CPToken cp_number(CPState *cp)
{
StrScanFmt fmt;
TValue o;
do { cp_save(cp, cp->c); } while (lj_char_isident(cp_get(cp)));
cp_save(cp, '\0');
fmt = lj_strscan_scan((const uint8_t *)cp->sb.buf, &o, STRSCAN_OPT_C);
if (fmt == STRSCAN_INT) cp->val.id = CTID_INT32;
else if (fmt == STRSCAN_U32) cp->val.id = CTID_UINT32;
else if (!(cp->mode & CPARSE_MODE_SKIP))
cp_errmsg(cp, CTOK_INTEGER, LJ_ERR_XNUMBER);
cp->val.u32 = (uint32_t)o.i;
return CTOK_INTEGER;
}
/* Parse identifier or keyword. */
static CPToken cp_ident(CPState *cp)
{
do { cp_save(cp, cp->c); } while (lj_char_isident(cp_get(cp)));
cp->str = lj_str_new(cp->L, cp->sb.buf, cp->sb.n);
cp->val.id = lj_ctype_getname(cp->cts, &cp->ct, cp->str, cp->tmask);
if (ctype_type(cp->ct->info) == CT_KW)
return ctype_cid(cp->ct->info);
return CTOK_IDENT;
}
/* Parse parameter. */
static CPToken cp_param(CPState *cp)
{
CPChar c = cp_get(cp);
TValue *o = cp->param;
if (lj_char_isident(c) || c == '$') /* Reserve $xyz for future extensions. */
cp_errmsg(cp, c, LJ_ERR_XSYNTAX);
if (!o || o >= cp->L->top)
cp_err(cp, LJ_ERR_FFI_NUMPARAM);
cp->param = o+1;
if (tvisstr(o)) {
cp->str = strV(o);
cp->val.id = 0;
cp->ct = &cp->cts->tab[0];
return CTOK_IDENT;
} else if (tvisnumber(o)) {
cp->val.i32 = numberVint(o);
cp->val.id = CTID_INT32;
return CTOK_INTEGER;
} else {
GCcdata *cd;
if (!tviscdata(o))
lj_err_argtype(cp->L, (int)(o-cp->L->base)+1, "type parameter");
cd = cdataV(o);
if (cd->ctypeid == CTID_CTYPEID)
cp->val.id = *(CTypeID *)cdataptr(cd);
else
cp->val.id = cd->ctypeid;
return '$';
}
}
/* Parse string or character constant. */
static CPToken cp_string(CPState *cp)
{
CPChar delim = cp->c;
cp_get(cp);
while (cp->c != delim) {
CPChar c = cp->c;
if (c == '\0') cp_errmsg(cp, CTOK_EOF, LJ_ERR_XSTR);
if (c == '\\') {
c = cp_get(cp);
switch (c) {
case '\0': cp_errmsg(cp, CTOK_EOF, LJ_ERR_XSTR); break;
case 'a': c = '\a'; break;
case 'b': c = '\b'; break;
case 'f': c = '\f'; break;
case 'n': c = '\n'; break;
case 'r': c = '\r'; break;
case 't': c = '\t'; break;
case 'v': c = '\v'; break;
case 'e': c = 27; break;
case 'x':
c = 0;
while (lj_char_isxdigit(cp_get(cp)))
c = (c<<4) + (lj_char_isdigit(cp->c) ? cp->c-'0' : (cp->c&15)+9);
cp_save(cp, (c & 0xff));
continue;
default:
if (lj_char_isdigit(c)) {
c -= '0';
if (lj_char_isdigit(cp_get(cp))) {
c = c*8 + (cp->c - '0');
if (lj_char_isdigit(cp_get(cp))) {
c = c*8 + (cp->c - '0');
cp_get(cp);
}
}
cp_save(cp, (c & 0xff));
continue;
}
break;
}
}
cp_save(cp, c);
cp_get(cp);
}
cp_get(cp);
if (delim == '"') {
cp->str = lj_str_new(cp->L, cp->sb.buf, cp->sb.n);
return CTOK_STRING;
} else {
if (cp->sb.n != 1) cp_err_token(cp, '\'');
cp->val.i32 = (int32_t)(char)cp->sb.buf[0];
cp->val.id = CTID_INT32;
return CTOK_INTEGER;
}
}
/* Skip C comment. */
static void cp_comment_c(CPState *cp)
{
do {
if (cp_get(cp) == '*') {
do {
if (cp_get(cp) == '/') { cp_get(cp); return; }
} while (cp->c == '*');
}
if (cp_iseol(cp->c)) cp_newline(cp);
} while (cp->c != '\0');
}
/* Skip C++ comment. */
static void cp_comment_cpp(CPState *cp)
{
while (!cp_iseol(cp_get(cp)) && cp->c != '\0')
;
}
/* Lexical scanner for C. Only a minimal subset is implemented. */
static CPToken cp_next_(CPState *cp)
{
lj_str_resetbuf(&cp->sb);
for (;;) {
if (lj_char_isident(cp->c))
return lj_char_isdigit(cp->c) ? cp_number(cp) : cp_ident(cp);
switch (cp->c) {
case '\n': case '\r': cp_newline(cp); /* fallthrough. */
case ' ': case '\t': case '\v': case '\f': cp_get(cp); break;
case '"': case '\'': return cp_string(cp);
case '/':
if (cp_get(cp) == '*') cp_comment_c(cp);
else if (cp->c == '/') cp_comment_cpp(cp);
else return '/';
break;
case '|':
if (cp_get(cp) != '|') return '|';
cp_get(cp); return CTOK_OROR;
case '&':
if (cp_get(cp) != '&') return '&';
cp_get(cp); return CTOK_ANDAND;
case '=':
if (cp_get(cp) != '=') return '=';
cp_get(cp); return CTOK_EQ;
case '!':
if (cp_get(cp) != '=') return '!';
cp_get(cp); return CTOK_NE;
case '<':
if (cp_get(cp) == '=') { cp_get(cp); return CTOK_LE; }
else if (cp->c == '<') { cp_get(cp); return CTOK_SHL; }
return '<';
case '>':
if (cp_get(cp) == '=') { cp_get(cp); return CTOK_GE; }
else if (cp->c == '>') { cp_get(cp); return CTOK_SHR; }
return '>';
case '-':
if (cp_get(cp) != '>') return '-';
cp_get(cp); return CTOK_DEREF;
case '$':
return cp_param(cp);
case '\0': return CTOK_EOF;
default: { CPToken c = cp->c; cp_get(cp); return c; }
}
}
}
static LJ_NOINLINE CPToken cp_next(CPState *cp)
{
return (cp->tok = cp_next_(cp));
}
/* -- C parser ------------------------------------------------------------ */
/* Namespaces for resolving identifiers. */
#define CPNS_DEFAULT \
((1u<<CT_KW)|(1u<<CT_TYPEDEF)|(1u<<CT_FUNC)|(1u<<CT_EXTERN)|(1u<<CT_CONSTVAL))
#define CPNS_STRUCT ((1u<<CT_KW)|(1u<<CT_STRUCT)|(1u<<CT_ENUM))
typedef CTypeID CPDeclIdx; /* Index into declaration stack. */
typedef uint32_t CPscl; /* Storage class flags. */
/* Type declaration context. */
typedef struct CPDecl {
CPDeclIdx top; /* Top of declaration stack. */
CPDeclIdx pos; /* Insertion position in declaration chain. */
CPDeclIdx specpos; /* Saved position for declaration specifier. */
uint32_t mode; /* Declarator mode. */
CPState *cp; /* C parser state. */
GCstr *name; /* Name of declared identifier (if direct). */
GCstr *redir; /* Redirected symbol name. */
CTypeID nameid; /* Existing typedef for declared identifier. */
CTInfo attr; /* Attributes. */
CTInfo fattr; /* Function attributes. */
CTInfo specattr; /* Saved attributes. */
CTInfo specfattr; /* Saved function attributes. */
CTSize bits; /* Field size in bits (if any). */
CType stack[CPARSE_MAX_DECLSTACK]; /* Type declaration stack. */
} CPDecl;
/* Forward declarations. */
static CPscl cp_decl_spec(CPState *cp, CPDecl *decl, CPscl scl);
static void cp_declarator(CPState *cp, CPDecl *decl);
static CTypeID cp_decl_abstract(CPState *cp);
/* Initialize C parser state. Caller must set up: L, p, srcname, mode. */
static void cp_init(CPState *cp)
{
cp->linenumber = 1;
cp->depth = 0;
cp->curpack = 0;
cp->packstack[0] = 255;
lj_str_initbuf(&cp->sb);
lj_str_resizebuf(cp->L, &cp->sb, LJ_MIN_SBUF);
lua_assert(cp->p != NULL);
cp_get(cp); /* Read-ahead first char. */
cp->tok = 0;
cp->tmask = CPNS_DEFAULT;
cp_next(cp); /* Read-ahead first token. */
}
/* Cleanup C parser state. */
static void cp_cleanup(CPState *cp)
{
global_State *g = G(cp->L);
lj_str_freebuf(g, &cp->sb);
}
/* Check and consume optional token. */
static int cp_opt(CPState *cp, CPToken tok)
{
if (cp->tok == tok) { cp_next(cp); return 1; }
return 0;
}
/* Check and consume token. */
static void cp_check(CPState *cp, CPToken tok)
{
if (cp->tok != tok) cp_err_token(cp, tok);
cp_next(cp);
}
/* Check if the next token may start a type declaration. */
static int cp_istypedecl(CPState *cp)
{
if (cp->tok >= CTOK_FIRSTDECL && cp->tok <= CTOK_LASTDECL) return 1;
if (cp->tok == CTOK_IDENT && ctype_istypedef(cp->ct->info)) return 1;
if (cp->tok == '$') return 1;
return 0;
}
/* -- Constant expression evaluator --------------------------------------- */
/* Forward declarations. */
static void cp_expr_unary(CPState *cp, CPValue *k);
static void cp_expr_sub(CPState *cp, CPValue *k, int pri);
/* Please note that type handling is very weak here. Most ops simply
** assume integer operands. Accessors are only needed to compute types and
** return synthetic values. The only purpose of the expression evaluator
** is to compute the values of constant expressions one would typically
** find in C header files. And again: this is NOT a validating C parser!
*/
/* Parse comma separated expression and return last result. */
static void cp_expr_comma(CPState *cp, CPValue *k)
{
do { cp_expr_sub(cp, k, 0); } while (cp_opt(cp, ','));
}
/* Parse sizeof/alignof operator. */
static void cp_expr_sizeof(CPState *cp, CPValue *k, int wantsz)
{
CTSize sz;
CTInfo info;
if (cp_opt(cp, '(')) {
if (cp_istypedecl(cp))
k->id = cp_decl_abstract(cp);
else
cp_expr_comma(cp, k);
cp_check(cp, ')');
} else {
cp_expr_unary(cp, k);
}
info = lj_ctype_info(cp->cts, k->id, &sz);
if (wantsz) {
if (sz != CTSIZE_INVALID)
k->u32 = sz;
else if (k->id != CTID_A_CCHAR) /* Special case for sizeof("string"). */
cp_err(cp, LJ_ERR_FFI_INVSIZE);
} else {
k->u32 = 1u << ctype_align(info);
}
k->id = CTID_UINT32; /* Really size_t. */
}
/* Parse prefix operators. */
static void cp_expr_prefix(CPState *cp, CPValue *k)
{
if (cp->tok == CTOK_INTEGER) {
*k = cp->val; cp_next(cp);
} else if (cp_opt(cp, '+')) {
cp_expr_unary(cp, k); /* Nothing to do (well, integer promotion). */
} else if (cp_opt(cp, '-')) {
cp_expr_unary(cp, k); k->i32 = -k->i32;
} else if (cp_opt(cp, '~')) {
cp_expr_unary(cp, k); k->i32 = ~k->i32;
} else if (cp_opt(cp, '!')) {
cp_expr_unary(cp, k); k->i32 = !k->i32; k->id = CTID_INT32;
} else if (cp_opt(cp, '(')) {
if (cp_istypedecl(cp)) { /* Cast operator. */
CTypeID id = cp_decl_abstract(cp);
cp_check(cp, ')');
cp_expr_unary(cp, k);
k->id = id; /* No conversion performed. */
} else { /* Sub-expression. */
cp_expr_comma(cp, k);
cp_check(cp, ')');
}
} else if (cp_opt(cp, '*')) { /* Indirection. */
CType *ct;
cp_expr_unary(cp, k);
ct = lj_ctype_rawref(cp->cts, k->id);
if (!ctype_ispointer(ct->info))
cp_err_badidx(cp, ct);
k->u32 = 0; k->id = ctype_cid(ct->info);
} else if (cp_opt(cp, '&')) { /* Address operator. */
cp_expr_unary(cp, k);
k->id = lj_ctype_intern(cp->cts, CTINFO(CT_PTR, CTALIGN_PTR+k->id),
CTSIZE_PTR);
} else if (cp_opt(cp, CTOK_SIZEOF)) {
cp_expr_sizeof(cp, k, 1);
} else if (cp_opt(cp, CTOK_ALIGNOF)) {
cp_expr_sizeof(cp, k, 0);
} else if (cp->tok == CTOK_IDENT) {
if (ctype_type(cp->ct->info) == CT_CONSTVAL) {
k->u32 = cp->ct->size; k->id = ctype_cid(cp->ct->info);
} else if (ctype_type(cp->ct->info) == CT_EXTERN) {
k->u32 = cp->val.id; k->id = ctype_cid(cp->ct->info);
} else if (ctype_type(cp->ct->info) == CT_FUNC) {
k->u32 = cp->val.id; k->id = cp->val.id;
} else {
goto err_expr;
}
cp_next(cp);
} else if (cp->tok == CTOK_STRING) {
CTSize sz = cp->str->len;
while (cp_next(cp) == CTOK_STRING)
sz += cp->str->len;
k->u32 = sz + 1;
k->id = CTID_A_CCHAR;
} else {
err_expr:
cp_errmsg(cp, cp->tok, LJ_ERR_XSYMBOL);
}
}
/* Parse postfix operators. */
static void cp_expr_postfix(CPState *cp, CPValue *k)
{
for (;;) {
CType *ct;
if (cp_opt(cp, '[')) { /* Array/pointer index. */
CPValue k2;
cp_expr_comma(cp, &k2);
ct = lj_ctype_rawref(cp->cts, k->id);
if (!ctype_ispointer(ct->info)) {
ct = lj_ctype_rawref(cp->cts, k2.id);
if (!ctype_ispointer(ct->info))
cp_err_badidx(cp, ct);
}
cp_check(cp, ']');
k->u32 = 0;
} else if (cp->tok == '.' || cp->tok == CTOK_DEREF) { /* Struct deref. */
CTSize ofs;
CType *fct;
ct = lj_ctype_rawref(cp->cts, k->id);
if (cp->tok == CTOK_DEREF) {
if (!ctype_ispointer(ct->info))
cp_err_badidx(cp, ct);
ct = lj_ctype_rawref(cp->cts, ctype_cid(ct->info));
}
cp_next(cp);
if (cp->tok != CTOK_IDENT) cp_err_token(cp, CTOK_IDENT);
if (!ctype_isstruct(ct->info) || ct->size == CTSIZE_INVALID ||
!(fct = lj_ctype_getfield(cp->cts, ct, cp->str, &ofs)) ||
ctype_isbitfield(fct->info)) {
GCstr *s = lj_ctype_repr(cp->cts->L, ctype_typeid(cp->cts, ct), NULL);
cp_errmsg(cp, 0, LJ_ERR_FFI_BADMEMBER, strdata(s), strdata(cp->str));
}
ct = fct;
k->u32 = ctype_isconstval(ct->info) ? ct->size : 0;
cp_next(cp);
} else {
return;
}
k->id = ctype_cid(ct->info);
}
}
/* Parse infix operators. */
static void cp_expr_infix(CPState *cp, CPValue *k, int pri)
{
CPValue k2;
k2.u32 = 0; k2.id = 0; /* Silence the compiler. */
for (;;) {
switch (pri) {
case 0:
if (cp_opt(cp, '?')) {
CPValue k3;
cp_expr_comma(cp, &k2); /* Right-associative. */
cp_check(cp, ':');
cp_expr_sub(cp, &k3, 0);
k->u32 = k->u32 ? k2.u32 : k3.u32;
k->id = k2.id > k3.id ? k2.id : k3.id;
continue;
}
case 1:
if (cp_opt(cp, CTOK_OROR)) {
cp_expr_sub(cp, &k2, 2); k->i32 = k->u32 || k2.u32; k->id = CTID_INT32;
continue;
}
case 2:
if (cp_opt(cp, CTOK_ANDAND)) {
cp_expr_sub(cp, &k2, 3); k->i32 = k->u32 && k2.u32; k->id = CTID_INT32;
continue;
}
case 3:
if (cp_opt(cp, '|')) {
cp_expr_sub(cp, &k2, 4); k->u32 = k->u32 | k2.u32; goto arith_result;
}
case 4:
if (cp_opt(cp, '^')) {
cp_expr_sub(cp, &k2, 5); k->u32 = k->u32 ^ k2.u32; goto arith_result;
}
case 5:
if (cp_opt(cp, '&')) {
cp_expr_sub(cp, &k2, 6); k->u32 = k->u32 & k2.u32; goto arith_result;
}
case 6:
if (cp_opt(cp, CTOK_EQ)) {
cp_expr_sub(cp, &k2, 7); k->i32 = k->u32 == k2.u32; k->id = CTID_INT32;
continue;
} else if (cp_opt(cp, CTOK_NE)) {
cp_expr_sub(cp, &k2, 7); k->i32 = k->u32 != k2.u32; k->id = CTID_INT32;
continue;
}
case 7:
if (cp_opt(cp, '<')) {
cp_expr_sub(cp, &k2, 8);
if (k->id == CTID_INT32 && k2.id == CTID_INT32)
k->i32 = k->i32 < k2.i32;
else
k->i32 = k->u32 < k2.u32;
k->id = CTID_INT32;
continue;
} else if (cp_opt(cp, '>')) {
cp_expr_sub(cp, &k2, 8);
if (k->id == CTID_INT32 && k2.id == CTID_INT32)
k->i32 = k->i32 > k2.i32;
else
k->i32 = k->u32 > k2.u32;
k->id = CTID_INT32;
continue;
} else if (cp_opt(cp, CTOK_LE)) {
cp_expr_sub(cp, &k2, 8);
if (k->id == CTID_INT32 && k2.id == CTID_INT32)
k->i32 = k->i32 <= k2.i32;
else
k->i32 = k->u32 <= k2.u32;
k->id = CTID_INT32;
continue;
} else if (cp_opt(cp, CTOK_GE)) {
cp_expr_sub(cp, &k2, 8);
if (k->id == CTID_INT32 && k2.id == CTID_INT32)
k->i32 = k->i32 >= k2.i32;
else
k->i32 = k->u32 >= k2.u32;
k->id = CTID_INT32;
continue;
}
case 8:
if (cp_opt(cp, CTOK_SHL)) {
cp_expr_sub(cp, &k2, 9); k->u32 = k->u32 << k2.u32;
continue;
} else if (cp_opt(cp, CTOK_SHR)) {
cp_expr_sub(cp, &k2, 9);
if (k->id == CTID_INT32)
k->i32 = k->i32 >> k2.i32;
else
k->u32 = k->u32 >> k2.u32;
continue;
}
case 9:
if (cp_opt(cp, '+')) {
cp_expr_sub(cp, &k2, 10); k->u32 = k->u32 + k2.u32;
arith_result:
if (k2.id > k->id) k->id = k2.id; /* Trivial promotion to unsigned. */
continue;
} else if (cp_opt(cp, '-')) {
cp_expr_sub(cp, &k2, 10); k->u32 = k->u32 - k2.u32; goto arith_result;
}
case 10:
if (cp_opt(cp, '*')) {
cp_expr_unary(cp, &k2); k->u32 = k->u32 * k2.u32; goto arith_result;
} else if (cp_opt(cp, '/')) {
cp_expr_unary(cp, &k2);
if (k2.id > k->id) k->id = k2.id; /* Trivial promotion to unsigned. */
if (k2.u32 == 0 ||
(k->id == CTID_INT32 && k->u32 == 0x80000000u && k2.i32 == -1))
cp_err(cp, LJ_ERR_BADVAL);
if (k->id == CTID_INT32)
k->i32 = k->i32 / k2.i32;
else
k->u32 = k->u32 / k2.u32;
continue;
} else if (cp_opt(cp, '%')) {
cp_expr_unary(cp, &k2);
if (k2.id > k->id) k->id = k2.id; /* Trivial promotion to unsigned. */
if (k2.u32 == 0 ||
(k->id == CTID_INT32 && k->u32 == 0x80000000u && k2.i32 == -1))
cp_err(cp, LJ_ERR_BADVAL);
if (k->id == CTID_INT32)
k->i32 = k->i32 % k2.i32;
else
k->u32 = k->u32 % k2.u32;
continue;
}
default:
return;
}
}
}
/* Parse and evaluate unary expression. */
static void cp_expr_unary(CPState *cp, CPValue *k)
{
if (++cp->depth > CPARSE_MAX_DECLDEPTH) cp_err(cp, LJ_ERR_XLEVELS);
cp_expr_prefix(cp, k);
cp_expr_postfix(cp, k);
cp->depth--;
}
/* Parse and evaluate sub-expression. */
static void cp_expr_sub(CPState *cp, CPValue *k, int pri)
{
cp_expr_unary(cp, k);
cp_expr_infix(cp, k, pri);
}
/* Parse constant integer expression. */
static void cp_expr_kint(CPState *cp, CPValue *k)
{
CType *ct;
cp_expr_sub(cp, k, 0);
ct = ctype_raw(cp->cts, k->id);
if (!ctype_isinteger(ct->info)) cp_err(cp, LJ_ERR_BADVAL);
}
/* Parse (non-negative) size expression. */
static CTSize cp_expr_ksize(CPState *cp)
{
CPValue k;
cp_expr_kint(cp, &k);
if (k.u32 >= 0x80000000u) cp_err(cp, LJ_ERR_FFI_INVSIZE);
return k.u32;
}
/* -- Type declaration stack management ----------------------------------- */
/* Add declaration element behind the insertion position. */
static CPDeclIdx cp_add(CPDecl *decl, CTInfo info, CTSize size)
{
CPDeclIdx top = decl->top;
if (top >= CPARSE_MAX_DECLSTACK) cp_err(decl->cp, LJ_ERR_XLEVELS);
decl->stack[top].info = info;
decl->stack[top].size = size;
decl->stack[top].sib = 0;
setgcrefnull(decl->stack[top].name);
decl->stack[top].next = decl->stack[decl->pos].next;
decl->stack[decl->pos].next = (CTypeID1)top;
decl->top = top+1;
return top;
}
/* Push declaration element before the insertion position. */
static CPDeclIdx cp_push(CPDecl *decl, CTInfo info, CTSize size)
{
return (decl->pos = cp_add(decl, info, size));
}
/* Push or merge attributes. */
static void cp_push_attributes(CPDecl *decl)
{
CType *ct = &decl->stack[decl->pos];
if (ctype_isfunc(ct->info)) { /* Ok to modify in-place. */
#if LJ_TARGET_X86
if ((decl->fattr & CTFP_CCONV))
ct->info = (ct->info & (CTMASK_NUM|CTF_VARARG|CTMASK_CID)) +
(decl->fattr & ~CTMASK_CID);
#endif
} else {
if ((decl->attr & CTFP_ALIGNED) && !(decl->mode & CPARSE_MODE_FIELD))
cp_push(decl, CTINFO(CT_ATTRIB, CTATTRIB(CTA_ALIGN)),
ctype_align(decl->attr));
}
}
/* Push unrolled type to declaration stack and merge qualifiers. */
static void cp_push_type(CPDecl *decl, CTypeID id)
{
CType *ct = ctype_get(decl->cp->cts, id);
CTInfo info = ct->info;
CTSize size = ct->size;
switch (ctype_type(info)) {
case CT_STRUCT: case CT_ENUM:
cp_push(decl, CTINFO(CT_TYPEDEF, id), 0); /* Don't copy unique types. */
if ((decl->attr & CTF_QUAL)) { /* Push unmerged qualifiers. */
cp_push(decl, CTINFO(CT_ATTRIB, CTATTRIB(CTA_QUAL)),
(decl->attr & CTF_QUAL));
decl->attr &= ~CTF_QUAL;
}
break;
case CT_ATTRIB:
if (ctype_isxattrib(info, CTA_QUAL))
decl->attr &= ~size; /* Remove redundant qualifiers. */
cp_push_type(decl, ctype_cid(info)); /* Unroll. */
cp_push(decl, info & ~CTMASK_CID, size); /* Copy type. */
break;
case CT_ARRAY:
if ((ct->info & (CTF_VECTOR|CTF_COMPLEX))) {
info |= (decl->attr & CTF_QUAL);
decl->attr &= ~CTF_QUAL;
}
cp_push_type(decl, ctype_cid(info)); /* Unroll. */
cp_push(decl, info & ~CTMASK_CID, size); /* Copy type. */
decl->stack[decl->pos].sib = 1; /* Mark as already checked and sized. */
/* Note: this is not copied to the ct->sib in the C type table. */
break;
case CT_FUNC:
/* Copy type, link parameters (shared). */
decl->stack[cp_push(decl, info, size)].sib = ct->sib;
break;
default:
/* Copy type, merge common qualifiers. */
cp_push(decl, info|(decl->attr & CTF_QUAL), size);
decl->attr &= ~CTF_QUAL;
break;
}
}
/* Consume the declaration element chain and intern the C type. */
static CTypeID cp_decl_intern(CPState *cp, CPDecl *decl)
{
CTypeID id = 0;
CPDeclIdx idx = 0;
CTSize csize = CTSIZE_INVALID;
CTSize cinfo = 0;
do {
CType *ct = &decl->stack[idx];
CTInfo info = ct->info;
CTInfo size = ct->size;
/* The cid is already part of info for copies of pointers/functions. */
idx = ct->next;
if (ctype_istypedef(info)) {
lua_assert(id == 0);
id = ctype_cid(info);
/* Always refetch info/size, since struct/enum may have been completed. */
cinfo = ctype_get(cp->cts, id)->info;
csize = ctype_get(cp->cts, id)->size;
lua_assert(ctype_isstruct(cinfo) || ctype_isenum(cinfo));
} else if (ctype_isfunc(info)) { /* Intern function. */
CType *fct;
CTypeID fid;
CTypeID sib;
if (id) {
CType *refct = ctype_raw(cp->cts, id);
/* Reject function or refarray return types. */
if (ctype_isfunc(refct->info) || ctype_isrefarray(refct->info))
cp_err(cp, LJ_ERR_FFI_INVTYPE);
}
/* No intervening attributes allowed, skip forward. */
while (idx) {
CType *ctn = &decl->stack[idx];
if (!ctype_isattrib(ctn->info)) break;
idx = ctn->next; /* Skip attribute. */
}
sib = ct->sib; /* Next line may reallocate the C type table. */
fid = lj_ctype_new(cp->cts, &fct);
csize = CTSIZE_INVALID;
fct->info = cinfo = info + id;
fct->size = size;
fct->sib = sib;
id = fid;
} else if (ctype_isattrib(info)) {
if (ctype_isxattrib(info, CTA_QUAL))
cinfo |= size;
else if (ctype_isxattrib(info, CTA_ALIGN))
CTF_INSERT(cinfo, ALIGN, size);
id = lj_ctype_intern(cp->cts, info+id, size);
/* Inherit csize/cinfo from original type. */
} else {
if (ctype_isnum(info)) { /* Handle mode/vector-size attributes. */
lua_assert(id == 0);
if (!(info & CTF_BOOL)) {
CTSize msize = ctype_msizeP(decl->attr);
CTSize vsize = ctype_vsizeP(decl->attr);
if (msize && (!(info & CTF_FP) || (msize == 4 || msize == 8))) {
CTSize malign = lj_fls(msize);
if (malign > 4) malign = 4; /* Limit alignment. */
CTF_INSERT(info, ALIGN, malign);
size = msize; /* Override size via mode. */
}
if (vsize) { /* Vector size set? */
CTSize esize = lj_fls(size);
if (vsize >= esize) {
/* Intern the element type first. */
id = lj_ctype_intern(cp->cts, info, size);
/* Then create a vector (array) with vsize alignment. */
size = (1u << vsize);
if (vsize > 4) vsize = 4; /* Limit alignment. */
if (ctype_align(info) > vsize) vsize = ctype_align(info);
info = CTINFO(CT_ARRAY, (info & CTF_QUAL) + CTF_VECTOR +
CTALIGN(vsize));
}
}
}
} else if (ctype_isptr(info)) {
/* Reject pointer/ref to ref. */
if (id && ctype_isref(ctype_raw(cp->cts, id)->info))
cp_err(cp, LJ_ERR_FFI_INVTYPE);
if (ctype_isref(info)) {
info &= ~CTF_VOLATILE; /* Refs are always const, never volatile. */
/* No intervening attributes allowed, skip forward. */
while (idx) {
CType *ctn = &decl->stack[idx];
if (!ctype_isattrib(ctn->info)) break;
idx = ctn->next; /* Skip attribute. */
}
}
} else if (ctype_isarray(info)) { /* Check for valid array size etc. */
if (ct->sib == 0) { /* Only check/size arrays not copied by unroll. */
if (ctype_isref(cinfo)) /* Reject arrays of refs. */
cp_err(cp, LJ_ERR_FFI_INVTYPE);
/* Reject VLS or unknown-sized types. */
if (ctype_isvltype(cinfo) || csize == CTSIZE_INVALID)
cp_err(cp, LJ_ERR_FFI_INVSIZE);
/* a[] and a[?] keep their invalid size. */
if (size != CTSIZE_INVALID) {
uint64_t xsz = (uint64_t)size * csize;
if (xsz >= 0x80000000u) cp_err(cp, LJ_ERR_FFI_INVSIZE);
size = (CTSize)xsz;
}
}
if ((cinfo & CTF_ALIGN) > (info & CTF_ALIGN)) /* Find max. align. */
info = (info & ~CTF_ALIGN) | (cinfo & CTF_ALIGN);
info |= (cinfo & CTF_QUAL); /* Inherit qual. */
} else {
lua_assert(ctype_isvoid(info));
}
csize = size;
cinfo = info+id;
id = lj_ctype_intern(cp->cts, info+id, size);
}
} while (idx);
return id;
}
/* -- C declaration parser ------------------------------------------------ */
#define H_(le, be) LJ_ENDIAN_SELECT(0x##le, 0x##be)
/* Reset declaration state to declaration specifier. */
static void cp_decl_reset(CPDecl *decl)
{
decl->pos = decl->specpos;
decl->top = decl->specpos+1;
decl->stack[decl->specpos].next = 0;
decl->attr = decl->specattr;
decl->fattr = decl->specfattr;
decl->name = NULL;
decl->redir = NULL;
}
/* Parse constant initializer. */
/* NYI: FP constants and strings as initializers. */
static CTypeID cp_decl_constinit(CPState *cp, CType **ctp, CTypeID ctypeid)
{
CType *ctt = ctype_get(cp->cts, ctypeid);
CTInfo info;
CTSize size;
CPValue k;
CTypeID constid;
while (ctype_isattrib(ctt->info)) { /* Skip attributes. */
ctypeid = ctype_cid(ctt->info); /* Update ID, too. */
ctt = ctype_get(cp->cts, ctypeid);
}
info = ctt->info;
size = ctt->size;
if (!ctype_isinteger(info) || !(info & CTF_CONST) || size > 4)
cp_err(cp, LJ_ERR_FFI_INVTYPE);
cp_check(cp, '=');
cp_expr_sub(cp, &k, 0);
constid = lj_ctype_new(cp->cts, ctp);
(*ctp)->info = CTINFO(CT_CONSTVAL, CTF_CONST|ctypeid);
k.u32 <<= 8*(4-size);
if ((info & CTF_UNSIGNED))
k.u32 >>= 8*(4-size);
else
k.u32 = (uint32_t)((int32_t)k.u32 >> 8*(4-size));
(*ctp)->size = k.u32;
return constid;
}
/* Parse size in parentheses as part of attribute. */
static CTSize cp_decl_sizeattr(CPState *cp)
{
CTSize sz;
uint32_t oldtmask = cp->tmask;
cp->tmask = CPNS_DEFAULT; /* Required for expression evaluator. */
cp_check(cp, '(');
sz = cp_expr_ksize(cp);
cp->tmask = oldtmask;
cp_check(cp, ')');
return sz;
}
/* Parse alignment attribute. */
static void cp_decl_align(CPState *cp, CPDecl *decl)
{
CTSize al = 4; /* Unspecified alignment is 16 bytes. */
if (cp->tok == '(') {
al = cp_decl_sizeattr(cp);
al = al ? lj_fls(al) : 0;
}
CTF_INSERT(decl->attr, ALIGN, al);
decl->attr |= CTFP_ALIGNED;
}
/* Parse GCC asm("name") redirect. */
static void cp_decl_asm(CPState *cp, CPDecl *decl)
{
UNUSED(decl);
cp_next(cp);
cp_check(cp, '(');
if (cp->tok == CTOK_STRING) {
GCstr *str = cp->str;
while (cp_next(cp) == CTOK_STRING) {
lj_str_pushf(cp->L, "%s%s", strdata(str), strdata(cp->str));
cp->L->top--;
str = strV(cp->L->top);
}
decl->redir = str;
}
cp_check(cp, ')');
}
/* Parse GCC __attribute__((mode(...))). */
static void cp_decl_mode(CPState *cp, CPDecl *decl)
{
cp_check(cp, '(');
if (cp->tok == CTOK_IDENT) {
const char *s = strdata(cp->str);
CTSize sz = 0, vlen = 0;
if (s[0] == '_' && s[1] == '_') s += 2;
if (*s == 'V') {
s++;
vlen = *s++ - '0';
if (*s >= '0' && *s <= '9')
vlen = vlen*10 + (*s++ - '0');
}
switch (*s++) {
case 'Q': sz = 1; break;
case 'H': sz = 2; break;
case 'S': sz = 4; break;
case 'D': sz = 8; break;
case 'T': sz = 16; break;
case 'O': sz = 32; break;
default: goto bad_size;
}
if (*s == 'I' || *s == 'F') {
CTF_INSERT(decl->attr, MSIZEP, sz);
if (vlen) CTF_INSERT(decl->attr, VSIZEP, lj_fls(vlen*sz));
}
bad_size:
cp_next(cp);
}
cp_check(cp, ')');
}
/* Parse GCC __attribute__((...)). */
static void cp_decl_gccattribute(CPState *cp, CPDecl *decl)
{
cp_next(cp);
cp_check(cp, '(');
cp_check(cp, '(');
while (cp->tok != ')') {
if (cp->tok == CTOK_IDENT) {
GCstr *attrstr = cp->str;
cp_next(cp);
switch (attrstr->hash) {
case H_(64a9208e,8ce14319): case H_(8e6331b2,95a282af): /* aligned */
cp_decl_align(cp, decl);
break;
case H_(42eb47de,f0ede26c): case H_(29f48a09,cf383e0c): /* packed */
decl->attr |= CTFP_PACKED;
break;
case H_(0a84eef6,8dfab04c): case H_(995cf92c,d5696591): /* mode */
cp_decl_mode(cp, decl);
break;
case H_(0ab31997,2d5213fa): case H_(bf875611,200e9990): /* vector_size */
{
CTSize vsize = cp_decl_sizeattr(cp);
if (vsize) CTF_INSERT(decl->attr, VSIZEP, lj_fls(vsize));
}
break;
#if LJ_TARGET_X86
case H_(5ad22db8,c689b848): case H_(439150fa,65ea78cb): /* regparm */
CTF_INSERT(decl->fattr, REGPARM, cp_decl_sizeattr(cp));
decl->fattr |= CTFP_CCONV;
break;
case H_(18fc0b98,7ff4c074): case H_(4e62abed,0a747424): /* cdecl */
CTF_INSERT(decl->fattr, CCONV, CTCC_CDECL);
decl->fattr |= CTFP_CCONV;
break;
case H_(72b2e41b,494c5a44): case H_(f2356d59,f25fc9bd): /* thiscall */
CTF_INSERT(decl->fattr, CCONV, CTCC_THISCALL);
decl->fattr |= CTFP_CCONV;
break;
case H_(0d0ffc42,ab746f88): case H_(21c54ba1,7f0ca7e3): /* fastcall */
CTF_INSERT(decl->fattr, CCONV, CTCC_FASTCALL);
decl->fattr |= CTFP_CCONV;
break;
case H_(ef76b040,9412e06a): case H_(de56697b,c750e6e1): /* stdcall */
CTF_INSERT(decl->fattr, CCONV, CTCC_STDCALL);
decl->fattr |= CTFP_CCONV;
break;
case H_(ea78b622,f234bd8e): case H_(252ffb06,8d50f34b): /* sseregparm */
decl->fattr |= CTF_SSEREGPARM;
decl->fattr |= CTFP_CCONV;
break;
#endif
default: /* Skip all other attributes. */
goto skip_attr;
}
} else if (cp->tok >= CTOK_FIRSTDECL) { /* For __attribute((const)) etc. */
cp_next(cp);
skip_attr:
if (cp_opt(cp, '(')) {
while (cp->tok != ')' && cp->tok != CTOK_EOF) cp_next(cp);
cp_check(cp, ')');
}
} else {
break;
}
if (!cp_opt(cp, ',')) break;
}
cp_check(cp, ')');
cp_check(cp, ')');
}
/* Parse MSVC __declspec(...). */
static void cp_decl_msvcattribute(CPState *cp, CPDecl *decl)
{
cp_next(cp);
cp_check(cp, '(');
while (cp->tok == CTOK_IDENT) {
GCstr *attrstr = cp->str;
cp_next(cp);
switch (attrstr->hash) {
case H_(bc2395fa,98f267f8): /* align */
cp_decl_align(cp, decl);
break;
default: /* Ignore all other attributes. */
if (cp_opt(cp, '(')) {
while (cp->tok != ')' && cp->tok != CTOK_EOF) cp_next(cp);
cp_check(cp, ')');
}
break;
}
}
cp_check(cp, ')');
}
/* Parse declaration attributes (and common qualifiers). */
static void cp_decl_attributes(CPState *cp, CPDecl *decl)
{
for (;;) {
switch (cp->tok) {
case CTOK_CONST: decl->attr |= CTF_CONST; break;
case CTOK_VOLATILE: decl->attr |= CTF_VOLATILE; break;
case CTOK_RESTRICT: break; /* Ignore. */
case CTOK_EXTENSION: break; /* Ignore. */
case CTOK_ATTRIBUTE: cp_decl_gccattribute(cp, decl); continue;
case CTOK_ASM: cp_decl_asm(cp, decl); continue;
case CTOK_DECLSPEC: cp_decl_msvcattribute(cp, decl); continue;
case CTOK_CCDECL:
#if LJ_TARGET_X86
CTF_INSERT(decl->fattr, CCONV, cp->ct->size);
decl->fattr |= CTFP_CCONV;
#endif
break;
case CTOK_PTRSZ:
#if LJ_64
CTF_INSERT(decl->attr, MSIZEP, cp->ct->size);
#endif
break;
default: return;
}
cp_next(cp);
}
}
/* Parse struct/union/enum name. */
static CTypeID cp_struct_name(CPState *cp, CPDecl *sdecl, CTInfo info)
{
CTypeID sid;
CType *ct;
cp->tmask = CPNS_STRUCT;
cp_next(cp);
cp_decl_attributes(cp, sdecl);
cp->tmask = CPNS_DEFAULT;
if (cp->tok != '{') {
if (cp->tok != CTOK_IDENT) cp_err_token(cp, CTOK_IDENT);
if (cp->val.id) { /* Name of existing struct/union/enum. */
sid = cp->val.id;
ct = cp->ct;
if ((ct->info ^ info) & (CTMASK_NUM|CTF_UNION)) /* Wrong type. */
cp_errmsg(cp, 0, LJ_ERR_FFI_REDEF, strdata(gco2str(gcref(ct->name))));
} else { /* Create named, incomplete struct/union/enum. */
if ((cp->mode & CPARSE_MODE_NOIMPLICIT))
cp_errmsg(cp, 0, LJ_ERR_FFI_BADTAG, strdata(cp->str));
sid = lj_ctype_new(cp->cts, &ct);
ct->info = info;
ct->size = CTSIZE_INVALID;
ctype_setname(ct, cp->str);
lj_ctype_addname(cp->cts, ct, sid);
}
cp_next(cp);
} else { /* Create anonymous, incomplete struct/union/enum. */
sid = lj_ctype_new(cp->cts, &ct);
ct->info = info;
ct->size = CTSIZE_INVALID;
}
if (cp->tok == '{') {
if (ct->size != CTSIZE_INVALID || ct->sib)
cp_errmsg(cp, 0, LJ_ERR_FFI_REDEF, strdata(gco2str(gcref(ct->name))));
ct->sib = 1; /* Indicate the type is currently being defined. */
}
return sid;
}
/* Determine field alignment. */
static CTSize cp_field_align(CPState *cp, CType *ct, CTInfo info)
{
CTSize align = ctype_align(info);
UNUSED(cp); UNUSED(ct);
#if (LJ_TARGET_X86 && !LJ_ABI_WIN) || (LJ_TARGET_ARM && __APPLE__)
/* The SYSV i386 and iOS ABIs limit alignment of non-vector fields to 2^2. */
if (align > 2 && !(info & CTFP_ALIGNED)) {
if (ctype_isarray(info) && !(info & CTF_VECTOR)) {
do {
ct = ctype_rawchild(cp->cts, ct);
info = ct->info;
} while (ctype_isarray(info) && !(info & CTF_VECTOR));
}
if (ctype_isnum(info) || ctype_isenum(info))
align = 2;
}
#endif
return align;
}
/* Layout struct/union fields. */
static void cp_struct_layout(CPState *cp, CTypeID sid, CTInfo sattr)
{
CTSize bofs = 0, bmaxofs = 0; /* Bit offset and max. bit offset. */
CTSize maxalign = ctype_align(sattr);
CType *sct = ctype_get(cp->cts, sid);
CTInfo sinfo = sct->info;
CTypeID fieldid = sct->sib;
while (fieldid) {
CType *ct = ctype_get(cp->cts, fieldid);
CTInfo attr = ct->size; /* Field declaration attributes (temp.). */
if (ctype_isfield(ct->info) ||
(ctype_isxattrib(ct->info, CTA_SUBTYPE) && attr)) {
CTSize align, amask; /* Alignment (pow2) and alignment mask (bits). */
CTSize sz;
CTInfo info = lj_ctype_info(cp->cts, ctype_cid(ct->info), &sz);
CTSize bsz, csz = 8*sz; /* Field size and container size (in bits). */
sinfo |= (info & (CTF_QUAL|CTF_VLA)); /* Merge pseudo-qualifiers. */
/* Check for size overflow and determine alignment. */
if (sz >= 0x20000000u || bofs + csz < bofs || (info & CTF_VLA)) {
if (!(sz == CTSIZE_INVALID && ctype_isarray(info) &&
!(sinfo & CTF_UNION)))
cp_err(cp, LJ_ERR_FFI_INVSIZE);
csz = sz = 0; /* Treat a[] and a[?] as zero-sized. */
}
align = cp_field_align(cp, ct, info);
if (((attr|sattr) & CTFP_PACKED) ||
((attr & CTFP_ALIGNED) && ctype_align(attr) > align))
align = ctype_align(attr);
if (cp->packstack[cp->curpack] < align)
align = cp->packstack[cp->curpack];
if (align > maxalign) maxalign = align;
amask = (8u << align) - 1;
bsz = ctype_bitcsz(ct->info); /* Bitfield size (temp.). */
if (bsz == CTBSZ_FIELD || !ctype_isfield(ct->info)) {
bsz = csz; /* Regular fields or subtypes always fill the container. */
bofs = (bofs + amask) & ~amask; /* Start new aligned field. */
ct->size = (bofs >> 3); /* Store field offset. */
} else { /* Bitfield. */
if (bsz == 0 || (attr & CTFP_ALIGNED) ||
(!((attr|sattr) & CTFP_PACKED) && (bofs & amask) + bsz > csz))
bofs = (bofs + amask) & ~amask; /* Start new aligned field. */
/* Prefer regular field over bitfield. */
if (bsz == csz && (bofs & amask) == 0) {
ct->info = CTINFO(CT_FIELD, ctype_cid(ct->info));
ct->size = (bofs >> 3); /* Store field offset. */
} else {
ct->info = CTINFO(CT_BITFIELD,
(info & (CTF_QUAL|CTF_UNSIGNED|CTF_BOOL)) +
(csz << (CTSHIFT_BITCSZ-3)) + (bsz << CTSHIFT_BITBSZ));
#if LJ_BE
ct->info += ((csz - (bofs & (csz-1)) - bsz) << CTSHIFT_BITPOS);
#else
ct->info += ((bofs & (csz-1)) << CTSHIFT_BITPOS);
#endif
ct->size = ((bofs & ~(csz-1)) >> 3); /* Store container offset. */
}
}
/* Determine next offset or max. offset. */
if ((sinfo & CTF_UNION)) {
if (bsz > bmaxofs) bmaxofs = bsz;
} else {
bofs += bsz;
}
} /* All other fields in the chain are already set up. */
fieldid = ct->sib;
}
/* Complete struct/union. */
sct->info = sinfo + CTALIGN(maxalign);
bofs = (sinfo & CTF_UNION) ? bmaxofs : bofs;
maxalign = (8u << maxalign) - 1;
sct->size = (((bofs + maxalign) & ~maxalign) >> 3);
}
/* Parse struct/union declaration. */
static CTypeID cp_decl_struct(CPState *cp, CPDecl *sdecl, CTInfo sinfo)
{
CTypeID sid = cp_struct_name(cp, sdecl, sinfo);
if (cp_opt(cp, '{')) { /* Struct/union definition. */
CTypeID lastid = sid;
int lastdecl = 0;
while (cp->tok != '}') {
CPDecl decl;
CPscl scl = cp_decl_spec(cp, &decl, CDF_STATIC);
decl.mode = scl ? CPARSE_MODE_DIRECT :
CPARSE_MODE_DIRECT|CPARSE_MODE_ABSTRACT|CPARSE_MODE_FIELD;
for (;;) {
CTypeID ctypeid;
if (lastdecl) cp_err_token(cp, '}');
/* Parse field declarator. */
decl.bits = CTSIZE_INVALID;
cp_declarator(cp, &decl);
ctypeid = cp_decl_intern(cp, &decl);
if ((scl & CDF_STATIC)) { /* Static constant in struct namespace. */
CType *ct;
CTypeID fieldid = cp_decl_constinit(cp, &ct, ctypeid);
ctype_get(cp->cts, lastid)->sib = fieldid;
lastid = fieldid;
ctype_setname(ct, decl.name);
} else {
CTSize bsz = CTBSZ_FIELD; /* Temp. for layout phase. */
CType *ct;
CTypeID fieldid = lj_ctype_new(cp->cts, &ct); /* Do this first. */
CType *tct = ctype_raw(cp->cts, ctypeid);
if (decl.bits == CTSIZE_INVALID) { /* Regular field. */
if (ctype_isarray(tct->info) && tct->size == CTSIZE_INVALID)
lastdecl = 1; /* a[] or a[?] must be the last declared field. */
/* Accept transparent struct/union/enum. */
if (!decl.name) {
if (!((ctype_isstruct(tct->info) && !(tct->info & CTF_VLA)) ||
ctype_isenum(tct->info)))
cp_err_token(cp, CTOK_IDENT);
ct->info = CTINFO(CT_ATTRIB, CTATTRIB(CTA_SUBTYPE) + ctypeid);
ct->size = ctype_isstruct(tct->info) ?
(decl.attr|0x80000000u) : 0; /* For layout phase. */
goto add_field;
}
} else { /* Bitfield. */
bsz = decl.bits;
if (!ctype_isinteger_or_bool(tct->info) ||
(bsz == 0 && decl.name) || 8*tct->size > CTBSZ_MAX ||
bsz > ((tct->info & CTF_BOOL) ? 1 : 8*tct->size))
cp_errmsg(cp, ':', LJ_ERR_BADVAL);
}
/* Create temporary field for layout phase. */
ct->info = CTINFO(CT_FIELD, ctypeid + (bsz << CTSHIFT_BITCSZ));
ct->size = decl.attr;
if (decl.name) ctype_setname(ct, decl.name);
add_field:
ctype_get(cp->cts, lastid)->sib = fieldid;
lastid = fieldid;
}
if (!cp_opt(cp, ',')) break;
cp_decl_reset(&decl);
}
cp_check(cp, ';');
}
cp_check(cp, '}');
ctype_get(cp->cts, lastid)->sib = 0; /* Drop sib = 1 for empty structs. */
cp_decl_attributes(cp, sdecl); /* Layout phase needs postfix attributes. */
cp_struct_layout(cp, sid, sdecl->attr);
}
return sid;
}
/* Parse enum declaration. */
static CTypeID cp_decl_enum(CPState *cp, CPDecl *sdecl)
{
CTypeID eid = cp_struct_name(cp, sdecl, CTINFO(CT_ENUM, CTID_VOID));
CTInfo einfo = CTINFO(CT_ENUM, CTALIGN(2) + CTID_UINT32);
CTSize esize = 4; /* Only 32 bit enums are supported. */
if (cp_opt(cp, '{')) { /* Enum definition. */
CPValue k;
CTypeID lastid = eid;
k.u32 = 0;
k.id = CTID_INT32;
do {
GCstr *name = cp->str;
if (cp->tok != CTOK_IDENT) cp_err_token(cp, CTOK_IDENT);
if (cp->val.id) cp_errmsg(cp, 0, LJ_ERR_FFI_REDEF, strdata(name));
cp_next(cp);
if (cp_opt(cp, '=')) {
cp_expr_kint(cp, &k);
if (k.id == CTID_UINT32) {
/* C99 says that enum constants are always (signed) integers.
** But since unsigned constants like 0x80000000 are quite common,
** those are left as uint32_t.
*/
if (k.i32 >= 0) k.id = CTID_INT32;
} else {
/* OTOH it's common practice and even mandated by some ABIs
** that the enum type itself is unsigned, unless there are any
** negative constants.
*/
k.id = CTID_INT32;
if (k.i32 < 0) einfo = CTINFO(CT_ENUM, CTALIGN(2) + CTID_INT32);
}
}
/* Add named enum constant. */
{
CType *ct;
CTypeID constid = lj_ctype_new(cp->cts, &ct);
ctype_get(cp->cts, lastid)->sib = constid;
lastid = constid;
ctype_setname(ct, name);
ct->info = CTINFO(CT_CONSTVAL, CTF_CONST|k.id);
ct->size = k.u32++;
if (k.u32 == 0x80000000u) k.id = CTID_UINT32;
lj_ctype_addname(cp->cts, ct, constid);
}
if (!cp_opt(cp, ',')) break;
} while (cp->tok != '}'); /* Trailing ',' is ok. */
cp_check(cp, '}');
/* Complete enum. */
ctype_get(cp->cts, eid)->info = einfo;
ctype_get(cp->cts, eid)->size = esize;
}
return eid;
}
/* Parse declaration specifiers. */
static CPscl cp_decl_spec(CPState *cp, CPDecl *decl, CPscl scl)
{
uint32_t cds = 0, sz = 0;
CTypeID tdef = 0;
decl->cp = cp;
decl->mode = cp->mode;
decl->name = NULL;
decl->redir = NULL;
decl->attr = 0;
decl->fattr = 0;
decl->pos = decl->top = 0;
decl->stack[0].next = 0;
for (;;) { /* Parse basic types. */
cp_decl_attributes(cp, decl);
if (cp->tok >= CTOK_FIRSTDECL && cp->tok <= CTOK_LASTDECLFLAG) {
uint32_t cbit;
if (cp->ct->size) {
if (sz) goto end_decl;
sz = cp->ct->size;
}
cbit = (1u << (cp->tok - CTOK_FIRSTDECL));
cds = cds | cbit | ((cbit & cds & CDF_LONG) << 1);
if (cp->tok >= CTOK_FIRSTSCL) {
if (!(scl & cbit)) cp_errmsg(cp, cp->tok, LJ_ERR_FFI_BADSCL);
} else if (tdef) {
goto end_decl;
}
cp_next(cp);
continue;
}
if (sz || tdef ||
(cds & (CDF_SHORT|CDF_LONG|CDF_SIGNED|CDF_UNSIGNED|CDF_COMPLEX)))
break;
switch (cp->tok) {
case CTOK_STRUCT:
tdef = cp_decl_struct(cp, decl, CTINFO(CT_STRUCT, 0));
continue;
case CTOK_UNION:
tdef = cp_decl_struct(cp, decl, CTINFO(CT_STRUCT, CTF_UNION));
continue;
case CTOK_ENUM:
tdef = cp_decl_enum(cp, decl);
continue;
case CTOK_IDENT:
if (ctype_istypedef(cp->ct->info)) {
tdef = ctype_cid(cp->ct->info); /* Get typedef. */
cp_next(cp);
continue;
}
break;
case '$':
tdef = cp->val.id;
cp_next(cp);
continue;
default:
break;
}
break;
}
end_decl:
if ((cds & CDF_COMPLEX)) /* Use predefined complex types. */
tdef = sz == 4 ? CTID_COMPLEX_FLOAT : CTID_COMPLEX_DOUBLE;
if (tdef) {
cp_push_type(decl, tdef);
} else if ((cds & CDF_VOID)) {
cp_push(decl, CTINFO(CT_VOID, (decl->attr & CTF_QUAL)), CTSIZE_INVALID);
decl->attr &= ~CTF_QUAL;
} else {
/* Determine type info and size. */
CTInfo info = CTINFO(CT_NUM, (cds & CDF_UNSIGNED) ? CTF_UNSIGNED : 0);
if ((cds & CDF_BOOL)) {
if ((cds & ~(CDF_SCL|CDF_BOOL|CDF_INT|CDF_SIGNED|CDF_UNSIGNED)))
cp_errmsg(cp, 0, LJ_ERR_FFI_INVTYPE);
info |= CTF_BOOL;
if (!(cds & CDF_SIGNED)) info |= CTF_UNSIGNED;
if (!sz) {
sz = 1;
}
} else if ((cds & CDF_FP)) {
info = CTINFO(CT_NUM, CTF_FP);
if ((cds & CDF_LONG)) sz = sizeof(long double);
} else if ((cds & CDF_CHAR)) {
if ((cds & (CDF_CHAR|CDF_SIGNED|CDF_UNSIGNED)) == CDF_CHAR)
info |= CTF_UCHAR; /* Handle platforms where char is unsigned. */
} else if ((cds & CDF_SHORT)) {
sz = sizeof(short);
} else if ((cds & CDF_LONGLONG)) {
sz = 8;
} else if ((cds & CDF_LONG)) {
info |= CTF_LONG;
sz = sizeof(long);
} else if (!sz) {
if (!(cds & (CDF_SIGNED|CDF_UNSIGNED)))
cp_errmsg(cp, cp->tok, LJ_ERR_FFI_DECLSPEC);
sz = sizeof(int);
}
lua_assert(sz != 0);
info += CTALIGN(lj_fls(sz)); /* Use natural alignment. */
info += (decl->attr & CTF_QUAL); /* Merge qualifiers. */
cp_push(decl, info, sz);
decl->attr &= ~CTF_QUAL;
}
decl->specpos = decl->pos;
decl->specattr = decl->attr;
decl->specfattr = decl->fattr;
return (cds & CDF_SCL); /* Return storage class. */
}
/* Parse array declaration. */
static void cp_decl_array(CPState *cp, CPDecl *decl)
{
CTInfo info = CTINFO(CT_ARRAY, 0);
CTSize nelem = CTSIZE_INVALID; /* Default size for a[] or a[?]. */
cp_decl_attributes(cp, decl);
if (cp_opt(cp, '?'))
info |= CTF_VLA; /* Create variable-length array a[?]. */
else if (cp->tok != ']')
nelem = cp_expr_ksize(cp);
cp_check(cp, ']');
cp_add(decl, info, nelem);
}
/* Parse function declaration. */
static void cp_decl_func(CPState *cp, CPDecl *fdecl)
{
CTSize nargs = 0;
CTInfo info = CTINFO(CT_FUNC, 0);
CTypeID lastid = 0, anchor = 0;
if (cp->tok != ')') {
do {
CPDecl decl;
CTypeID ctypeid, fieldid;
CType *ct;
if (cp_opt(cp, '.')) { /* Vararg function. */
cp_check(cp, '.'); /* Workaround for the minimalistic lexer. */
cp_check(cp, '.');
info |= CTF_VARARG;
break;
}
cp_decl_spec(cp, &decl, CDF_REGISTER);
decl.mode = CPARSE_MODE_DIRECT|CPARSE_MODE_ABSTRACT;
cp_declarator(cp, &decl);
ctypeid = cp_decl_intern(cp, &decl);
ct = ctype_raw(cp->cts, ctypeid);
if (ctype_isvoid(ct->info))
break;
else if (ctype_isrefarray(ct->info))
ctypeid = lj_ctype_intern(cp->cts,
CTINFO(CT_PTR, CTALIGN_PTR|ctype_cid(ct->info)), CTSIZE_PTR);
else if (ctype_isfunc(ct->info))
ctypeid = lj_ctype_intern(cp->cts,
CTINFO(CT_PTR, CTALIGN_PTR|ctypeid), CTSIZE_PTR);
/* Add new parameter. */
fieldid = lj_ctype_new(cp->cts, &ct);
if (anchor)
ctype_get(cp->cts, lastid)->sib = fieldid;
else
anchor = fieldid;
lastid = fieldid;
if (decl.name) ctype_setname(ct, decl.name);
ct->info = CTINFO(CT_FIELD, ctypeid);
ct->size = nargs++;
} while (cp_opt(cp, ','));
}
cp_check(cp, ')');
if (cp_opt(cp, '{')) { /* Skip function definition. */
int level = 1;
cp->mode |= CPARSE_MODE_SKIP;
for (;;) {
if (cp->tok == '{') level++;
else if (cp->tok == '}' && --level == 0) break;
else if (cp->tok == CTOK_EOF) cp_err_token(cp, '}');
cp_next(cp);
}
cp->mode &= ~CPARSE_MODE_SKIP;
cp->tok = ';'; /* Ok for cp_decl_multi(), error in cp_decl_single(). */
}
info |= (fdecl->fattr & ~CTMASK_CID);
fdecl->fattr = 0;
fdecl->stack[cp_add(fdecl, info, nargs)].sib = anchor;
}
/* Parse declarator. */
static void cp_declarator(CPState *cp, CPDecl *decl)
{
if (++cp->depth > CPARSE_MAX_DECLDEPTH) cp_err(cp, LJ_ERR_XLEVELS);
for (;;) { /* Head of declarator. */
if (cp_opt(cp, '*')) { /* Pointer. */
CTSize sz;
CTInfo info;
cp_decl_attributes(cp, decl);
sz = CTSIZE_PTR;
info = CTINFO(CT_PTR, CTALIGN_PTR);
#if LJ_64
if (ctype_msizeP(decl->attr) == 4) {
sz = 4;
info = CTINFO(CT_PTR, CTALIGN(2));
}
#endif
info += (decl->attr & (CTF_QUAL|CTF_REF));
decl->attr &= ~(CTF_QUAL|(CTMASK_MSIZEP<<CTSHIFT_MSIZEP));
cp_push(decl, info, sz);
} else if (cp_opt(cp, '&') || cp_opt(cp, CTOK_ANDAND)) { /* Reference. */
decl->attr &= ~(CTF_QUAL|(CTMASK_MSIZEP<<CTSHIFT_MSIZEP));
cp_push(decl, CTINFO_REF(0), CTSIZE_PTR);
} else {
break;
}
}
if (cp_opt(cp, '(')) { /* Inner declarator. */
CPDeclIdx pos;
cp_decl_attributes(cp, decl);
/* Resolve ambiguity between inner declarator and 1st function parameter. */
if ((decl->mode & CPARSE_MODE_ABSTRACT) &&
(cp->tok == ')' || cp_istypedecl(cp))) goto func_decl;
pos = decl->pos;
cp_declarator(cp, decl);
cp_check(cp, ')');
decl->pos = pos;
} else if (cp->tok == CTOK_IDENT) { /* Direct declarator. */
if (!(decl->mode & CPARSE_MODE_DIRECT)) cp_err_token(cp, CTOK_EOF);
decl->name = cp->str;
decl->nameid = cp->val.id;
cp_next(cp);
} else { /* Abstract declarator. */
if (!(decl->mode & CPARSE_MODE_ABSTRACT)) cp_err_token(cp, CTOK_IDENT);
}
for (;;) { /* Tail of declarator. */
if (cp_opt(cp, '[')) { /* Array. */
cp_decl_array(cp, decl);
} else if (cp_opt(cp, '(')) { /* Function. */
func_decl:
cp_decl_func(cp, decl);
} else {
break;
}
}
if ((decl->mode & CPARSE_MODE_FIELD) && cp_opt(cp, ':')) /* Field width. */
decl->bits = cp_expr_ksize(cp);
/* Process postfix attributes. */
cp_decl_attributes(cp, decl);
cp_push_attributes(decl);
cp->depth--;
}
/* Parse an abstract type declaration and return it's C type ID. */
static CTypeID cp_decl_abstract(CPState *cp)
{
CPDecl decl;
cp_decl_spec(cp, &decl, 0);
decl.mode = CPARSE_MODE_ABSTRACT;
cp_declarator(cp, &decl);
return cp_decl_intern(cp, &decl);
}
/* Handle pragmas. */
static void cp_pragma(CPState *cp, BCLine pragmaline)
{
cp_next(cp);
if (cp->tok == CTOK_IDENT &&
cp->str->hash == H_(e79b999f,42ca3e85)) { /* pack */
cp_next(cp);
cp_check(cp, '(');
if (cp->tok == CTOK_IDENT) {
if (cp->str->hash == H_(738e923c,a1b65954)) { /* push */
if (cp->curpack < CPARSE_MAX_PACKSTACK) {
cp->packstack[cp->curpack+1] = cp->packstack[cp->curpack];
cp->curpack++;
}
} else if (cp->str->hash == H_(6c71cf27,6c71cf27)) { /* pop */
if (cp->curpack > 0) cp->curpack--;
} else {
cp_errmsg(cp, cp->tok, LJ_ERR_XSYMBOL);
}
cp_next(cp);
if (!cp_opt(cp, ',')) goto end_pack;
}
if (cp->tok == CTOK_INTEGER) {
cp->packstack[cp->curpack] = cp->val.u32 ? lj_fls(cp->val.u32) : 0;
cp_next(cp);
} else {
cp->packstack[cp->curpack] = 255;
}
end_pack:
cp_check(cp, ')');
} else { /* Ignore all other pragmas. */
while (cp->tok != CTOK_EOF && cp->linenumber == pragmaline)
cp_next(cp);
}
}
/* Parse multiple C declarations of types or extern identifiers. */
static void cp_decl_multi(CPState *cp)
{
int first = 1;
while (cp->tok != CTOK_EOF) {
CPDecl decl;
CPscl scl;
if (cp_opt(cp, ';')) { /* Skip empty statements. */
first = 0;
continue;
}
if (cp->tok == '#') { /* Workaround, since we have no preprocessor, yet. */
BCLine pragmaline = cp->linenumber;
if (!(cp_next(cp) == CTOK_IDENT &&
cp->str->hash == H_(f5e6b4f8,1d509107))) /* pragma */
cp_errmsg(cp, cp->tok, LJ_ERR_XSYMBOL);
cp_pragma(cp, pragmaline);
continue;
}
scl = cp_decl_spec(cp, &decl, CDF_TYPEDEF|CDF_EXTERN|CDF_STATIC);
if ((cp->tok == ';' || cp->tok == CTOK_EOF) &&
ctype_istypedef(decl.stack[0].info)) {
CTInfo info = ctype_rawchild(cp->cts, &decl.stack[0])->info;
if (ctype_isstruct(info) || ctype_isenum(info))
goto decl_end; /* Accept empty declaration of struct/union/enum. */
}
for (;;) {
CTypeID ctypeid;
cp_declarator(cp, &decl);
ctypeid = cp_decl_intern(cp, &decl);
if (decl.name && !decl.nameid) { /* NYI: redeclarations are ignored. */
CType *ct;
CTypeID id;
if ((scl & CDF_TYPEDEF)) { /* Create new typedef. */
id = lj_ctype_new(cp->cts, &ct);
ct->info = CTINFO(CT_TYPEDEF, ctypeid);
goto noredir;
} else if (ctype_isfunc(ctype_get(cp->cts, ctypeid)->info)) {
/* Treat both static and extern function declarations as extern. */
ct = ctype_get(cp->cts, ctypeid);
/* We always get new anonymous functions (typedefs are copied). */
lua_assert(gcref(ct->name) == NULL);
id = ctypeid; /* Just name it. */
} else if ((scl & CDF_STATIC)) { /* Accept static constants. */
id = cp_decl_constinit(cp, &ct, ctypeid);
goto noredir;
} else { /* External references have extern or no storage class. */
id = lj_ctype_new(cp->cts, &ct);
ct->info = CTINFO(CT_EXTERN, ctypeid);
}
if (decl.redir) { /* Add attribute for redirected symbol name. */
CType *cta;
CTypeID aid = lj_ctype_new(cp->cts, &cta);
ct = ctype_get(cp->cts, id); /* Table may have been reallocated. */
cta->info = CTINFO(CT_ATTRIB, CTATTRIB(CTA_REDIR));
cta->sib = ct->sib;
ct->sib = aid;
ctype_setname(cta, decl.redir);
}
noredir:
ctype_setname(ct, decl.name);
lj_ctype_addname(cp->cts, ct, id);
}
if (!cp_opt(cp, ',')) break;
cp_decl_reset(&decl);
}
decl_end:
if (cp->tok == CTOK_EOF && first) break; /* May omit ';' for 1 decl. */
first = 0;
cp_check(cp, ';');
}
}
/* Parse a single C type declaration. */
static void cp_decl_single(CPState *cp)
{
CPDecl decl;
cp_decl_spec(cp, &decl, 0);
cp_declarator(cp, &decl);
cp->val.id = cp_decl_intern(cp, &decl);
if (cp->tok != CTOK_EOF) cp_err_token(cp, CTOK_EOF);
}
#undef H_
/* ------------------------------------------------------------------------ */
/* Protected callback for C parser. */
static TValue *cpcparser(lua_State *L, lua_CFunction dummy, void *ud)
{
CPState *cp = (CPState *)ud;
UNUSED(dummy);
cframe_errfunc(L->cframe) = -1; /* Inherit error function. */
cp_init(cp);
if ((cp->mode & CPARSE_MODE_MULTI))
cp_decl_multi(cp);
else
cp_decl_single(cp);
if (cp->param && cp->param != cp->L->top)
cp_err(cp, LJ_ERR_FFI_NUMPARAM);
lua_assert(cp->depth == 0);
return NULL;
}
/* C parser. */
int lj_cparse(CPState *cp)
{
LJ_CTYPE_SAVE(cp->cts);
int errcode = lj_vm_cpcall(cp->L, NULL, cp, cpcparser);
if (errcode)
LJ_CTYPE_RESTORE(cp->cts);
cp_cleanup(cp);
return errcode;
}
#endif