/* * Format String Generator for IDL Compiler * * Copyright 2005-2006 Eric Kohl * Copyright 2005-2006 Robert Shearman * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include "config.h" #include "wine/port.h" #include #include #ifdef HAVE_UNISTD_H # include #endif #include #include #include #include #include #include "widl.h" #include "utils.h" #include "parser.h" #include "header.h" #include "wine/list.h" #include "typegen.h" static const func_t *current_func; static const type_t *current_structure; static const ifref_t *current_iface; static struct list expr_eval_routines = LIST_INIT(expr_eval_routines); struct expr_eval_routine { struct list entry; const type_t *structure; unsigned int baseoff; const expr_t *expr; }; static size_t fields_memsize(const var_list_t *fields, unsigned int *align); static size_t write_struct_tfs(FILE *file, type_t *type, const char *name, unsigned int *tfsoff); static int write_embedded_types(FILE *file, const attr_list_t *attrs, type_t *type, const char *name, int write_ptr, unsigned int *tfsoff); static const var_t *find_array_or_string_in_struct(const type_t *type); const char *string_of_type(unsigned char type) { switch (type) { case RPC_FC_BYTE: return "FC_BYTE"; case RPC_FC_CHAR: return "FC_CHAR"; case RPC_FC_SMALL: return "FC_SMALL"; case RPC_FC_USMALL: return "FC_USMALL"; case RPC_FC_WCHAR: return "FC_WCHAR"; case RPC_FC_SHORT: return "FC_SHORT"; case RPC_FC_USHORT: return "FC_USHORT"; case RPC_FC_LONG: return "FC_LONG"; case RPC_FC_ULONG: return "FC_ULONG"; case RPC_FC_FLOAT: return "FC_FLOAT"; case RPC_FC_HYPER: return "FC_HYPER"; case RPC_FC_DOUBLE: return "FC_DOUBLE"; case RPC_FC_ENUM16: return "FC_ENUM16"; case RPC_FC_ENUM32: return "FC_ENUM32"; case RPC_FC_IGNORE: return "FC_IGNORE"; case RPC_FC_ERROR_STATUS_T: return "FC_ERROR_STATUS_T"; case RPC_FC_RP: return "FC_RP"; case RPC_FC_UP: return "FC_UP"; case RPC_FC_OP: return "FC_OP"; case RPC_FC_FP: return "FC_FP"; case RPC_FC_ENCAPSULATED_UNION: return "FC_ENCAPSULATED_UNION"; case RPC_FC_NON_ENCAPSULATED_UNION: return "FC_NON_ENCAPSULATED_UNION"; case RPC_FC_STRUCT: return "FC_STRUCT"; case RPC_FC_PSTRUCT: return "FC_PSTRUCT"; case RPC_FC_CSTRUCT: return "FC_CSTRUCT"; case RPC_FC_CPSTRUCT: return "FC_CPSTRUCT"; case RPC_FC_CVSTRUCT: return "FC_CVSTRUCT"; case RPC_FC_BOGUS_STRUCT: return "FC_BOGUS_STRUCT"; case RPC_FC_SMFARRAY: return "FC_SMFARRAY"; case RPC_FC_LGFARRAY: return "FC_LGFARRAY"; case RPC_FC_SMVARRAY: return "FC_SMVARRAY"; case RPC_FC_LGVARRAY: return "FC_LGVARRAY"; case RPC_FC_CARRAY: return "FC_CARRAY"; case RPC_FC_CVARRAY: return "FC_CVARRAY"; case RPC_FC_BOGUS_ARRAY: return "FC_BOGUS_ARRAY"; case RPC_FC_ALIGNM4: return "FC_ALIGNM4"; case RPC_FC_ALIGNM8: return "FC_ALIGNM8"; case RPC_FC_POINTER: return "FC_POINTER"; case RPC_FC_C_CSTRING: return "FC_C_CSTRING"; case RPC_FC_C_WSTRING: return "FC_C_WSTRING"; case RPC_FC_CSTRING: return "FC_CSTRING"; case RPC_FC_WSTRING: return "FC_WSTRING"; default: error("string_of_type: unknown type 0x%02x\n", type); return NULL; } } int is_struct(unsigned char type) { switch (type) { case RPC_FC_STRUCT: case RPC_FC_PSTRUCT: case RPC_FC_CSTRUCT: case RPC_FC_CPSTRUCT: case RPC_FC_CVSTRUCT: case RPC_FC_BOGUS_STRUCT: return 1; default: return 0; } } static int is_non_complex_struct(const type_t *type) { switch (type->type) { case RPC_FC_STRUCT: case RPC_FC_PSTRUCT: case RPC_FC_CSTRUCT: case RPC_FC_CPSTRUCT: case RPC_FC_CVSTRUCT: return 1; default: return 0; } } int is_union(unsigned char type) { switch (type) { case RPC_FC_ENCAPSULATED_UNION: case RPC_FC_NON_ENCAPSULATED_UNION: return 1; default: return 0; } } static int type_has_pointers(const type_t *type) { if (is_user_type(type)) return FALSE; else if (is_ptr(type)) return TRUE; else if (is_array(type)) return type_has_pointers(type->ref); else if (is_struct(type->type)) { const var_t *field; if (type->fields) LIST_FOR_EACH_ENTRY( field, type->fields, const var_t, entry ) { if (type_has_pointers(field->type)) return TRUE; } } else if (is_union(type->type)) { var_list_t *fields; const var_t *field; if (type->type == RPC_FC_ENCAPSULATED_UNION) { const var_t *uv = LIST_ENTRY(list_tail(type->fields), const var_t, entry); fields = uv->type->fields; } else fields = type->fields; if (fields) LIST_FOR_EACH_ENTRY( field, fields, const var_t, entry ) { if (field->type && type_has_pointers(field->type)) return TRUE; } } return FALSE; } static unsigned short user_type_offset(const char *name) { user_type_t *ut; unsigned short off = 0; LIST_FOR_EACH_ENTRY(ut, &user_type_list, user_type_t, entry) { if (strcmp(name, ut->name) == 0) return off; ++off; } error("user_type_offset: couldn't find type (%s)\n", name); return 0; } static void update_tfsoff(type_t *type, unsigned int offset, FILE *file) { type->typestring_offset = offset; if (file) type->tfswrite = FALSE; } static void guard_rec(type_t *type) { /* types that contain references to themselves (like a linked list), need to be shielded from infinite recursion when writing embedded types */ if (type->typestring_offset) type->tfswrite = FALSE; else type->typestring_offset = 1; } static type_t *get_user_type(const type_t *t, const char **pname) { for (;;) { type_t *ut = get_attrp(t->attrs, ATTR_WIREMARSHAL); if (ut) { if (pname) *pname = t->name; return ut; } if (t->kind == TKIND_ALIAS) t = t->orig; else return 0; } } int is_user_type(const type_t *t) { return get_user_type(t, NULL) != NULL; } static int is_embedded_complex(const type_t *type) { unsigned char tc = type->type; return is_struct(tc) || is_union(tc) || is_array(type) || is_user_type(type) || (is_ptr(type) && type->ref->type == RPC_FC_IP); } static const char *get_context_handle_type_name(const type_t *type) { const type_t *t; for (t = type; is_ptr(t); t = t->ref) if (is_attr(t->attrs, ATTR_CONTEXTHANDLE)) return t->name; assert(0); return NULL; } /* This is actually fairly involved to implement precisely, due to the effects attributes may have and things like that. Right now this is only used for optimization, so just check for a very small set of criteria that guarantee the types are equivalent; assume every thing else is different. */ static int compare_type(const type_t *a, const type_t *b) { if (a == b || (a->name && b->name && strcmp(a->name, b->name) == 0)) return 0; /* Ordering doesn't need to be implemented yet. */ return 1; } static int compare_expr(const expr_t *a, const expr_t *b) { int ret; if (a->type != b->type) return a->type - b->type; switch (a->type) { case EXPR_NUM: case EXPR_HEXNUM: case EXPR_TRUEFALSE: return a->u.lval - b->u.lval; case EXPR_DOUBLE: return a->u.dval - b->u.dval; case EXPR_IDENTIFIER: return strcmp(a->u.sval, b->u.sval); case EXPR_COND: ret = compare_expr(a->ref, b->ref); if (ret != 0) return ret; ret = compare_expr(a->u.ext, b->u.ext); if (ret != 0) return ret; return compare_expr(a->ext2, b->ext2); case EXPR_OR: case EXPR_AND: case EXPR_ADD: case EXPR_SUB: case EXPR_MUL: case EXPR_DIV: case EXPR_SHL: case EXPR_SHR: ret = compare_expr(a->ref, b->ref); if (ret != 0) return ret; return compare_expr(a->u.ext, b->u.ext); case EXPR_CAST: ret = compare_type(a->u.tref, b->u.tref); if (ret != 0) return ret; /* Fall through. */ case EXPR_NOT: case EXPR_NEG: case EXPR_PPTR: case EXPR_ADDRESSOF: return compare_expr(a->ref, b->ref); case EXPR_SIZEOF: return compare_type(a->u.tref, b->u.tref); case EXPR_VOID: return 0; } return -1; } #define WRITE_FCTYPE(file, fctype, typestring_offset) \ do { \ if (file) \ fprintf(file, "/* %2u */\n", typestring_offset); \ print_file((file), 2, "0x%02x, /* " #fctype " */\n", RPC_##fctype); \ } \ while (0) static void print_file(FILE *file, int indent, const char *format, ...) { va_list va; va_start(va, format); print(file, indent, format, va); va_end(va); } void print(FILE *file, int indent, const char *format, va_list va) { if (file) { if (format[0] != '\n') while (0 < indent--) fprintf(file, " "); vfprintf(file, format, va); } } static void write_var_init(FILE *file, int indent, const type_t *t, const char *n) { if (decl_indirect(t)) print_file(file, indent, "MIDL_memset(&%s, 0, sizeof(%s));\n", n, n); else if (is_ptr(t) || is_array(t)) print_file(file, indent, "%s = 0;\n", n); } void write_parameters_init(FILE *file, int indent, const func_t *func) { const var_t *var; if (!is_void(func->def->type)) write_var_init(file, indent, func->def->type, "_RetVal"); if (!func->args) return; LIST_FOR_EACH_ENTRY( var, func->args, const var_t, entry ) write_var_init(file, indent, var->type, var->name); fprintf(file, "\n"); } static void write_formatdesc(FILE *f, int indent, const char *str) { print_file(f, indent, "typedef struct _MIDL_%s_FORMAT_STRING\n", str); print_file(f, indent, "{\n"); print_file(f, indent + 1, "short Pad;\n"); print_file(f, indent + 1, "unsigned char Format[%s_FORMAT_STRING_SIZE];\n", str); print_file(f, indent, "} MIDL_%s_FORMAT_STRING;\n", str); print_file(f, indent, "\n"); } void write_formatstringsdecl(FILE *f, int indent, ifref_list_t *ifaces, type_pred_t pred) { print_file(f, indent, "#define TYPE_FORMAT_STRING_SIZE %d\n", get_size_typeformatstring(ifaces, pred)); print_file(f, indent, "#define PROC_FORMAT_STRING_SIZE %d\n", get_size_procformatstring(ifaces, pred)); fprintf(f, "\n"); write_formatdesc(f, indent, "TYPE"); write_formatdesc(f, indent, "PROC"); fprintf(f, "\n"); print_file(f, indent, "static const MIDL_TYPE_FORMAT_STRING __MIDL_TypeFormatString;\n"); print_file(f, indent, "static const MIDL_PROC_FORMAT_STRING __MIDL_ProcFormatString;\n"); print_file(f, indent, "\n"); } static inline int is_base_type(unsigned char type) { switch (type) { case RPC_FC_BYTE: case RPC_FC_CHAR: case RPC_FC_USMALL: case RPC_FC_SMALL: case RPC_FC_WCHAR: case RPC_FC_USHORT: case RPC_FC_SHORT: case RPC_FC_ULONG: case RPC_FC_LONG: case RPC_FC_HYPER: case RPC_FC_IGNORE: case RPC_FC_FLOAT: case RPC_FC_DOUBLE: case RPC_FC_ENUM16: case RPC_FC_ENUM32: case RPC_FC_ERROR_STATUS_T: case RPC_FC_BIND_PRIMITIVE: return TRUE; default: return FALSE; } } int decl_indirect(const type_t *t) { return is_user_type(t) || (!is_base_type(t->type) && !is_ptr(t) && !is_array(t)); } static size_t write_procformatstring_var(FILE *file, int indent, const var_t *var, int is_return) { size_t size; const type_t *type = var->type; int is_in = is_attr(var->attrs, ATTR_IN); int is_out = is_attr(var->attrs, ATTR_OUT); if (!is_in && !is_out) is_in = TRUE; if (!type->declarray && is_base_type(type->type)) { if (is_return) print_file(file, indent, "0x53, /* FC_RETURN_PARAM_BASETYPE */\n"); else print_file(file, indent, "0x4e, /* FC_IN_PARAM_BASETYPE */\n"); if (type->type == RPC_FC_BIND_PRIMITIVE) { print_file(file, indent, "0x%02x, /* FC_IGNORE */\n", RPC_FC_IGNORE); size = 2; /* includes param type prefix */ } else if (is_base_type(type->type)) { print_file(file, indent, "0x%02x, /* %s */\n", type->type, string_of_type(type->type)); size = 2; /* includes param type prefix */ } else { error("Unknown/unsupported type: %s (0x%02x)\n", var->name, type->type); size = 0; } } else { if (is_return) print_file(file, indent, "0x52, /* FC_RETURN_PARAM */\n"); else if (is_in && is_out) print_file(file, indent, "0x50, /* FC_IN_OUT_PARAM */\n"); else if (is_out) print_file(file, indent, "0x51, /* FC_OUT_PARAM */\n"); else print_file(file, indent, "0x4d, /* FC_IN_PARAM */\n"); print_file(file, indent, "0x01,\n"); print_file(file, indent, "NdrFcShort(0x%x),\n", type->typestring_offset); size = 4; /* includes param type prefix */ } return size; } void write_procformatstring(FILE *file, const ifref_list_t *ifaces, type_pred_t pred) { const ifref_t *iface; int indent = 0; const var_t *var; print_file(file, indent, "static const MIDL_PROC_FORMAT_STRING __MIDL_ProcFormatString =\n"); print_file(file, indent, "{\n"); indent++; print_file(file, indent, "0,\n"); print_file(file, indent, "{\n"); indent++; if (ifaces) LIST_FOR_EACH_ENTRY( iface, ifaces, const ifref_t, entry ) { if (!pred(iface->iface)) continue; if (iface->iface->funcs) { const func_t *func; LIST_FOR_EACH_ENTRY( func, iface->iface->funcs, const func_t, entry ) { if (is_local(func->def->attrs)) continue; /* emit argument data */ if (func->args) { LIST_FOR_EACH_ENTRY( var, func->args, const var_t, entry ) write_procformatstring_var(file, indent, var, FALSE); } /* emit return value data */ var = func->def; if (is_void(var->type)) { print_file(file, indent, "0x5b, /* FC_END */\n"); print_file(file, indent, "0x5c, /* FC_PAD */\n"); } else write_procformatstring_var(file, indent, var, TRUE); } } } print_file(file, indent, "0x0\n"); indent--; print_file(file, indent, "}\n"); indent--; print_file(file, indent, "};\n"); print_file(file, indent, "\n"); } static int write_base_type(FILE *file, const type_t *type, unsigned int *typestring_offset) { if (is_base_type(type->type)) { print_file(file, 2, "0x%02x,\t/* %s */\n", type->type, string_of_type(type->type)); *typestring_offset += 1; return 1; } return 0; } /* write conformance / variance descriptor */ static size_t write_conf_or_var_desc(FILE *file, const type_t *structure, unsigned int baseoff, const type_t *type, const expr_t *expr) { unsigned char operator_type = 0; unsigned char conftype = RPC_FC_NORMAL_CONFORMANCE; const char *conftype_string = ""; const char *operator_string = "no operators"; const expr_t *subexpr; if (!expr) { print_file(file, 2, "NdrFcLong(0xffffffff),\t/* -1 */\n"); return 4; } if (!structure) { /* Top-level conformance calculations are done inline. */ print_file (file, 2, "0x%x,\t/* Corr desc: parameter */\n", RPC_FC_TOP_LEVEL_CONFORMANCE); print_file (file, 2, "0x0,\n"); print_file (file, 2, "NdrFcShort(0x0),\n"); return 4; } if (expr->is_const) { if (expr->cval > UCHAR_MAX * (USHRT_MAX + 1) + USHRT_MAX) error("write_conf_or_var_desc: constant value %ld is greater than " "the maximum constant size of %d\n", expr->cval, UCHAR_MAX * (USHRT_MAX + 1) + USHRT_MAX); print_file(file, 2, "0x%x, /* Corr desc: constant, val = %ld */\n", RPC_FC_CONSTANT_CONFORMANCE, expr->cval); print_file(file, 2, "0x%x,\n", expr->cval & ~USHRT_MAX); print_file(file, 2, "NdrFcShort(0x%x),\n", expr->cval & USHRT_MAX); return 4; } if (is_ptr(type) || (is_array(type) && !type->declarray)) { conftype = RPC_FC_POINTER_CONFORMANCE; conftype_string = "field pointer, "; } subexpr = expr; switch (subexpr->type) { case EXPR_PPTR: subexpr = subexpr->ref; operator_type = RPC_FC_DEREFERENCE; operator_string = "FC_DEREFERENCE"; break; case EXPR_DIV: if (subexpr->u.ext->is_const && (subexpr->u.ext->cval == 2)) { subexpr = subexpr->ref; operator_type = RPC_FC_DIV_2; operator_string = "FC_DIV_2"; } break; case EXPR_MUL: if (subexpr->u.ext->is_const && (subexpr->u.ext->cval == 2)) { subexpr = subexpr->ref; operator_type = RPC_FC_MULT_2; operator_string = "FC_MULT_2"; } break; case EXPR_SUB: if (subexpr->u.ext->is_const && (subexpr->u.ext->cval == 1)) { subexpr = subexpr->ref; operator_type = RPC_FC_SUB_1; operator_string = "FC_SUB_1"; } break; case EXPR_ADD: if (subexpr->u.ext->is_const && (subexpr->u.ext->cval == 1)) { subexpr = subexpr->ref; operator_type = RPC_FC_ADD_1; operator_string = "FC_ADD_1"; } break; default: break; } if (subexpr->type == EXPR_IDENTIFIER) { const type_t *correlation_variable = NULL; unsigned char correlation_variable_type; unsigned char param_type = 0; size_t offset = 0; const var_t *var; if (structure->fields) LIST_FOR_EACH_ENTRY( var, structure->fields, const var_t, entry ) { unsigned int align = 0; /* FIXME: take alignment into account */ if (var->name && !strcmp(var->name, subexpr->u.sval)) { correlation_variable = var->type; break; } offset += type_memsize(var->type, &align); } if (!correlation_variable) error("write_conf_or_var_desc: couldn't find variable %s in structure\n", subexpr->u.sval); offset -= baseoff; correlation_variable_type = correlation_variable->type; switch (correlation_variable_type) { case RPC_FC_CHAR: case RPC_FC_SMALL: param_type = RPC_FC_SMALL; break; case RPC_FC_BYTE: case RPC_FC_USMALL: param_type = RPC_FC_USMALL; break; case RPC_FC_WCHAR: case RPC_FC_SHORT: case RPC_FC_ENUM16: param_type = RPC_FC_SHORT; break; case RPC_FC_USHORT: param_type = RPC_FC_USHORT; break; case RPC_FC_LONG: case RPC_FC_ENUM32: param_type = RPC_FC_LONG; break; case RPC_FC_ULONG: param_type = RPC_FC_ULONG; break; case RPC_FC_RP: case RPC_FC_UP: case RPC_FC_OP: case RPC_FC_FP: if (sizeof(void *) == 4) /* FIXME */ param_type = RPC_FC_LONG; else param_type = RPC_FC_HYPER; break; default: error("write_conf_or_var_desc: conformance variable type not supported 0x%x\n", correlation_variable_type); } print_file(file, 2, "0x%x, /* Corr desc: %s%s */\n", conftype | param_type, conftype_string, string_of_type(param_type)); print_file(file, 2, "0x%x, /* %s */\n", operator_type, operator_string); print_file(file, 2, "NdrFcShort(0x%x), /* offset = %d */\n", offset, offset); } else { unsigned int callback_offset = 0; struct expr_eval_routine *eval; int found = 0; LIST_FOR_EACH_ENTRY(eval, &expr_eval_routines, struct expr_eval_routine, entry) { if (!strcmp (eval->structure->name, structure->name) && !compare_expr (eval->expr, expr)) { found = 1; break; } callback_offset++; } if (!found) { eval = xmalloc (sizeof(*eval)); eval->structure = structure; eval->baseoff = baseoff; eval->expr = expr; list_add_tail (&expr_eval_routines, &eval->entry); } if (callback_offset > USHRT_MAX) error("Maximum number of callback routines reached\n"); print_file(file, 2, "0x%x, /* Corr desc: %s */\n", conftype, conftype_string); print_file(file, 2, "0x%x, /* %s */\n", RPC_FC_CALLBACK, "FC_CALLBACK"); print_file(file, 2, "NdrFcShort(0x%x), /* %u */\n", callback_offset, callback_offset); } return 4; } static size_t fields_memsize(const var_list_t *fields, unsigned int *align) { int have_align = FALSE; size_t size = 0; const var_t *v; if (!fields) return 0; LIST_FOR_EACH_ENTRY( v, fields, const var_t, entry ) { unsigned int falign = 0; size_t fsize = type_memsize(v->type, &falign); if (!have_align) { *align = falign; have_align = TRUE; } size = (size + (falign - 1)) & ~(falign - 1); size += fsize; } size = (size + (*align - 1)) & ~(*align - 1); return size; } static size_t union_memsize(const var_list_t *fields, unsigned int *pmaxa) { size_t size, maxs = 0; unsigned int align = *pmaxa; const var_t *v; if (fields) LIST_FOR_EACH_ENTRY( v, fields, const var_t, entry ) { /* we could have an empty default field with NULL type */ if (v->type) { size = type_memsize(v->type, &align); if (maxs < size) maxs = size; if (*pmaxa < align) *pmaxa = align; } } return maxs; } int get_padding(const var_list_t *fields) { unsigned short offset = 0; int salign = -1; const var_t *f; if (!fields) return 0; LIST_FOR_EACH_ENTRY(f, fields, const var_t, entry) { type_t *ft = f->type; unsigned int align = 0; size_t size = type_memsize(ft, &align); if (salign == -1) salign = align; offset = (offset + (align - 1)) & ~(align - 1); offset += size; } return ((offset + (salign - 1)) & ~(salign - 1)) - offset; } size_t type_memsize(const type_t *t, unsigned int *align) { size_t size = 0; if (t->declarray && is_conformant_array(t)) { type_memsize(t->ref, align); size = 0; } else if (is_ptr(t) || is_conformant_array(t)) { size = sizeof(void *); if (size > *align) *align = size; } else switch (t->type) { case RPC_FC_BYTE: case RPC_FC_CHAR: case RPC_FC_USMALL: case RPC_FC_SMALL: size = 1; if (size > *align) *align = size; break; case RPC_FC_WCHAR: case RPC_FC_USHORT: case RPC_FC_SHORT: case RPC_FC_ENUM16: size = 2; if (size > *align) *align = size; break; case RPC_FC_ULONG: case RPC_FC_LONG: case RPC_FC_ERROR_STATUS_T: case RPC_FC_ENUM32: case RPC_FC_FLOAT: size = 4; if (size > *align) *align = size; break; case RPC_FC_HYPER: case RPC_FC_DOUBLE: size = 8; if (size > *align) *align = size; break; case RPC_FC_STRUCT: case RPC_FC_CVSTRUCT: case RPC_FC_CPSTRUCT: case RPC_FC_CSTRUCT: case RPC_FC_PSTRUCT: case RPC_FC_BOGUS_STRUCT: size = fields_memsize(t->fields, align); break; case RPC_FC_ENCAPSULATED_UNION: case RPC_FC_NON_ENCAPSULATED_UNION: size = union_memsize(t->fields, align); break; case RPC_FC_SMFARRAY: case RPC_FC_LGFARRAY: case RPC_FC_SMVARRAY: case RPC_FC_LGVARRAY: case RPC_FC_BOGUS_ARRAY: size = t->dim * type_memsize(t->ref, align); break; default: error("type_memsize: Unknown type %d\n", t->type); size = 0; } return size; } static unsigned int write_nonsimple_pointer(FILE *file, const type_t *type, size_t offset) { short absoff = type->ref->typestring_offset; short reloff = absoff - (offset + 2); int ptr_attr = is_ptr(type->ref) ? 0x10 : 0x0; print_file(file, 2, "0x%02x, 0x%x,\t/* %s */\n", type->type, ptr_attr, string_of_type(type->type)); print_file(file, 2, "NdrFcShort(0x%hx),\t/* Offset= %hd (%hd) */\n", reloff, reloff, absoff); return 4; } static unsigned char conf_string_type_of_char_type(unsigned char t) { switch (t) { case RPC_FC_BYTE: case RPC_FC_CHAR: return RPC_FC_C_CSTRING; case RPC_FC_WCHAR: return RPC_FC_C_WSTRING; } error("string_type_of_char_type: unrecognized type %d\n", t); return 0; } static unsigned int write_simple_pointer(FILE *file, const type_t *type) { unsigned char fc = is_string_type(type->attrs, type) ? conf_string_type_of_char_type(type->ref->type) : type->ref->type; print_file(file, 2, "0x%02x, 0x8,\t/* %s [simple_pointer] */\n", type->type, string_of_type(type->type)); print_file(file, 2, "0x%02x,\t/* %s */\n", fc, string_of_type(fc)); print_file(file, 2, "0x5c,\t/* FC_PAD */\n"); return 4; } static void print_start_tfs_comment(FILE *file, type_t *t, unsigned int tfsoff) { print_file(file, 0, "/* %u (", tfsoff); write_type_decl(file, t, NULL); print_file(file, 0, ") */\n"); } static size_t write_pointer_tfs(FILE *file, type_t *type, unsigned int *typestring_offset) { unsigned int offset = *typestring_offset; print_start_tfs_comment(file, type, offset); update_tfsoff(type, offset, file); if (type->ref->typestring_offset) *typestring_offset += write_nonsimple_pointer(file, type, offset); else if (is_base_type(type->ref->type)) *typestring_offset += write_simple_pointer(file, type); return offset; } static int processed(const type_t *type) { return type->typestring_offset && !type->tfswrite; } static int user_type_has_variable_size(const type_t *t) { if (is_ptr(t)) return TRUE; else switch (t->type) { case RPC_FC_PSTRUCT: case RPC_FC_CSTRUCT: case RPC_FC_CPSTRUCT: case RPC_FC_CVSTRUCT: return TRUE; } /* Note: Since this only applies to user types, we can't have a conformant array here, and strings should get filed under pointer in this case. */ return FALSE; } static void write_user_tfs(FILE *file, type_t *type, unsigned int *tfsoff) { unsigned int start, absoff, flags; unsigned int align = 0, ualign = 0; const char *name; type_t *utype = get_user_type(type, &name); size_t usize = user_type_has_variable_size(utype) ? 0 : type_memsize(utype, &ualign); size_t size = type_memsize(type, &align); unsigned short funoff = user_type_offset(name); short reloff; guard_rec(type); if (is_base_type(utype->type)) { absoff = *tfsoff; print_start_tfs_comment(file, utype, absoff); print_file(file, 2, "0x%x,\t/* %s */\n", utype->type, string_of_type(utype->type)); print_file(file, 2, "0x5c,\t/* FC_PAD */\n"); *tfsoff += 2; } else { if (!processed(utype)) write_embedded_types(file, NULL, utype, utype->name, TRUE, tfsoff); absoff = utype->typestring_offset; } if (utype->type == RPC_FC_RP) flags = 0x40; else if (utype->type == RPC_FC_UP) flags = 0x80; else flags = 0; start = *tfsoff; update_tfsoff(type, start, file); print_start_tfs_comment(file, type, start); print_file(file, 2, "0x%x,\t/* FC_USER_MARSHAL */\n", RPC_FC_USER_MARSHAL); print_file(file, 2, "0x%x,\t/* Alignment= %d, Flags= %02x */\n", flags | (align - 1), align - 1, flags); print_file(file, 2, "NdrFcShort(0x%hx),\t/* Function offset= %hu */\n", funoff, funoff); print_file(file, 2, "NdrFcShort(0x%lx),\t/* %lu */\n", size, size); print_file(file, 2, "NdrFcShort(0x%lx),\t/* %lu */\n", usize, usize); *tfsoff += 8; reloff = absoff - *tfsoff; print_file(file, 2, "NdrFcShort(0x%hx),\t/* Offset= %hd (%lu) */\n", reloff, reloff, absoff); *tfsoff += 2; } static void write_member_type(FILE *file, const type_t *cont, const attr_list_t *attrs, const type_t *type, unsigned int *corroff, unsigned int *tfsoff) { if (is_embedded_complex(type) && !is_conformant_array(type)) { size_t absoff; short reloff; if (is_union(type->type) && is_attr(attrs, ATTR_SWITCHIS)) { absoff = *corroff; *corroff += 8; } else { absoff = type->typestring_offset; } reloff = absoff - (*tfsoff + 2); print_file(file, 2, "0x4c,\t/* FC_EMBEDDED_COMPLEX */\n"); /* FIXME: actually compute necessary padding */ print_file(file, 2, "0x0,\t/* FIXME: padding */\n"); print_file(file, 2, "NdrFcShort(0x%hx),\t/* Offset= %hd (%lu) */\n", reloff, reloff, absoff); *tfsoff += 4; } else if (is_ptr(type) || is_conformant_array(type)) { unsigned char fc = (cont->type == RPC_FC_BOGUS_STRUCT ? RPC_FC_POINTER : RPC_FC_LONG); print_file(file, 2, "0x%x,\t/* %s */\n", fc, string_of_type(fc)); *tfsoff += 1; } else if (!write_base_type(file, type, tfsoff)) error("Unsupported member type 0x%x\n", type->type); } static void write_end(FILE *file, unsigned int *tfsoff) { if (*tfsoff % 2 == 0) { print_file(file, 2, "0x%x,\t\t/* FC_PAD */\n", RPC_FC_PAD); *tfsoff += 1; } print_file(file, 2, "0x%x,\t\t/* FC_END */\n", RPC_FC_END); *tfsoff += 1; } static void write_descriptors(FILE *file, type_t *type, unsigned int *tfsoff) { unsigned int offset = 0; var_list_t *fs = type->fields; var_t *f; if (fs) LIST_FOR_EACH_ENTRY(f, fs, var_t, entry) { unsigned int align = 0; type_t *ft = f->type; if (is_union(ft->type) && is_attr(f->attrs, ATTR_SWITCHIS)) { unsigned int absoff = ft->typestring_offset; short reloff = absoff - (*tfsoff + 6); print_file(file, 0, "/* %d */\n", *tfsoff); print_file(file, 2, "0x%x,\t/* %s */\n", ft->type, string_of_type(ft->type)); print_file(file, 2, "0x%x,\t/* FIXME: always FC_LONG */\n", RPC_FC_LONG); write_conf_or_var_desc(file, current_structure, offset, ft, get_attrp(f->attrs, ATTR_SWITCHIS)); print_file(file, 2, "NdrFcShort(%hd),\t/* Offset= %hd (%u) */\n", reloff, reloff, absoff); *tfsoff += 8; } /* FIXME: take alignment into account */ offset += type_memsize(ft, &align); } } static int write_no_repeat_pointer_descriptions( FILE *file, type_t *type, size_t *offset_in_memory, size_t *offset_in_buffer, unsigned int *typestring_offset) { int written = 0; unsigned int align; if (is_ptr(type) || (!type->declarray && is_conformant_array(type))) { print_file(file, 2, "0x%02x, /* FC_NO_REPEAT */\n", RPC_FC_NO_REPEAT); print_file(file, 2, "0x%02x, /* FC_PAD */\n", RPC_FC_PAD); /* pointer instance */ print_file(file, 2, "NdrFcShort(0x%x), /* Memory offset = %d */\n", *offset_in_memory, *offset_in_memory); print_file(file, 2, "NdrFcShort(0x%x), /* Buffer offset = %d */\n", *offset_in_buffer, *offset_in_buffer); *typestring_offset += 6; if (is_ptr(type)) write_pointer_tfs(file, type, typestring_offset); else { unsigned absoff = type->typestring_offset; short reloff = absoff - (*typestring_offset + 2); /* FIXME: get pointer attributes from field */ print_file(file, 2, "0x%02x, 0x0,\t/* %s */\n", RPC_FC_UP, "FC_UP"); print_file(file, 2, "NdrFcShort(0x%hx),\t/* Offset= %hd (%u) */\n", reloff, reloff, absoff); *typestring_offset += 4; } align = 0; *offset_in_memory += type_memsize(type, &align); /* FIXME: is there a case where these two are different? */ align = 0; *offset_in_buffer += type_memsize(type, &align); return 1; } if (is_non_complex_struct(type)) { const var_t *v; LIST_FOR_EACH_ENTRY( v, type->fields, const var_t, entry ) written += write_no_repeat_pointer_descriptions( file, v->type, offset_in_memory, offset_in_buffer, typestring_offset); } else { align = 0; *offset_in_memory += type_memsize(type, &align); /* FIXME: is there a case where these two are different? */ align = 0; *offset_in_buffer += type_memsize(type, &align); } return written; } static int write_pointer_description_offsets( FILE *file, const attr_list_t *attrs, type_t *type, size_t *offset_in_memory, size_t *offset_in_buffer, unsigned int *typestring_offset) { int written = 0; unsigned int align; if (is_ptr(type) && type->ref->type != RPC_FC_IP) { if (offset_in_memory && offset_in_buffer) { /* pointer instance */ /* FIXME: sometimes from end of structure, sometimes from beginning */ print_file(file, 2, "NdrFcShort(0x%x), /* Memory offset = %d */\n", *offset_in_memory, *offset_in_memory); print_file(file, 2, "NdrFcShort(0x%x), /* Buffer offset = %d */\n", *offset_in_buffer, *offset_in_buffer); align = 0; *offset_in_memory += type_memsize(type, &align); /* FIXME: is there a case where these two are different? */ align = 0; *offset_in_buffer += type_memsize(type, &align); } *typestring_offset += 4; if (processed(type->ref) || is_base_type(type->ref->type)) write_pointer_tfs(file, type, typestring_offset); else error("write_pointer_description_offsets: type format string unknown\n"); return 1; } if (is_array(type)) { return write_pointer_description_offsets( file, attrs, type->ref, offset_in_memory, offset_in_buffer, typestring_offset); } else if (is_non_complex_struct(type)) { /* otherwise search for interesting fields to parse */ const var_t *v; LIST_FOR_EACH_ENTRY( v, type->fields, const var_t, entry ) { written += write_pointer_description_offsets( file, v->attrs, v->type, offset_in_memory, offset_in_buffer, typestring_offset); } } else { align = 0; if (offset_in_memory) *offset_in_memory += type_memsize(type, &align); /* FIXME: is there a case where these two are different? */ align = 0; if (offset_in_buffer) *offset_in_buffer += type_memsize(type, &align); } return written; } /* Note: if file is NULL return value is number of pointers to write, else * it is the number of type format characters written */ static int write_fixed_array_pointer_descriptions( FILE *file, const attr_list_t *attrs, type_t *type, size_t *offset_in_memory, size_t *offset_in_buffer, unsigned int *typestring_offset) { unsigned int align; int pointer_count = 0; if (type->type == RPC_FC_SMFARRAY || type->type == RPC_FC_LGFARRAY) { unsigned int temp = 0; /* unfortunately, this needs to be done in two passes to avoid * writing out redundant FC_FIXED_REPEAT descriptions */ pointer_count = write_pointer_description_offsets( NULL, attrs, type->ref, NULL, NULL, &temp); if (pointer_count > 0) { unsigned int increment_size; size_t offset_of_array_pointer_mem = 0; size_t offset_of_array_pointer_buf = 0; align = 0; increment_size = type_memsize(type->ref, &align); print_file(file, 2, "0x%02x, /* FC_FIXED_REPEAT */\n", RPC_FC_FIXED_REPEAT); print_file(file, 2, "0x%02x, /* FC_PAD */\n", RPC_FC_PAD); print_file(file, 2, "NdrFcShort(0x%x), /* Iterations = %d */\n", type->dim, type->dim); print_file(file, 2, "NdrFcShort(0x%x), /* Increment = %d */\n", increment_size, increment_size); print_file(file, 2, "NdrFcShort(0x%x), /* Offset to array = %d */\n", *offset_in_memory, *offset_in_memory); print_file(file, 2, "NdrFcShort(0x%x), /* Number of pointers = %d */\n", pointer_count, pointer_count); *typestring_offset += 10; pointer_count = write_pointer_description_offsets( file, attrs, type, &offset_of_array_pointer_mem, &offset_of_array_pointer_buf, typestring_offset); } } else if (is_struct(type->type)) { const var_t *v; LIST_FOR_EACH_ENTRY( v, type->fields, const var_t, entry ) { pointer_count += write_fixed_array_pointer_descriptions( file, v->attrs, v->type, offset_in_memory, offset_in_buffer, typestring_offset); } } else { align = 0; if (offset_in_memory) *offset_in_memory += type_memsize(type, &align); /* FIXME: is there a case where these two are different? */ align = 0; if (offset_in_buffer) *offset_in_buffer += type_memsize(type, &align); } return pointer_count; } /* Note: if file is NULL return value is number of pointers to write, else * it is the number of type format characters written */ static int write_conformant_array_pointer_descriptions( FILE *file, const attr_list_t *attrs, type_t *type, size_t offset_in_memory, unsigned int *typestring_offset) { unsigned int align; int pointer_count = 0; if (is_conformant_array(type) && !type->length_is) { unsigned int temp = 0; /* unfortunately, this needs to be done in two passes to avoid * writing out redundant FC_VARIABLE_REPEAT descriptions */ pointer_count = write_pointer_description_offsets( NULL, attrs, type->ref, NULL, NULL, &temp); if (pointer_count > 0) { unsigned int increment_size; size_t offset_of_array_pointer_mem = offset_in_memory; size_t offset_of_array_pointer_buf = offset_in_memory; align = 0; increment_size = type_memsize(type->ref, &align); if (increment_size > USHRT_MAX) error("array size of %u bytes is too large\n", increment_size); print_file(file, 2, "0x%02x, /* FC_VARIABLE_REPEAT */\n", RPC_FC_VARIABLE_REPEAT); print_file(file, 2, "0x%02x, /* FC_FIXED_OFFSET */\n", RPC_FC_FIXED_OFFSET); print_file(file, 2, "NdrFcShort(0x%x), /* Increment = %d */\n", increment_size, increment_size); print_file(file, 2, "NdrFcShort(0x%x), /* Offset to array = %d */\n", offset_in_memory, offset_in_memory); print_file(file, 2, "NdrFcShort(0x%x), /* Number of pointers = %d */\n", pointer_count, pointer_count); *typestring_offset += 8; pointer_count = write_pointer_description_offsets( file, attrs, type->ref, &offset_of_array_pointer_mem, &offset_of_array_pointer_buf, typestring_offset); } } return pointer_count; } /* Note: if file is NULL return value is number of pointers to write, else * it is the number of type format characters written */ static int write_varying_array_pointer_descriptions( FILE *file, const attr_list_t *attrs, type_t *type, size_t *offset_in_memory, size_t *offset_in_buffer, unsigned int *typestring_offset) { unsigned int align; int pointer_count = 0; /* FIXME: do varying array searching here, but pointer searching in write_pointer_description_offsets */ if (is_array(type) && type->length_is) { unsigned int temp = 0; /* unfortunately, this needs to be done in two passes to avoid * writing out redundant FC_VARIABLE_REPEAT descriptions */ pointer_count = write_pointer_description_offsets( NULL, attrs, type->ref, NULL, NULL, &temp); if (pointer_count > 0) { unsigned int increment_size; size_t offset_of_array_pointer_mem = 0; size_t offset_of_array_pointer_buf = 0; align = 0; increment_size = type_memsize(type->ref, &align); if (increment_size > USHRT_MAX) error("array size of %u bytes is too large\n", increment_size); print_file(file, 2, "0x%02x, /* FC_VARIABLE_REPEAT */\n", RPC_FC_VARIABLE_REPEAT); print_file(file, 2, "0x%02x, /* FC_VARIABLE_OFFSET */\n", RPC_FC_VARIABLE_OFFSET); print_file(file, 2, "NdrFcShort(0x%x), /* Increment = %d */\n", increment_size, increment_size); print_file(file, 2, "NdrFcShort(0x%x), /* Offset to array = %d */\n", *offset_in_memory, *offset_in_memory); print_file(file, 2, "NdrFcShort(0x%x), /* Number of pointers = %d */\n", pointer_count, pointer_count); *typestring_offset += 8; pointer_count = write_pointer_description_offsets( file, attrs, type, &offset_of_array_pointer_mem, &offset_of_array_pointer_buf, typestring_offset); } } else if (is_struct(type->type)) { const var_t *v; LIST_FOR_EACH_ENTRY( v, type->fields, const var_t, entry ) { pointer_count += write_varying_array_pointer_descriptions( file, v->attrs, v->type, offset_in_memory, offset_in_buffer, typestring_offset); } } else { align = 0; if (offset_in_memory) *offset_in_memory += type_memsize(type, &align); /* FIXME: is there a case where these two are different? */ align = 0; if (offset_in_buffer) *offset_in_buffer += type_memsize(type, &align); } return pointer_count; } static void write_pointer_description(FILE *file, type_t *type, unsigned int *typestring_offset) { size_t offset_in_buffer; size_t offset_in_memory; /* pass 1: search for single instance of a pointer (i.e. don't descend * into arrays) */ if (!is_array(type)) { offset_in_memory = 0; offset_in_buffer = 0; write_no_repeat_pointer_descriptions( file, type, &offset_in_memory, &offset_in_buffer, typestring_offset); } /* pass 2: search for pointers in fixed arrays */ offset_in_memory = 0; offset_in_buffer = 0; write_fixed_array_pointer_descriptions( file, NULL, type, &offset_in_memory, &offset_in_buffer, typestring_offset); /* pass 3: search for pointers in conformant only arrays (but don't descend * into conformant varying or varying arrays) */ if ((!type->declarray || !current_structure) && is_conformant_array(type)) write_conformant_array_pointer_descriptions( file, NULL, type, 0, typestring_offset); else if (type->type == RPC_FC_CPSTRUCT) { unsigned int align = 0; type_t *carray = find_array_or_string_in_struct(type)->type; write_conformant_array_pointer_descriptions( file, NULL, carray, type_memsize(type, &align), typestring_offset); } /* pass 4: search for pointers in varying arrays */ offset_in_memory = 0; offset_in_buffer = 0; write_varying_array_pointer_descriptions( file, NULL, type, &offset_in_memory, &offset_in_buffer, typestring_offset); } int is_declptr(const type_t *t) { return is_ptr(t) || (is_conformant_array(t) && !t->declarray); } static size_t write_string_tfs(FILE *file, const attr_list_t *attrs, type_t *type, const char *name, unsigned int *typestring_offset, int toplevel) { size_t start_offset; unsigned char rtype; if (toplevel && is_declptr(type)) { unsigned char flag = is_conformant_array(type) ? 0 : RPC_FC_P_SIMPLEPOINTER; int pointer_type = is_ptr(type) ? type->type : get_attrv(attrs, ATTR_POINTERTYPE); if (!pointer_type) pointer_type = RPC_FC_RP; print_file(file, 2,"0x%x, 0x%x,\t/* %s%s */\n", pointer_type, flag, string_of_type(pointer_type), flag ? " [simple_pointer]" : ""); *typestring_offset += 2; if (!flag) { print_file(file, 2, "NdrFcShort(0x2),\n"); *typestring_offset += 2; } } start_offset = *typestring_offset; update_tfsoff(type, start_offset, file); rtype = type->ref->type; if ((rtype != RPC_FC_BYTE) && (rtype != RPC_FC_CHAR) && (rtype != RPC_FC_WCHAR)) { error("write_string_tfs: Unimplemented for type 0x%x of name: %s\n", rtype, name); return start_offset; } if (type->declarray && !is_conformant_array(type)) { /* FIXME: multi-dimensional array */ if (0xffffuL < type->dim) error("array size for parameter %s exceeds %u bytes by %lu bytes\n", name, 0xffffu, type->dim - 0xffffu); if (rtype == RPC_FC_CHAR) WRITE_FCTYPE(file, FC_CSTRING, *typestring_offset); else WRITE_FCTYPE(file, FC_WSTRING, *typestring_offset); print_file(file, 2, "0x%x, /* FC_PAD */\n", RPC_FC_PAD); *typestring_offset += 2; print_file(file, 2, "NdrFcShort(0x%x), /* %d */\n", type->dim, type->dim); *typestring_offset += 2; return start_offset; } else if (type->size_is) { unsigned int align = 0; if (rtype == RPC_FC_CHAR) WRITE_FCTYPE(file, FC_C_CSTRING, *typestring_offset); else WRITE_FCTYPE(file, FC_C_WSTRING, *typestring_offset); print_file(file, 2, "0x%x, /* FC_STRING_SIZED */\n", RPC_FC_STRING_SIZED); *typestring_offset += 2; *typestring_offset += write_conf_or_var_desc( file, current_structure, (type->declarray && current_structure ? type_memsize(current_structure, &align) : 0), type, type->size_is); return start_offset; } else { if (rtype == RPC_FC_WCHAR) WRITE_FCTYPE(file, FC_C_WSTRING, *typestring_offset); else WRITE_FCTYPE(file, FC_C_CSTRING, *typestring_offset); print_file(file, 2, "0x%x, /* FC_PAD */\n", RPC_FC_PAD); *typestring_offset += 2; return start_offset; } } static size_t write_array_tfs(FILE *file, const attr_list_t *attrs, type_t *type, const char *name, unsigned int *typestring_offset) { const expr_t *length_is = type->length_is; const expr_t *size_is = type->size_is; unsigned int align = 0; size_t size; size_t start_offset; int has_pointer; int pointer_type = get_attrv(attrs, ATTR_POINTERTYPE); unsigned int baseoff = type->declarray && current_structure ? type_memsize(current_structure, &align) : 0; if (!pointer_type) pointer_type = RPC_FC_RP; if (write_embedded_types(file, attrs, type->ref, name, FALSE, typestring_offset)) has_pointer = TRUE; else has_pointer = type_has_pointers(type->ref); align = 0; size = type_memsize((is_conformant_array(type) ? type->ref : type), &align); start_offset = *typestring_offset; update_tfsoff(type, start_offset, file); print_start_tfs_comment(file, type, start_offset); print_file(file, 2, "0x%02x,\t/* %s */\n", type->type, string_of_type(type->type)); print_file(file, 2, "0x%x,\t/* %d */\n", align - 1, align - 1); *typestring_offset += 2; align = 0; if (type->type != RPC_FC_BOGUS_ARRAY) { unsigned char tc = type->type; if (tc == RPC_FC_LGFARRAY || tc == RPC_FC_LGVARRAY) { print_file(file, 2, "NdrFcLong(0x%x),\t/* %lu */\n", size, size); *typestring_offset += 4; } else { print_file(file, 2, "NdrFcShort(0x%x),\t/* %lu */\n", size, size); *typestring_offset += 2; } if (is_conformant_array(type)) *typestring_offset += write_conf_or_var_desc(file, current_structure, baseoff, type, size_is); if (type->type == RPC_FC_SMVARRAY || type->type == RPC_FC_LGVARRAY) { unsigned int elalign = 0; size_t elsize = type_memsize(type->ref, &elalign); if (type->type == RPC_FC_LGVARRAY) { print_file(file, 2, "NdrFcLong(0x%x),\t/* %lu */\n", type->dim, type->dim); *typestring_offset += 4; } else { print_file(file, 2, "NdrFcShort(0x%x),\t/* %lu */\n", type->dim, type->dim); *typestring_offset += 2; } print_file(file, 2, "NdrFcShort(0x%x),\t/* %lu */\n", elsize, elsize); *typestring_offset += 2; } if (length_is) *typestring_offset += write_conf_or_var_desc(file, current_structure, baseoff, type, length_is); if (has_pointer && (!type->declarray || !current_structure)) { print_file(file, 2, "0x%x, /* FC_PP */\n", RPC_FC_PP); print_file(file, 2, "0x%x, /* FC_PAD */\n", RPC_FC_PAD); *typestring_offset += 2; write_pointer_description(file, type, typestring_offset); print_file(file, 2, "0x%x, /* FC_END */\n", RPC_FC_END); *typestring_offset += 1; } write_member_type(file, type, NULL, type->ref, NULL, typestring_offset); write_end(file, typestring_offset); } else { unsigned int dim = size_is ? 0 : type->dim; print_file(file, 2, "NdrFcShort(0x%x),\t/* %u */\n", dim, dim); *typestring_offset += 2; *typestring_offset += write_conf_or_var_desc(file, current_structure, baseoff, type, size_is); *typestring_offset += write_conf_or_var_desc(file, current_structure, baseoff, type, length_is); write_member_type(file, type, NULL, type->ref, NULL, typestring_offset); write_end(file, typestring_offset); } return start_offset; } static const var_t *find_array_or_string_in_struct(const type_t *type) { const var_t *last_field = LIST_ENTRY( list_tail(type->fields), const var_t, entry ); const type_t *ft = last_field->type; if (ft->declarray && is_conformant_array(ft)) return last_field; if (ft->type == RPC_FC_CSTRUCT || ft->type == RPC_FC_CPSTRUCT || ft->type == RPC_FC_CVSTRUCT) return find_array_or_string_in_struct(last_field->type); else return NULL; } static void write_struct_members(FILE *file, const type_t *type, unsigned int *corroff, unsigned int *typestring_offset) { const var_t *field; unsigned short offset = 0; int salign = -1; int padding; if (type->fields) LIST_FOR_EACH_ENTRY( field, type->fields, const var_t, entry ) { type_t *ft = field->type; if (!ft->declarray || !is_conformant_array(ft)) { unsigned int align = 0; size_t size = type_memsize(ft, &align); if (salign == -1) salign = align; if ((align - 1) & offset) { unsigned char fc = 0; switch (align) { case 4: fc = RPC_FC_ALIGNM4; break; case 8: fc = RPC_FC_ALIGNM8; break; default: error("write_struct_members: cannot align type %d\n", ft->type); } print_file(file, 2, "0x%x,\t/* %s */\n", fc, string_of_type(fc)); offset = (offset + (align - 1)) & ~(align - 1); *typestring_offset += 1; } write_member_type(file, type, field->attrs, field->type, corroff, typestring_offset); offset += size; } } padding = ((offset + (salign - 1)) & ~(salign - 1)) - offset; if (padding) { print_file(file, 2, "0x%x,\t/* FC_STRUCTPAD%d */\n", RPC_FC_STRUCTPAD1 + padding - 1, padding); *typestring_offset += 1; } write_end(file, typestring_offset); } static size_t write_struct_tfs(FILE *file, type_t *type, const char *name, unsigned int *tfsoff) { const type_t *save_current_structure = current_structure; unsigned int total_size; const var_t *array; size_t start_offset; size_t array_offset; int has_pointers = 0; unsigned int align = 0; unsigned int corroff; var_t *f; guard_rec(type); current_structure = type; total_size = type_memsize(type, &align); if (total_size > USHRT_MAX) error("structure size for %s exceeds %d bytes by %d bytes\n", name, USHRT_MAX, total_size - USHRT_MAX); if (type->fields) LIST_FOR_EACH_ENTRY(f, type->fields, var_t, entry) has_pointers |= write_embedded_types(file, f->attrs, f->type, f->name, FALSE, tfsoff); if (!has_pointers) has_pointers = type_has_pointers(type); array = find_array_or_string_in_struct(type); if (array && !processed(array->type)) array_offset = is_attr(array->attrs, ATTR_STRING) ? write_string_tfs(file, array->attrs, array->type, array->name, tfsoff, FALSE) : write_array_tfs(file, array->attrs, array->type, array->name, tfsoff); corroff = *tfsoff; write_descriptors(file, type, tfsoff); start_offset = *tfsoff; update_tfsoff(type, start_offset, file); print_start_tfs_comment(file, type, start_offset); print_file(file, 2, "0x%x,\t/* %s */\n", type->type, string_of_type(type->type)); print_file(file, 2, "0x%x,\t/* %d */\n", align - 1, align - 1); print_file(file, 2, "NdrFcShort(0x%x),\t/* %d */\n", total_size, total_size); *tfsoff += 4; if (array) { unsigned int absoff = array->type->typestring_offset; short reloff = absoff - *tfsoff; print_file(file, 2, "NdrFcShort(0x%hx),\t/* Offset= %hd (%lu) */\n", reloff, reloff, absoff); *tfsoff += 2; } else if (type->type == RPC_FC_BOGUS_STRUCT) { print_file(file, 2, "NdrFcShort(0x0),\n"); *tfsoff += 2; } if (type->type == RPC_FC_BOGUS_STRUCT) { /* On the sizing pass, type->ptrdesc may be zero, but it's ok as nothing is written to file yet. On the actual writing pass, this will have been updated. */ unsigned int absoff = type->ptrdesc ? type->ptrdesc : *tfsoff; short reloff = absoff - *tfsoff; print_file(file, 2, "NdrFcShort(0x%hx),\t/* Offset= %hd (%u) */\n", reloff, reloff, absoff); *tfsoff += 2; } else if ((type->type == RPC_FC_PSTRUCT) || (type->type == RPC_FC_CPSTRUCT) || (type->type == RPC_FC_CVSTRUCT && has_pointers)) { print_file(file, 2, "0x%x, /* FC_PP */\n", RPC_FC_PP); print_file(file, 2, "0x%x, /* FC_PAD */\n", RPC_FC_PAD); *tfsoff += 2; write_pointer_description(file, type, tfsoff); print_file(file, 2, "0x%x, /* FC_END */\n", RPC_FC_END); *tfsoff += 1; } write_struct_members(file, type, &corroff, tfsoff); if (type->type == RPC_FC_BOGUS_STRUCT) { const var_list_t *fs = type->fields; const var_t *f; type->ptrdesc = *tfsoff; if (fs) LIST_FOR_EACH_ENTRY(f, fs, const var_t, entry) { type_t *ft = f->type; if (is_ptr(ft)) write_pointer_tfs(file, ft, tfsoff); else if (!ft->declarray && is_conformant_array(ft)) { unsigned int absoff = ft->typestring_offset; short reloff = absoff - (*tfsoff + 2); int ptr_type = get_attrv(f->attrs, ATTR_POINTERTYPE); /* FIXME: We need to store pointer attributes for arrays so we don't lose pointer_default info. */ if (ptr_type == 0) ptr_type = RPC_FC_UP; print_file(file, 0, "/* %d */\n", *tfsoff); print_file(file, 2, "0x%x, 0x0,\t/* %s */\n", ptr_type, string_of_type(ptr_type)); print_file(file, 2, "NdrFcShort(0x%hx),\t/* Offset= %hd (%u) */\n", reloff, reloff, absoff); *tfsoff += 4; } } if (type->ptrdesc == *tfsoff) type->ptrdesc = 0; } current_structure = save_current_structure; return start_offset; } static size_t write_pointer_only_tfs(FILE *file, const attr_list_t *attrs, int pointer_type, unsigned char flags, size_t offset, unsigned int *typeformat_offset) { size_t start_offset = *typeformat_offset; short reloff = offset - (*typeformat_offset + 2); int in_attr, out_attr; in_attr = is_attr(attrs, ATTR_IN); out_attr = is_attr(attrs, ATTR_OUT); if (!in_attr && !out_attr) in_attr = 1; if (out_attr && !in_attr && pointer_type == RPC_FC_RP) flags |= 0x04; print_file(file, 2, "0x%x, 0x%x,\t\t/* %s", pointer_type, flags, string_of_type(pointer_type)); if (file) { if (flags & 0x04) fprintf(file, " [allocated_on_stack]"); if (flags & 0x10) fprintf(file, " [pointer_deref]"); fprintf(file, " */\n"); } print_file(file, 2, "NdrFcShort(0x%x),\t/* %d */\n", reloff, offset); *typeformat_offset += 4; return start_offset; } static void write_branch_type(FILE *file, const type_t *t, unsigned int *tfsoff) { if (t == NULL) { print_file(file, 2, "NdrFcShort(0x0),\t/* No type */\n"); } else if (is_base_type(t->type)) { print_file(file, 2, "NdrFcShort(0x80%02x),\t/* Simple arm type: %s */\n", t->type, string_of_type(t->type)); } else if (t->typestring_offset) { short reloff = t->typestring_offset - *tfsoff; print_file(file, 2, "NdrFcShort(0x%x),\t/* Offset= %d (%d) */\n", reloff, reloff, t->typestring_offset); } else error("write_branch_type: type unimplemented (0x%x)\n", t->type); *tfsoff += 2; } static size_t write_union_tfs(FILE *file, type_t *type, unsigned int *tfsoff) { unsigned int align = 0; unsigned int start_offset; size_t size = type_memsize(type, &align); var_list_t *fields; size_t nbranch = 0; type_t *deftype = NULL; short nodeftype = 0xffff; var_t *f; guard_rec(type); if (type->type == RPC_FC_ENCAPSULATED_UNION) { const var_t *uv = LIST_ENTRY(list_tail(type->fields), const var_t, entry); fields = uv->type->fields; } else fields = type->fields; if (fields) LIST_FOR_EACH_ENTRY(f, fields, var_t, entry) { expr_list_t *cases = get_attrp(f->attrs, ATTR_CASE); if (cases) nbranch += list_count(cases); if (f->type) write_embedded_types(file, f->attrs, f->type, f->name, TRUE, tfsoff); } start_offset = *tfsoff; update_tfsoff(type, start_offset, file); print_start_tfs_comment(file, type, start_offset); if (type->type == RPC_FC_ENCAPSULATED_UNION) { const var_t *sv = LIST_ENTRY(list_head(type->fields), const var_t, entry); const type_t *st = sv->type; switch (st->type) { case RPC_FC_CHAR: case RPC_FC_SMALL: case RPC_FC_USMALL: case RPC_FC_SHORT: case RPC_FC_USHORT: case RPC_FC_LONG: case RPC_FC_ULONG: case RPC_FC_ENUM16: case RPC_FC_ENUM32: print_file(file, 2, "0x%x,\t/* %s */\n", type->type, string_of_type(type->type)); print_file(file, 2, "0x%x,\t/* Switch type= %s */\n", 0x40 | st->type, string_of_type(st->type)); *tfsoff += 2; break; default: error("union switch type must be an integer, char, or enum\n"); } } print_file(file, 2, "NdrFcShort(0x%x),\t/* %d */\n", size, size); print_file(file, 2, "NdrFcShort(0x%x),\t/* %d */\n", nbranch, nbranch); *tfsoff += 4; if (fields) LIST_FOR_EACH_ENTRY(f, fields, var_t, entry) { type_t *ft = f->type; expr_list_t *cases = get_attrp(f->attrs, ATTR_CASE); int deflt = is_attr(f->attrs, ATTR_DEFAULT); expr_t *c; if (cases == NULL && !deflt) error("union field %s with neither case nor default attribute\n", f->name); if (cases) LIST_FOR_EACH_ENTRY(c, cases, expr_t, entry) { /* MIDL doesn't check for duplicate cases, even though that seems like a reasonable thing to do, it just dumps them to the TFS like we're going to do here. */ print_file(file, 2, "NdrFcLong(0x%x),\t/* %d */\n", c->cval, c->cval); *tfsoff += 4; write_branch_type(file, ft, tfsoff); } /* MIDL allows multiple default branches, even though that seems illogical, it just chooses the last one, which is what we will do. */ if (deflt) { deftype = ft; nodeftype = 0; } } if (deftype) { write_branch_type(file, deftype, tfsoff); } else { print_file(file, 2, "NdrFcShort(0x%x),\n", nodeftype); *tfsoff += 2; } return start_offset; } static size_t write_ip_tfs(FILE *file, const attr_list_t *attrs, type_t *type, unsigned int *typeformat_offset) { size_t i; size_t start_offset = *typeformat_offset; expr_t *iid = get_attrp(attrs, ATTR_IIDIS); if (iid) { print_file(file, 2, "0x2f, /* FC_IP */\n"); print_file(file, 2, "0x5c, /* FC_PAD */\n"); *typeformat_offset += write_conf_or_var_desc(file, NULL, 0, type, iid) + 2; } else { const type_t *base = is_ptr(type) ? type->ref : type; const UUID *uuid = get_attrp(base->attrs, ATTR_UUID); if (! uuid) error("%s: interface %s missing UUID\n", __FUNCTION__, base->name); update_tfsoff(type, start_offset, file); print_start_tfs_comment(file, type, start_offset); print_file(file, 2, "0x2f,\t/* FC_IP */\n"); print_file(file, 2, "0x5a,\t/* FC_CONSTANT_IID */\n"); print_file(file, 2, "NdrFcLong(0x%08lx),\n", uuid->Data1); print_file(file, 2, "NdrFcShort(0x%04x),\n", uuid->Data2); print_file(file, 2, "NdrFcShort(0x%04x),\n", uuid->Data3); for (i = 0; i < 8; ++i) print_file(file, 2, "0x%02x,\n", uuid->Data4[i]); if (file) fprintf(file, "\n"); *typeformat_offset += 18; } return start_offset; } static size_t write_contexthandle_tfs(FILE *file, const type_t *type, const var_t *var, unsigned int *typeformat_offset) { size_t start_offset = *typeformat_offset; unsigned char flags = 0; if (is_attr(current_iface->attrs, ATTR_STRICTCONTEXTHANDLE)) flags |= NDR_STRICT_CONTEXT_HANDLE; if (is_ptr(type)) flags |= 0x80; if (is_attr(var->attrs, ATTR_IN)) { flags |= 0x40; if (!is_attr(var->attrs, ATTR_OUT)) flags |= NDR_CONTEXT_HANDLE_CANNOT_BE_NULL; } if (is_attr(var->attrs, ATTR_OUT)) flags |= 0x20; WRITE_FCTYPE(file, FC_BIND_CONTEXT, *typeformat_offset); print_file(file, 2, "0x%x,\t/* Context flags: ", flags); /* return and can't be null values overlap */ if (((flags & 0x21) != 0x21) && (flags & NDR_CONTEXT_HANDLE_CANNOT_BE_NULL)) print_file(file, 0, "can't be null, "); if (flags & NDR_CONTEXT_HANDLE_SERIALIZE) print_file(file, 0, "serialize, "); if (flags & NDR_CONTEXT_HANDLE_NO_SERIALIZE) print_file(file, 0, "no serialize, "); if (flags & NDR_STRICT_CONTEXT_HANDLE) print_file(file, 0, "strict, "); if ((flags & 0x21) == 0x20) print_file(file, 0, "out, "); if ((flags & 0x21) == 0x21) print_file(file, 0, "return, "); if (flags & 0x40) print_file(file, 0, "in, "); if (flags & 0x80) print_file(file, 0, "via ptr, "); print_file(file, 0, "*/\n"); print_file(file, 2, "0, /* FIXME: rundown routine index*/\n"); print_file(file, 2, "0, /* FIXME: param num */\n"); *typeformat_offset += 4; return start_offset; } static size_t write_typeformatstring_var(FILE *file, int indent, const func_t *func, type_t *type, const var_t *var, unsigned int *typeformat_offset) { size_t offset; if (is_context_handle(type)) return write_contexthandle_tfs(file, type, var, typeformat_offset); if (is_user_type(type)) { write_user_tfs(file, type, typeformat_offset); return type->typestring_offset; } if ((last_ptr(type) || last_array(type)) && is_ptrchain_attr(var, ATTR_STRING)) return write_string_tfs(file, var->attrs, type, var->name, typeformat_offset, TRUE); if (is_array(type)) { int ptr_type; size_t off; off = write_array_tfs(file, var->attrs, type, var->name, typeformat_offset); ptr_type = get_attrv(var->attrs, ATTR_POINTERTYPE); /* Top level pointers to conformant arrays may be handled specially since we can bypass the pointer, but if the array is burried beneath another pointer (e.g., "[size_is(,n)] int **p" then we always need to write the pointer. */ if (!ptr_type && var->type != type) /* FIXME: This should use pointer_default, but the information isn't kept around for arrays. */ ptr_type = RPC_FC_UP; if (ptr_type && ptr_type != RPC_FC_RP) { unsigned int absoff = type->typestring_offset; short reloff = absoff - (*typeformat_offset + 2); off = *typeformat_offset; print_file(file, 0, "/* %d */\n", off); print_file(file, 2, "0x%x, 0x0,\t/* %s */\n", ptr_type, string_of_type(ptr_type)); print_file(file, 2, "NdrFcShort(0x%hx),\t/* Offset= %hd (%u) */\n", reloff, reloff, absoff); *typeformat_offset += 4; } return off; } if (!is_ptr(type)) { /* basic types don't need a type format string */ if (is_base_type(type->type)) return 0; switch (type->type) { case RPC_FC_STRUCT: case RPC_FC_PSTRUCT: case RPC_FC_CSTRUCT: case RPC_FC_CPSTRUCT: case RPC_FC_CVSTRUCT: case RPC_FC_BOGUS_STRUCT: return write_struct_tfs(file, type, var->name, typeformat_offset); case RPC_FC_ENCAPSULATED_UNION: case RPC_FC_NON_ENCAPSULATED_UNION: return write_union_tfs(file, type, typeformat_offset); case RPC_FC_IGNORE: case RPC_FC_BIND_PRIMITIVE: /* nothing to do */ return 0; default: error("write_typeformatstring_var: Unsupported type 0x%x for variable %s\n", type->type, var->name); } } else if (last_ptr(type)) { size_t start_offset = *typeformat_offset; int in_attr = is_attr(var->attrs, ATTR_IN); int out_attr = is_attr(var->attrs, ATTR_OUT); const type_t *base = type->ref; if (base->type == RPC_FC_IP || (base->type == 0 && is_attr(var->attrs, ATTR_IIDIS))) { return write_ip_tfs(file, var->attrs, type, typeformat_offset); } /* special case for pointers to base types */ if (is_base_type(base->type)) { print_file(file, indent, "0x%x, 0x%x, /* %s %s[simple_pointer] */\n", type->type, (!in_attr && out_attr) ? 0x0C : 0x08, string_of_type(type->type), (!in_attr && out_attr) ? "[allocated_on_stack] " : ""); print_file(file, indent, "0x%02x, /* %s */\n", base->type, string_of_type(base->type)); print_file(file, indent, "0x5c, /* FC_PAD */\n"); *typeformat_offset += 4; return start_offset; } } assert(is_ptr(type)); offset = write_typeformatstring_var(file, indent, func, type->ref, var, typeformat_offset); if (file) fprintf(file, "/* %2u */\n", *typeformat_offset); return write_pointer_only_tfs(file, var->attrs, type->type, !last_ptr(type) ? 0x10 : 0, offset, typeformat_offset); } static int write_embedded_types(FILE *file, const attr_list_t *attrs, type_t *type, const char *name, int write_ptr, unsigned int *tfsoff) { int retmask = 0; if (is_user_type(type)) { write_user_tfs(file, type, tfsoff); } else if (is_ptr(type)) { type_t *ref = type->ref; if (ref->type == RPC_FC_IP || (ref->type == 0 && is_attr(attrs, ATTR_IIDIS))) { write_ip_tfs(file, attrs, type, tfsoff); } else { if (!processed(ref) && !is_base_type(ref->type)) retmask |= write_embedded_types(file, NULL, ref, name, TRUE, tfsoff); if (write_ptr) write_pointer_tfs(file, type, tfsoff); retmask |= 1; } } else if (last_array(type) && is_attr(attrs, ATTR_STRING)) { write_string_tfs(file, attrs, type, name, tfsoff, FALSE); } else if (type->declarray && is_conformant_array(type)) ; /* conformant arrays and strings are handled specially */ else if (is_array(type)) { write_array_tfs(file, attrs, type, name, tfsoff); if (is_conformant_array(type)) retmask |= 1; } else if (is_struct(type->type)) { if (!processed(type)) write_struct_tfs(file, type, name, tfsoff); } else if (is_union(type->type)) { if (!processed(type)) write_union_tfs(file, type, tfsoff); } else if (!is_base_type(type->type)) error("write_embedded_types: unknown embedded type for %s (0x%x)\n", name, type->type); return retmask; } static size_t process_tfs(FILE *file, const ifref_list_t *ifaces, type_pred_t pred) { const var_t *var; const ifref_t *iface; unsigned int typeformat_offset = 2; if (ifaces) LIST_FOR_EACH_ENTRY( iface, ifaces, const ifref_t, entry ) { if (!pred(iface->iface)) continue; if (iface->iface->funcs) { const func_t *func; current_iface = iface; LIST_FOR_EACH_ENTRY( func, iface->iface->funcs, const func_t, entry ) { if (is_local(func->def->attrs)) continue; current_func = func; if (func->args) LIST_FOR_EACH_ENTRY( var, func->args, const var_t, entry ) update_tfsoff( var->type, write_typeformatstring_var( file, 2, func, var->type, var, &typeformat_offset), file); } } } return typeformat_offset + 1; } void write_typeformatstring(FILE *file, const ifref_list_t *ifaces, type_pred_t pred) { int indent = 0; print_file(file, indent, "static const MIDL_TYPE_FORMAT_STRING __MIDL_TypeFormatString =\n"); print_file(file, indent, "{\n"); indent++; print_file(file, indent, "0,\n"); print_file(file, indent, "{\n"); indent++; print_file(file, indent, "NdrFcShort(0x0),\n"); set_all_tfswrite(TRUE); process_tfs(file, ifaces, pred); print_file(file, indent, "0x0\n"); indent--; print_file(file, indent, "}\n"); indent--; print_file(file, indent, "};\n"); print_file(file, indent, "\n"); } static unsigned int get_required_buffer_size_type( const type_t *type, const char *name, unsigned int *alignment) { *alignment = 0; if (is_user_type(type)) { const char *uname; const type_t *utype = get_user_type(type, &uname); return get_required_buffer_size_type(utype, uname, alignment); } else { switch (type->type) { case RPC_FC_BYTE: case RPC_FC_CHAR: case RPC_FC_USMALL: case RPC_FC_SMALL: *alignment = 4; return 1; case RPC_FC_WCHAR: case RPC_FC_USHORT: case RPC_FC_SHORT: case RPC_FC_ENUM16: *alignment = 4; return 2; case RPC_FC_ULONG: case RPC_FC_LONG: case RPC_FC_ENUM32: case RPC_FC_FLOAT: case RPC_FC_ERROR_STATUS_T: *alignment = 4; return 4; case RPC_FC_HYPER: case RPC_FC_DOUBLE: *alignment = 8; return 8; case RPC_FC_IGNORE: case RPC_FC_BIND_PRIMITIVE: return 0; case RPC_FC_STRUCT: case RPC_FC_PSTRUCT: { size_t size = 0; const var_t *field; if (!type->fields) return 0; LIST_FOR_EACH_ENTRY( field, type->fields, const var_t, entry ) { unsigned int alignment; size += get_required_buffer_size_type(field->type, field->name, &alignment); } return size; } case RPC_FC_RP: return is_base_type( type->ref->type ) || type->ref->type == RPC_FC_STRUCT ? get_required_buffer_size_type( type->ref, name, alignment ) : 0; case RPC_FC_SMFARRAY: case RPC_FC_LGFARRAY: return type->dim * get_required_buffer_size_type(type->ref, name, alignment); default: return 0; } } } static unsigned int get_required_buffer_size(const var_t *var, unsigned int *alignment, enum pass pass) { int in_attr = is_attr(var->attrs, ATTR_IN); int out_attr = is_attr(var->attrs, ATTR_OUT); const type_t *t; if (!in_attr && !out_attr) in_attr = 1; *alignment = 0; for (t = var->type; is_ptr(t); t = t->ref) if (is_attr(t->attrs, ATTR_CONTEXTHANDLE)) { *alignment = 4; return 20; } if (pass == PASS_OUT) { if (out_attr && is_ptr(var->type)) { type_t *type = var->type; if (type->type == RPC_FC_STRUCT) { const var_t *field; unsigned int size = 36; if (!type->fields) return size; LIST_FOR_EACH_ENTRY( field, type->fields, const var_t, entry ) { unsigned int align; size += get_required_buffer_size_type( field->type, field->name, &align); } return size; } } return 0; } else { if ((!out_attr || in_attr) && !var->type->size_is && !is_attr(var->attrs, ATTR_STRING) && !var->type->declarray) { if (is_ptr(var->type)) { type_t *type = var->type; if (is_base_type(type->type)) { return 25; } else if (type->type == RPC_FC_STRUCT) { unsigned int size = 36; const var_t *field; if (!type->fields) return size; LIST_FOR_EACH_ENTRY( field, type->fields, const var_t, entry ) { unsigned int align; size += get_required_buffer_size_type( field->type, field->name, &align); } return size; } } } return get_required_buffer_size_type(var->type, var->name, alignment); } } static unsigned int get_function_buffer_size( const func_t *func, enum pass pass ) { const var_t *var; unsigned int total_size = 0, alignment; if (func->args) { LIST_FOR_EACH_ENTRY( var, func->args, const var_t, entry ) { total_size += get_required_buffer_size(var, &alignment, pass); total_size += alignment; } } if (pass == PASS_OUT && !is_void(func->def->type)) { total_size += get_required_buffer_size(func->def, &alignment, PASS_RETURN); total_size += alignment; } return total_size; } static void print_phase_function(FILE *file, int indent, const char *type, enum remoting_phase phase, const var_t *var, unsigned int type_offset) { const char *function; switch (phase) { case PHASE_BUFFERSIZE: function = "BufferSize"; break; case PHASE_MARSHAL: function = "Marshall"; break; case PHASE_UNMARSHAL: function = "Unmarshall"; break; case PHASE_FREE: function = "Free"; break; default: assert(0); return; } print_file(file, indent, "Ndr%s%s(\n", type, function); indent++; print_file(file, indent, "&_StubMsg,\n"); print_file(file, indent, "%s%s%s%s,\n", (phase == PHASE_UNMARSHAL) ? "(unsigned char **)" : "(unsigned char *)", (phase == PHASE_UNMARSHAL || decl_indirect(var->type)) ? "&" : "", (phase == PHASE_UNMARSHAL && decl_indirect(var->type)) ? "_p_" : "", var->name); print_file(file, indent, "(PFORMAT_STRING)&__MIDL_TypeFormatString.Format[%d]%s\n", type_offset, (phase == PHASE_UNMARSHAL) ? "," : ");"); if (phase == PHASE_UNMARSHAL) print_file(file, indent, "0);\n"); indent--; } void print_phase_basetype(FILE *file, int indent, enum remoting_phase phase, enum pass pass, const var_t *var, const char *varname) { type_t *type = var->type; unsigned int size; unsigned int alignment = 0; unsigned char rtype; /* no work to do for other phases, buffer sizing is done elsewhere */ if (phase != PHASE_MARSHAL && phase != PHASE_UNMARSHAL) return; rtype = is_ptr(type) ? type->ref->type : type->type; switch (rtype) { case RPC_FC_BYTE: case RPC_FC_CHAR: case RPC_FC_SMALL: case RPC_FC_USMALL: size = 1; alignment = 1; break; case RPC_FC_WCHAR: case RPC_FC_USHORT: case RPC_FC_SHORT: case RPC_FC_ENUM16: size = 2; alignment = 2; break; case RPC_FC_ULONG: case RPC_FC_LONG: case RPC_FC_ENUM32: case RPC_FC_FLOAT: case RPC_FC_ERROR_STATUS_T: size = 4; alignment = 4; break; case RPC_FC_HYPER: case RPC_FC_DOUBLE: size = 8; alignment = 8; break; case RPC_FC_IGNORE: case RPC_FC_BIND_PRIMITIVE: /* no marshalling needed */ return; default: error("print_phase_basetype: Unsupported type: %s (0x%02x, ptr_level: 0)\n", var->name, rtype); size = 0; } if (phase == PHASE_MARSHAL) print_file(file, indent, "MIDL_memset(_StubMsg.Buffer, 0, (0x%x - (long)_StubMsg.Buffer) & 0x%x);\n", alignment, alignment - 1); print_file(file, indent, "_StubMsg.Buffer = (unsigned char *)(((long)_StubMsg.Buffer + %u) & ~0x%x);\n", alignment - 1, alignment - 1); if (phase == PHASE_MARSHAL) { print_file(file, indent, "*("); write_type_decl(file, is_ptr(type) ? type->ref : type, NULL); if (is_ptr(type)) fprintf(file, " *)_StubMsg.Buffer = *"); else fprintf(file, " *)_StubMsg.Buffer = "); fprintf(file, "%s", varname); fprintf(file, ";\n"); } else if (phase == PHASE_UNMARSHAL) { print_file(file, indent, "if (_StubMsg.Buffer + sizeof("); write_type_decl(file, is_ptr(type) ? type->ref : type, NULL); fprintf(file, ") > _StubMsg.BufferEnd)\n"); print_file(file, indent, "{\n"); print_file(file, indent + 1, "RpcRaiseException(RPC_X_BAD_STUB_DATA);\n"); print_file(file, indent, "}\n"); if (pass == PASS_IN || pass == PASS_RETURN) print_file(file, indent, ""); else print_file(file, indent, "*"); fprintf(file, "%s", varname); if (pass == PASS_IN && is_ptr(type)) fprintf(file, " = ("); else fprintf(file, " = *("); write_type_decl(file, is_ptr(type) ? type->ref : type, NULL); fprintf(file, " *)_StubMsg.Buffer;\n"); } print_file(file, indent, "_StubMsg.Buffer += sizeof("); write_type_decl(file, var->type, NULL); fprintf(file, ");\n"); } /* returns whether the MaxCount, Offset or ActualCount members need to be * filled in for the specified phase */ static inline int is_size_needed_for_phase(enum remoting_phase phase) { return (phase != PHASE_UNMARSHAL); } expr_t *get_size_is_expr(const type_t *t, const char *name) { expr_t *x = NULL; for ( ; is_ptr(t) || is_array(t); t = t->ref) if (t->size_is) { if (!x) x = t->size_is; else error("%s: multidimensional conformant" " arrays not supported at the top level\n", name); } return x; } static void write_remoting_arg(FILE *file, int indent, const func_t *func, enum pass pass, enum remoting_phase phase, const var_t *var) { int in_attr, out_attr, pointer_type; const type_t *type = var->type; unsigned char rtype; size_t start_offset = type->typestring_offset; pointer_type = get_attrv(var->attrs, ATTR_POINTERTYPE); if (!pointer_type) pointer_type = RPC_FC_RP; in_attr = is_attr(var->attrs, ATTR_IN); out_attr = is_attr(var->attrs, ATTR_OUT); if (!in_attr && !out_attr) in_attr = 1; if (phase != PHASE_FREE) switch (pass) { case PASS_IN: if (!in_attr) return; break; case PASS_OUT: if (!out_attr) return; break; case PASS_RETURN: break; } rtype = type->type; if (is_context_handle(type)) { if (phase == PHASE_MARSHAL) { if (pass == PASS_IN) { print_file(file, indent, "NdrClientContextMarshall(\n"); print_file(file, indent + 1, "&_StubMsg,\n"); print_file(file, indent + 1, "(NDR_CCONTEXT)%s%s,\n", is_ptr(type) ? "*" : "", var->name); print_file(file, indent + 1, "%s);\n", in_attr && out_attr ? "1" : "0"); } else { print_file(file, indent, "NdrServerContextNewMarshall(\n"); print_file(file, indent + 1, "&_StubMsg,\n"); print_file(file, indent + 1, "(NDR_SCONTEXT)%s,\n", var->name); print_file(file, indent + 1, "(NDR_RUNDOWN)%s_rundown,\n", get_context_handle_type_name(var->type)); print_file(file, indent + 1, "(PFORMAT_STRING)&__MIDL_TypeFormatString.Format[%d]);\n", start_offset); } } else if (phase == PHASE_UNMARSHAL) { if (pass == PASS_OUT) { print_file(file, indent, "NdrClientContextUnmarshall(\n"); print_file(file, indent + 1, "&_StubMsg,\n"); print_file(file, indent + 1, "(NDR_CCONTEXT *)%s,\n", var->name); print_file(file, indent + 1, "_Handle);\n"); } else { print_file(file, indent, "%s = NdrServerContextNewUnmarshall(\n", var->name); print_file(file, indent + 1, "&_StubMsg,\n"); print_file(file, indent + 1, "(PFORMAT_STRING)&__MIDL_TypeFormatString.Format[%d]);\n", start_offset); } } } else if (is_user_type(var->type)) { print_phase_function(file, indent, "UserMarshal", phase, var, start_offset); } else if (is_string_type(var->attrs, var->type)) { if (is_array(type) && !is_conformant_array(type)) print_phase_function(file, indent, "NonConformantString", phase, var, start_offset); else { if (type->size_is && is_size_needed_for_phase(phase)) { print_file(file, indent, "_StubMsg.MaxCount = (unsigned long)"); write_expr(file, type->size_is, 1); fprintf(file, ";\n"); } if ((phase == PHASE_FREE) || (pointer_type == RPC_FC_UP)) print_phase_function(file, indent, "Pointer", phase, var, start_offset - (type->size_is ? 4 : 2)); else print_phase_function(file, indent, "ConformantString", phase, var, start_offset); } } else if (is_array(type)) { unsigned char tc = type->type; const char *array_type = "FixedArray"; /* We already have the size_is expression since it's at the top level, but do checks for multidimensional conformant arrays. When we handle them, we'll need to extend this function to return a list, and then we'll actually use the return value. */ get_size_is_expr(type, var->name); if (tc == RPC_FC_SMVARRAY || tc == RPC_FC_LGVARRAY) { if (is_size_needed_for_phase(phase)) { print_file(file, indent, "_StubMsg.Offset = (unsigned long)0;\n"); /* FIXME */ print_file(file, indent, "_StubMsg.ActualCount = (unsigned long)"); write_expr(file, type->length_is, 1); fprintf(file, ";\n\n"); } array_type = "VaryingArray"; } else if (tc == RPC_FC_CARRAY) { if (is_size_needed_for_phase(phase)) { print_file(file, indent, "_StubMsg.MaxCount = (unsigned long)"); write_expr(file, type->size_is, 1); fprintf(file, ";\n\n"); } array_type = "ConformantArray"; } else if (tc == RPC_FC_CVARRAY || tc == RPC_FC_BOGUS_ARRAY) { if (is_size_needed_for_phase(phase)) { if (type->size_is) { print_file(file, indent, "_StubMsg.MaxCount = (unsigned long)"); write_expr(file, type->size_is, 1); fprintf(file, ";\n"); } if (type->length_is) { print_file(file, indent, "_StubMsg.Offset = (unsigned long)0;\n"); /* FIXME */ print_file(file, indent, "_StubMsg.ActualCount = (unsigned long)"); write_expr(file, type->length_is, 1); fprintf(file, ";\n\n"); } } array_type = (tc == RPC_FC_BOGUS_ARRAY ? "ComplexArray" : "ConformantVaryingArray"); } if (pointer_type != RPC_FC_RP) array_type = "Pointer"; print_phase_function(file, indent, array_type, phase, var, start_offset); if (phase == PHASE_FREE && pointer_type == RPC_FC_RP) { /* these are all unmarshalled by allocating memory */ if (type->type == RPC_FC_BOGUS_ARRAY || type->type == RPC_FC_CVARRAY || ((type->type == RPC_FC_SMVARRAY || type->type == RPC_FC_LGVARRAY) && in_attr) || (type->type == RPC_FC_CARRAY && !in_attr)) { print_file(file, indent, "if (%s)\n", var->name); indent++; print_file(file, indent, "_StubMsg.pfnFree(%s);\n", var->name); } } } else if (!is_ptr(var->type) && is_base_type(rtype)) { if (phase != PHASE_FREE) print_phase_basetype(file, indent, phase, pass, var, var->name); } else if (!is_ptr(var->type)) { switch (rtype) { case RPC_FC_STRUCT: case RPC_FC_PSTRUCT: print_phase_function(file, indent, "SimpleStruct", phase, var, start_offset); break; case RPC_FC_CSTRUCT: case RPC_FC_CPSTRUCT: print_phase_function(file, indent, "ConformantStruct", phase, var, start_offset); break; case RPC_FC_CVSTRUCT: print_phase_function(file, indent, "ConformantVaryingStruct", phase, var, start_offset); break; case RPC_FC_BOGUS_STRUCT: print_phase_function(file, indent, "ComplexStruct", phase, var, start_offset); break; case RPC_FC_RP: if (is_base_type( var->type->ref->type )) { print_phase_basetype(file, indent, phase, pass, var, var->name); } else if (var->type->ref->type == RPC_FC_STRUCT) { if (phase != PHASE_BUFFERSIZE && phase != PHASE_FREE) print_phase_function(file, indent, "SimpleStruct", phase, var, start_offset + 4); } else { expr_t *iid; if ((iid = get_attrp( var->attrs, ATTR_IIDIS ))) { print_file( file, indent, "_StubMsg.MaxCount = (unsigned long) " ); write_expr( file, iid, 1 ); fprintf( file, ";\n\n" ); } print_phase_function(file, indent, "Pointer", phase, var, start_offset); } break; default: error("write_remoting_arguments: Unsupported type: %s (0x%02x)\n", var->name, rtype); } } else { if (last_ptr(var->type) && (pointer_type == RPC_FC_RP) && is_base_type(rtype)) { if (phase != PHASE_FREE) print_phase_basetype(file, indent, phase, pass, var, var->name); } else if (last_ptr(var->type) && (pointer_type == RPC_FC_RP) && (rtype == RPC_FC_STRUCT)) { if (phase != PHASE_BUFFERSIZE && phase != PHASE_FREE) print_phase_function(file, indent, "SimpleStruct", phase, var, start_offset + 4); } else { expr_t *iid; expr_t *sx = get_size_is_expr(type, var->name); if ((iid = get_attrp( var->attrs, ATTR_IIDIS ))) { print_file( file, indent, "_StubMsg.MaxCount = (unsigned long) " ); write_expr( file, iid, 1 ); fprintf( file, ";\n\n" ); } else if (sx) { print_file(file, indent, "_StubMsg.MaxCount = (unsigned long) "); write_expr(file, sx, 1); fprintf(file, ";\n\n"); } if (var->type->ref->type == RPC_FC_IP) print_phase_function(file, indent, "InterfacePointer", phase, var, start_offset); else print_phase_function(file, indent, "Pointer", phase, var, start_offset); } } fprintf(file, "\n"); } void write_remoting_arguments(FILE *file, int indent, const func_t *func, enum pass pass, enum remoting_phase phase) { if (phase == PHASE_BUFFERSIZE && pass != PASS_RETURN) { unsigned int size = get_function_buffer_size( func, pass ); print_file(file, indent, "_StubMsg.BufferLength = %u;\n", size); } if (pass == PASS_RETURN) { var_t var; var = *func->def; var.name = xstrdup( "_RetVal" ); write_remoting_arg( file, indent, func, pass, phase, &var ); free( var.name ); } else { const var_t *var; if (!func->args) return; LIST_FOR_EACH_ENTRY( var, func->args, const var_t, entry ) write_remoting_arg( file, indent, func, pass, phase, var ); } } size_t get_size_procformatstring_var(const var_t *var) { return write_procformatstring_var(NULL, 0, var, FALSE); } size_t get_size_procformatstring_func(const func_t *func) { const var_t *var; size_t size = 0; /* argument list size */ if (func->args) LIST_FOR_EACH_ENTRY( var, func->args, const var_t, entry ) size += get_size_procformatstring_var(var); /* return value size */ if (is_void(func->def->type)) size += 2; /* FC_END and FC_PAD */ else size += get_size_procformatstring_var(func->def); return size; } size_t get_size_procformatstring(const ifref_list_t *ifaces, type_pred_t pred) { const ifref_t *iface; size_t size = 1; const func_t *func; if (ifaces) LIST_FOR_EACH_ENTRY( iface, ifaces, const ifref_t, entry ) { if (!pred(iface->iface)) continue; if (iface->iface->funcs) LIST_FOR_EACH_ENTRY( func, iface->iface->funcs, const func_t, entry ) if (!is_local(func->def->attrs)) size += get_size_procformatstring_func( func ); } return size; } size_t get_size_typeformatstring(const ifref_list_t *ifaces, type_pred_t pred) { set_all_tfswrite(FALSE); return process_tfs(NULL, ifaces, pred); } static void write_struct_expr(FILE *h, const expr_t *e, int brackets, const var_list_t *fields, const char *structvar) { switch (e->type) { case EXPR_VOID: break; case EXPR_NUM: fprintf(h, "%lu", e->u.lval); break; case EXPR_HEXNUM: fprintf(h, "0x%lx", e->u.lval); break; case EXPR_DOUBLE: fprintf(h, "%#.15g", e->u.dval); break; case EXPR_TRUEFALSE: if (e->u.lval == 0) fprintf(h, "FALSE"); else fprintf(h, "TRUE"); break; case EXPR_IDENTIFIER: { const var_t *field; LIST_FOR_EACH_ENTRY( field, fields, const var_t, entry ) if (!strcmp(e->u.sval, field->name)) { fprintf(h, "%s->%s", structvar, e->u.sval); break; } if (&field->entry == fields) error("no field found for identifier %s\n", e->u.sval); break; } case EXPR_NEG: fprintf(h, "-"); write_struct_expr(h, e->ref, 1, fields, structvar); break; case EXPR_NOT: fprintf(h, "~"); write_struct_expr(h, e->ref, 1, fields, structvar); break; case EXPR_PPTR: fprintf(h, "*"); write_struct_expr(h, e->ref, 1, fields, structvar); break; case EXPR_CAST: fprintf(h, "("); write_type_decl(h, e->u.tref, NULL); fprintf(h, ")"); write_struct_expr(h, e->ref, 1, fields, structvar); break; case EXPR_SIZEOF: fprintf(h, "sizeof("); write_type_decl(h, e->u.tref, NULL); fprintf(h, ")"); break; case EXPR_SHL: case EXPR_SHR: case EXPR_MUL: case EXPR_DIV: case EXPR_ADD: case EXPR_SUB: case EXPR_AND: case EXPR_OR: if (brackets) fprintf(h, "("); write_struct_expr(h, e->ref, 1, fields, structvar); switch (e->type) { case EXPR_SHL: fprintf(h, " << "); break; case EXPR_SHR: fprintf(h, " >> "); break; case EXPR_MUL: fprintf(h, " * "); break; case EXPR_DIV: fprintf(h, " / "); break; case EXPR_ADD: fprintf(h, " + "); break; case EXPR_SUB: fprintf(h, " - "); break; case EXPR_AND: fprintf(h, " & "); break; case EXPR_OR: fprintf(h, " | "); break; default: break; } write_struct_expr(h, e->u.ext, 1, fields, structvar); if (brackets) fprintf(h, ")"); break; case EXPR_COND: if (brackets) fprintf(h, "("); write_struct_expr(h, e->ref, 1, fields, structvar); fprintf(h, " ? "); write_struct_expr(h, e->u.ext, 1, fields, structvar); fprintf(h, " : "); write_struct_expr(h, e->ext2, 1, fields, structvar); if (brackets) fprintf(h, ")"); break; case EXPR_ADDRESSOF: fprintf(h, "&"); write_struct_expr(h, e->ref, 1, fields, structvar); break; } } void declare_stub_args( FILE *file, int indent, const func_t *func ) { int in_attr, out_attr; int i = 0; const var_t *def = func->def; const var_t *var; /* declare return value '_RetVal' */ if (!is_void(def->type)) { print_file(file, indent, ""); write_type_decl_left(file, def->type); fprintf(file, " _RetVal;\n"); } if (!func->args) return; LIST_FOR_EACH_ENTRY( var, func->args, const var_t, entry ) { int is_string = is_attr(var->attrs, ATTR_STRING); in_attr = is_attr(var->attrs, ATTR_IN); out_attr = is_attr(var->attrs, ATTR_OUT); if (!out_attr && !in_attr) in_attr = 1; if (is_context_handle(var->type)) print_file(file, indent, "NDR_SCONTEXT %s;\n", var->name); else { if (!in_attr && !var->type->size_is && !is_string) { print_file(file, indent, ""); write_type_decl(file, var->type->declarray ? var->type : var->type->ref, "_W%u", i++); fprintf(file, ";\n"); } print_file(file, indent, ""); write_type_decl_left(file, var->type); fprintf(file, " "); if (var->type->declarray) { fprintf(file, "( *"); write_name(file, var); fprintf(file, " )"); } else write_name(file, var); write_type_right(file, var->type, FALSE); fprintf(file, ";\n"); if (decl_indirect(var->type)) print_file(file, indent, "void *_p_%s = &%s;\n", var->name, var->name); } } } void assign_stub_out_args( FILE *file, int indent, const func_t *func ) { int in_attr, out_attr; int i = 0, sep = 0; const var_t *var; if (!func->args) return; LIST_FOR_EACH_ENTRY( var, func->args, const var_t, entry ) { int is_string = is_attr(var->attrs, ATTR_STRING); in_attr = is_attr(var->attrs, ATTR_IN); out_attr = is_attr(var->attrs, ATTR_OUT); if (!out_attr && !in_attr) in_attr = 1; if (!in_attr) { print_file(file, indent, ""); write_name(file, var); if (is_context_handle(var->type)) { fprintf(file, " = NdrContextHandleInitialize(\n"); print_file(file, indent + 1, "&_StubMsg,\n"); print_file(file, indent + 1, "(PFORMAT_STRING)&__MIDL_TypeFormatString.Format[%d]);\n", var->type->typestring_offset); } else if (var->type->size_is) { unsigned int size, align = 0; type_t *type = var->type; fprintf(file, " = NdrAllocate(&_StubMsg, "); for ( ; type->size_is ; type = type->ref) { write_expr(file, type->size_is, TRUE); fprintf(file, " * "); } size = type_memsize(type, &align); fprintf(file, "%u);\n", size); } else if (!is_string) { fprintf(file, " = &_W%u;\n", i); if (is_ptr(var->type) && !last_ptr(var->type)) print_file(file, indent, "_W%u = 0;\n", i); i++; } sep = 1; } } if (sep) fprintf(file, "\n"); } int write_expr_eval_routines(FILE *file, const char *iface) { static const char *var_name = "pS"; int result = 0; struct expr_eval_routine *eval; unsigned short callback_offset = 0; LIST_FOR_EACH_ENTRY(eval, &expr_eval_routines, struct expr_eval_routine, entry) { const char *name = eval->structure->name; const var_list_t *fields = eval->structure->fields; result = 1; print_file(file, 0, "static void __RPC_USER %s_%sExprEval_%04u(PMIDL_STUB_MESSAGE pStubMsg)\n", iface, name, callback_offset); print_file(file, 0, "{\n"); print_file (file, 1, "%s *%s = (%s *)(pStubMsg->StackTop - %u);\n", name, var_name, name, eval->baseoff); print_file(file, 1, "pStubMsg->Offset = 0;\n"); /* FIXME */ print_file(file, 1, "pStubMsg->MaxCount = (unsigned long)"); write_struct_expr(file, eval->expr, 1, fields, var_name); fprintf(file, ";\n"); print_file(file, 0, "}\n\n"); callback_offset++; } return result; } void write_expr_eval_routine_list(FILE *file, const char *iface) { struct expr_eval_routine *eval; struct expr_eval_routine *cursor; unsigned short callback_offset = 0; fprintf(file, "static const EXPR_EVAL ExprEvalRoutines[] =\n"); fprintf(file, "{\n"); LIST_FOR_EACH_ENTRY_SAFE(eval, cursor, &expr_eval_routines, struct expr_eval_routine, entry) { const char *name = eval->structure->name; print_file(file, 1, "%s_%sExprEval_%04u,\n", iface, name, callback_offset); callback_offset++; list_remove(&eval->entry); free(eval); } fprintf(file, "};\n\n"); } void write_user_quad_list(FILE *file) { user_type_t *ut; if (list_empty(&user_type_list)) return; fprintf(file, "static const USER_MARSHAL_ROUTINE_QUADRUPLE UserMarshalRoutines[] =\n"); fprintf(file, "{\n"); LIST_FOR_EACH_ENTRY(ut, &user_type_list, user_type_t, entry) { const char *sep = &ut->entry == list_tail(&user_type_list) ? "" : ","; print_file(file, 1, "{\n"); print_file(file, 2, "(USER_MARSHAL_SIZING_ROUTINE)%s_UserSize,\n", ut->name); print_file(file, 2, "(USER_MARSHAL_MARSHALLING_ROUTINE)%s_UserMarshal,\n", ut->name); print_file(file, 2, "(USER_MARSHAL_UNMARSHALLING_ROUTINE)%s_UserUnmarshal,\n", ut->name); print_file(file, 2, "(USER_MARSHAL_FREEING_ROUTINE)%s_UserFree\n", ut->name); print_file(file, 1, "}%s\n", sep); } fprintf(file, "};\n\n"); } void write_endpoints( FILE *f, const char *prefix, const str_list_t *list ) { const struct str_list_entry_t *endpoint; const char *p; /* this should be an array of RPC_PROTSEQ_ENDPOINT but we want const strings */ print_file( f, 0, "static const unsigned char * %s__RpcProtseqEndpoint[][2] =\n{\n", prefix ); LIST_FOR_EACH_ENTRY( endpoint, list, const struct str_list_entry_t, entry ) { print_file( f, 1, "{ (const unsigned char *)\"" ); for (p = endpoint->str; *p && *p != ':'; p++) { if (*p == '"' || *p == '\\') fputc( '\\', f ); fputc( *p, f ); } if (!*p) goto error; if (p[1] != '[') goto error; fprintf( f, "\", (const unsigned char *)\"" ); for (p += 2; *p && *p != ']'; p++) { if (*p == '"' || *p == '\\') fputc( '\\', f ); fputc( *p, f ); } if (*p != ']') goto error; fprintf( f, "\" },\n" ); } print_file( f, 0, "};\n\n" ); return; error: error("Invalid endpoint syntax '%s'\n", endpoint->str); }