/* ** C declaration parser. ** Copyright (C) 2005-2015 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_buf.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" #include "lj_strfmt.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_strfmt_pushf(cp->L, "%c", tok); else return lj_strfmt_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); } /* Save character in buffer. */ static LJ_AINLINE void cp_save(CPState *cp, CPChar c) { lj_buf_putb(&cp->sb, 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 (sbufP(&cp->sb) == sbufB(&cp->sb)) cp_save(cp, '$'); cp_save(cp, '\0'); tokstr = sbufB(&cp->sb); } else { tokstr = cp_tok2str(cp, tok); } L = cp->L; va_start(argp, em); msg = lj_strfmt_pushvf(L, err2msg(em), argp); va_end(argp); if (tokstr) msg = lj_strfmt_pushf(L, err2msg(LJ_ERR_XNEAR), msg, tokstr); if (cp->linenumber > 1) msg = lj_strfmt_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 *)sbufB(&cp->sb), &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_buf_str(cp->L, &cp->sb); 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_buf_str(cp->L, &cp->sb); return CTOK_STRING; } else { if (sbuflen(&cp->sb) != 1) cp_err_token(cp, '\''); cp->val.i32 = (int32_t)(char)*sbufB(&cp->sb); 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_buf_reset(&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<linenumber = 1; cp->depth = 0; cp->curpack = 0; cp->packstack[0] = 255; lj_buf_init(cp->L, &cp->sb); 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_buf_free(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_strfmt_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<attr &= ~(CTF_QUAL|(CTMASK_MSIZEP<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