mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-02-26 21:51:28 +00:00
Switching to native ruamoko bool for vkgen resulted in bool getting incorrectly parsed as a string (which mean getting the string's address written to the value). Fixes the randomly enabled debug pipelines (and probably a few randomly disabled normal pipelines, though less likely as they're usually implicitly initialized). The one bit of actual legit UB (ie, that which wasn't immediately mitigated one way or another) found by ubsan, and it was a data error.
578 lines
14 KiB
R
578 lines
14 KiB
R
#include <hash.h>
|
|
#include <qfile.h>
|
|
#include <runtime.h>
|
|
#include <string.h>
|
|
#include <types.h>
|
|
#include <Array.h>
|
|
#include <PropertyList.h>
|
|
|
|
#include "vkfielddef.h"
|
|
#include "vkfieldtype.h"
|
|
#include "vkgen.h"
|
|
#include "vkstruct.h"
|
|
|
|
@implementation Struct
|
|
|
|
-(void) dealloc
|
|
{
|
|
[field_dict release];
|
|
[field_defs release];
|
|
[parse_def release];
|
|
[only release];
|
|
str_free (outname);
|
|
str_free (label_field);
|
|
[super dealloc];
|
|
}
|
|
|
|
-(string) name
|
|
{
|
|
return str_mid(type.strct.tag, 4);
|
|
}
|
|
|
|
-(void) addToQueue
|
|
{
|
|
string name = [self name];
|
|
if (!Hash_Find (processed_types, name)) {
|
|
//printf (" +%s\n", name);
|
|
Hash_Add (processed_types, (void *) name);
|
|
[queue addObject: self];
|
|
}
|
|
}
|
|
|
|
-(void) queueFieldTypes
|
|
{
|
|
qfot_struct_t *strct =&type.strct;
|
|
PLItem *field_dict = [parse getObjectForKey:[self name]];
|
|
int readonly = [field_dict string] == "readonly";
|
|
|
|
if (readonly) {
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < strct.num_fields; i++) {
|
|
qfot_var_t *var = &strct.fields[i];
|
|
if (field_dict) {
|
|
PLItem *item = [field_dict getObjectForKey:var.name];
|
|
FieldDef *def = [FieldDef fielddef:item
|
|
struct:self
|
|
field:var.name];
|
|
if (![def searchType]) {
|
|
continue;
|
|
}
|
|
}
|
|
Type *type = [Type findType:var.type];
|
|
[type addToQueue];
|
|
}
|
|
}
|
|
|
|
-(qfot_var_t *)findField:(string) fieldName
|
|
{
|
|
for (int i = 0; i < type.strct.num_fields; i++) {
|
|
qfot_var_t *var = &type.strct.fields[i];
|
|
if (var.name == fieldName) {
|
|
return var;
|
|
}
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
-(string)sTypeName
|
|
{
|
|
string s = "VK_STRUCTURE_TYPE";
|
|
string name = [self outname];
|
|
int length = strlen (name);
|
|
int start, end, c;
|
|
for (start = 2; start < length; start = end) {
|
|
for (end = start + 1; end < length; end++) {
|
|
c = str_char (name, end);
|
|
if (c >= 'A' && c <= 'Z') {
|
|
break;
|
|
}
|
|
}
|
|
s += "_" + str_mid (name, start, end);
|
|
}
|
|
str_free (name);
|
|
return str_upper (s);
|
|
}
|
|
|
|
-(void) setLabelField:(string)label_field
|
|
{
|
|
self.label_field = label_field;
|
|
}
|
|
|
|
-(void) writeForward
|
|
{
|
|
PLItem *field_dict = [parse getObjectForKey:[self name]];
|
|
int readonly = [field_dict string] == "readonly";
|
|
if (!readonly) {
|
|
fprintf (output_file, "static int %s (const plfield_t *field,"
|
|
" const plitem_t *item, void *data, plitem_t *messages,"
|
|
" void *context);\n",
|
|
[self parseFunc]);
|
|
}
|
|
}
|
|
|
|
static void
|
|
write_function_head (Struct *self)
|
|
{
|
|
fprintf (output_file, "static int %s (const plfield_t *field,"
|
|
" const plitem_t *item, void *data, plitem_t *messages,"
|
|
" void *context)\n",
|
|
[self parseFunc]);
|
|
fprintf (output_file, "{\n");
|
|
}
|
|
|
|
static void
|
|
write_function_tail (Struct *self)
|
|
{
|
|
fprintf (output_file, "}\n");
|
|
}
|
|
|
|
static void
|
|
write_parse_type (Struct *self, PLItem *item)
|
|
{
|
|
if ([item string] == "auto") {
|
|
fprintf (output_file, "\t\tif (!PL_ParseStruct (%s_fields, item, "
|
|
"data, messages, context)) {\n", [self outname]);
|
|
fprintf (output_file, "\t\t\treturn 0;\n");
|
|
fprintf (output_file, "\t\t}\n");
|
|
} else {
|
|
//FieldDef *def = [FieldDef fielddef:item struct:self field:".parse"];
|
|
[self.parse_def writeParse];
|
|
}
|
|
}
|
|
|
|
static void
|
|
write_auto_parse (Struct *self, string field, int name)
|
|
{
|
|
string item = name ? "name" : "item";
|
|
|
|
fprintf (output_file, "\t\tdo {\n");
|
|
fprintf (output_file, "\t\t\tplfield_t *f = find_field (%s_fields, %s, "
|
|
"item, messages);\n", [self outname], sprintf ("\"%s\"", field));
|
|
fprintf (output_file, "\t\t\tif (!f) {\n");
|
|
fprintf (output_file, "\t\t\t\treturn 0;\n");
|
|
fprintf (output_file, "\t\t\t}\n");
|
|
if (name) {
|
|
fprintf (output_file, "\t\t\tplitem_t *name = "
|
|
"PL_NewString (field->name);\n");
|
|
}
|
|
fprintf (output_file, "\t\t\tf->parser (f, %s, &%s, messages, context);\n",
|
|
item, sprintf ("((%s *) data)->%s", [self outname], field));
|
|
if (name) {
|
|
fprintf (output_file, "\t\t\tPL_Release (name);\n");
|
|
}
|
|
fprintf (output_file, "\t\t} while (0);\n");
|
|
}
|
|
|
|
static int
|
|
check_need_table (Struct *self, PLItem *field_dict, string type)
|
|
{
|
|
string key = nil;
|
|
switch (type) {
|
|
case "QFDictionary": key = ".dictionary"; break;
|
|
case "QFArray": key = ".array"; break;
|
|
case "QFBinary": key = ".binary"; break;
|
|
case "QFString": key = ".string"; break;
|
|
}
|
|
PLItem *type_obj = [field_dict getObjectForKey:key];
|
|
int count = [type_obj numKeys];
|
|
if (!count) {
|
|
return 0;
|
|
}
|
|
for (int i = 0; i < count; i++) {
|
|
string field = [type_obj keyAtIndex:i];
|
|
PLItem *item = [type_obj getObjectForKey:field];
|
|
string str = [item string];
|
|
|
|
if (field == ".parse") {
|
|
if (str != "auto") {
|
|
self.parse_def = [[FieldDef fielddef:item
|
|
struct:self
|
|
field:"parse"] retain];
|
|
}
|
|
return 1;
|
|
}
|
|
if (str == "$auto") {
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
write_type (Struct *self, PLItem *field_dict, string type)
|
|
{
|
|
string key = nil;
|
|
switch (type) {
|
|
case "QFDictionary": key = ".dictionary"; break;
|
|
case "QFArray": key = ".array"; break;
|
|
case "QFBinary": key = ".binary"; break;
|
|
case "QFString": key = ".string"; break;
|
|
}
|
|
PLItem *type_obj = [field_dict getObjectForKey:key];
|
|
int count = [type_obj numKeys];
|
|
if (!count) {
|
|
//FIXME errors
|
|
return;
|
|
}
|
|
fprintf (output_file, "\tif (type == %s) {\n", type);
|
|
for (int i = 0; i < count; i++) {
|
|
string field = [type_obj keyAtIndex:i];
|
|
PLItem *item = [type_obj getObjectForKey:field];
|
|
string str = [item string];
|
|
|
|
if (field == ".parse") {
|
|
write_parse_type (self, item);
|
|
continue;
|
|
}
|
|
|
|
switch (str) {
|
|
case "$item.string":
|
|
str = "vkstrdup (context, PL_String (item))";
|
|
break;
|
|
case "$item.line":
|
|
str = "PL_Line (item)";
|
|
break;
|
|
case "$name":
|
|
str = "vkstrdup (context, field->name)";
|
|
break;
|
|
case "$index":
|
|
str = "field->offset";
|
|
break;
|
|
case "$auto":
|
|
write_auto_parse (self, field, 0);
|
|
continue;
|
|
case "$name.auto":
|
|
write_auto_parse (self, field, 1);
|
|
continue;
|
|
}
|
|
fprintf (output_file, "\t\t((%s *) data)->%s = %s;\n", [self outname],
|
|
field, str);
|
|
}
|
|
fprintf (output_file, "\t\treturn 1;\n");
|
|
fprintf (output_file, "\t}\n");
|
|
}
|
|
|
|
static void
|
|
write_parser (Struct *self, int have_sType, PLItem *only)
|
|
{
|
|
write_function_head (self);
|
|
if (have_sType) {
|
|
fprintf (output_file, "\t((%s *) data)->sType", [self outname]);
|
|
fprintf (output_file, " = %s;\n", [self sTypeName]);
|
|
}
|
|
if (self.label_field) {
|
|
fprintf (output_file, "\t((%s *) data)->%s", [self outname],
|
|
self.label_field);
|
|
fprintf (output_file, " = vkstrdup (context, field->name);\n");
|
|
}
|
|
if (only) {
|
|
fprintf (output_file, "\tplfield_t *f = &%s_fields[0];\n",
|
|
[self outname]);
|
|
fprintf (output_file,
|
|
"\tif (!PL_CheckType (PL_Type (item), f->type)) {\n"
|
|
"\t\tPL_TypeMismatch (messages, item, "
|
|
"f->name, f->type, PL_Type (item));\n"
|
|
"\t\treturn 0;\n"
|
|
"\t}\n"
|
|
"\tvoid *flddata = (byte *)data + f->offset;\n"
|
|
"\treturn f->parser (f, item, flddata, messages, "
|
|
"context);\n");
|
|
} else {
|
|
fprintf (output_file,
|
|
"\tif (PL_Type (item) == QFString\n"
|
|
"\t\t&& !(item = parse_reference (item, \"%s\", "
|
|
"messages, context))) {\n"
|
|
"\t\treturn 0;\n"
|
|
"\t}\n"
|
|
"\treturn PL_ParseStruct (%s_fields, item, data, "
|
|
"messages, context);\n",
|
|
[self outname], [self outname]);
|
|
}
|
|
write_function_tail (self);
|
|
}
|
|
|
|
static void
|
|
write_cexpr (Struct *self, Array *field_defs)
|
|
{
|
|
fprintf (output_file, "static exprsym_t %s_symbols[] = {\n",
|
|
[self outname]);
|
|
if (field_defs) {
|
|
PLItem *field_def;
|
|
qfot_var_t *field;
|
|
|
|
for (int i = [field_defs count]; i-- > 0; ) {
|
|
FieldDef *field_def = [field_defs objectAtIndex:i];
|
|
[field_def writeSymbol];
|
|
}
|
|
} else {
|
|
for (int i = 0; i < self.type.strct.num_fields; i++) {
|
|
qfot_var_t *field = &self.type.strct.fields[i];
|
|
if (field.name == "sType" || field.name == "pNext") {
|
|
continue;
|
|
}
|
|
Type *field_type = [Type findType: field.type];
|
|
fprintf (output_file,
|
|
"\t{\"%s\", &%s, (void *) field_offset (%s, %s)},\n",
|
|
field.name, [field_type cexprType], [self outname],
|
|
field.name);
|
|
}
|
|
}
|
|
fprintf (output_file, "\t{ }\n");
|
|
fprintf (output_file, "};\n");
|
|
|
|
fprintf (output_file, "static exprtab_t %s_symtab = {\n", [self outname]);
|
|
fprintf (output_file, "\t%s_symbols,\n", [self outname]);
|
|
fprintf (output_file, "};\n");
|
|
|
|
fprintf (output_file, "exprtype_t %s_type = {\n", [self outname]);
|
|
fprintf (output_file, "\t.name = \"%s\",\n", [self outname]);
|
|
fprintf (output_file, "\t.size = sizeof (%s),\n", [self outname]);
|
|
fprintf (output_file, "\t.binops = cexpr_struct_binops,\n");
|
|
fprintf (output_file, "\t.unops = 0,\n");
|
|
fprintf (output_file, "\t.data = &%s_symtab,\n", [self outname]);
|
|
fprintf (output_file, "};\n");
|
|
fprintf (output_file, "\n");
|
|
fprintf (header_file, "extern exprtype_t %s_type;\n", [self outname]);
|
|
}
|
|
|
|
static void
|
|
write_table (Struct *self, PLItem *field_dict, Array *field_defs,
|
|
PLItem *only, int need_parser)
|
|
{
|
|
qfot_type_t *type = self.type;
|
|
int have_sType = 0;
|
|
int have_pNext = 0;
|
|
int readonly = [field_dict string] == "readonly";
|
|
|
|
for (int i = 0; i < type.strct.num_fields; i++) {
|
|
qfot_var_t *field = &type.strct.fields[i];
|
|
if (field.name == "sType") {
|
|
have_sType = 1;
|
|
}
|
|
if (field.name == "pNext") {
|
|
have_pNext = 1;
|
|
self.write_symtab = 1;
|
|
}
|
|
}
|
|
|
|
for (int i = [field_defs count]; i-- > 0; ) {
|
|
FieldDef *field_def = [field_defs objectAtIndex:i];
|
|
[field_def writeParseData];
|
|
}
|
|
[self.parse_def writeParseData];
|
|
|
|
if (!readonly) {
|
|
fprintf (output_file, "static plfield_t %s_fields[] = {\n",
|
|
[self outname]);
|
|
if (!only) {
|
|
fprintf (output_file,
|
|
"\t{\"@inherit\", 0, QFString, parse_inherit, "
|
|
"&%s_fields},\n", [self outname]);
|
|
}
|
|
if (have_pNext) {
|
|
fprintf (output_file,
|
|
"\t{\"@next\", field_offset (%s, pNext), "
|
|
"QFArray, parse_next, 0},\n", [self outname]);
|
|
}
|
|
for (int i = [field_defs count]; i-- > 0; ) {
|
|
FieldDef *field_def = [field_defs objectAtIndex:i];
|
|
[field_def writeField];
|
|
}
|
|
fprintf (output_file, "\t{ }\n");
|
|
fprintf (output_file, "};\n");
|
|
|
|
if (need_parser) {
|
|
write_parser (self, have_sType, only);
|
|
}
|
|
if (have_pNext) {
|
|
fprintf (output_file, "static parserref_t %s_parser = ",
|
|
[self outname]);
|
|
fprintf (output_file, "{\"%s\", %s, sizeof(%s)};\n",
|
|
[self outname], [self parseFunc], [self outname]);
|
|
}
|
|
}
|
|
}
|
|
|
|
-(void) initParse:(PLItem *)parse
|
|
{
|
|
if ([parse string] == "skip") {
|
|
skip = 1;
|
|
return;
|
|
}
|
|
|
|
field_dict = [parse retain];
|
|
|
|
PLItem *new_name = [field_dict getObjectForKey:".name"];
|
|
if (new_name) {
|
|
outname = str_hold ([new_name string]);
|
|
}
|
|
|
|
field_defs = [[Array array] retain];
|
|
|
|
only = [[field_dict getObjectForKey:".only"] retain];
|
|
if (only) {
|
|
string field_name = [only string];
|
|
qfot_var_t *field = nil;
|
|
for (int i = 0; i < type.strct.num_fields; i++) {
|
|
qfot_var_t *f = &type.strct.fields[i];
|
|
if (f.name == field_name) {
|
|
field = f;
|
|
break;
|
|
}
|
|
}
|
|
Type *field_type = [Type findType: field.type];
|
|
FieldDef *field_def = [field_type fielddef:self field:field.name];
|
|
if (!field_def) {
|
|
field_def = [FieldDef fielddef:nil
|
|
struct:self
|
|
field:field.name];
|
|
}
|
|
[field_defs addObject: field_def];
|
|
} else if (field_dict) {
|
|
PLItem *field_keys = [field_dict allKeys];
|
|
|
|
for (int i = [field_keys count]; i-- > 0; ) {
|
|
string field_name = [[field_keys getObjectAtIndex:i] string];
|
|
|
|
if (str_mid(field_name, 0, 1) == ".") {
|
|
continue;
|
|
}
|
|
PLItem *field_item = [field_dict getObjectForKey:field_name];
|
|
FieldDef *field_def = [FieldDef fielddef:field_item
|
|
struct:self
|
|
field:field_name];
|
|
[field_defs addObject: field_def];
|
|
}
|
|
} else {
|
|
for (int i = 0; i < type.strct.num_fields; i++) {
|
|
qfot_var_t *field = &type.strct.fields[i];
|
|
if (field.name == "sType" || field.name == "pNext") {
|
|
continue;
|
|
}
|
|
Type *field_type = [Type findType: field.type];
|
|
FieldDef *field_def = [field_type fielddef:self field:field.name];
|
|
if (!field_def) {
|
|
field_def = [FieldDef fielddef:nil
|
|
struct:self
|
|
field:field.name];
|
|
}
|
|
[field_defs addObject: field_def];
|
|
}
|
|
}
|
|
}
|
|
|
|
-(void) writeTable
|
|
{
|
|
if (skip) {
|
|
return;
|
|
}
|
|
|
|
if ([field_dict getObjectForKey:".type"]) {
|
|
PLItem *type = [field_dict getObjectForKey:".type"];
|
|
string str = [type string];
|
|
int need_table = 0;
|
|
|
|
if (str) {
|
|
need_table |= check_need_table (self, field_dict, str);
|
|
} else {
|
|
for (int i = [type count]; i-- > 0; ) {
|
|
string str = [[type getObjectAtIndex:i] string];
|
|
need_table |= check_need_table (self, field_dict, str);
|
|
}
|
|
}
|
|
if (need_table) {
|
|
write_table (self, field_dict, field_defs, only, 0);
|
|
}
|
|
write_function_head (self);
|
|
fprintf (output_file, "\tpltype_t type = PL_Type (item);\n");
|
|
if (str) {
|
|
write_type (self, field_dict, str);
|
|
} else {
|
|
for (int i = [type count]; i-- > 0; ) {
|
|
str = [[type getObjectAtIndex:i] string];
|
|
write_type (self, field_dict, str);
|
|
}
|
|
}
|
|
fprintf (output_file,
|
|
"\tPL_TypeMismatch (messages, item, field->name, %s, type);\n",
|
|
parseItemType (type));
|
|
fprintf (output_file, "\treturn 0;\n");
|
|
write_function_tail (self);
|
|
write_cexpr (self, field_defs);
|
|
return;
|
|
}
|
|
|
|
write_table (self, field_dict, field_defs, only, 1);
|
|
|
|
write_cexpr (self, field_defs);
|
|
}
|
|
|
|
-(void) writeSymtabInit
|
|
{
|
|
PLItem *field_dict = [parse getObjectForKey:[self outname]];
|
|
|
|
if ([parse string] == "skip") {
|
|
return;
|
|
}
|
|
|
|
fprintf (output_file, "\tqfMessageL (\"%s_symtab\");\n", [self outname]);
|
|
fprintf (output_file, "\tcexpr_init_symtab (&%s_symtab, context);\n",
|
|
[self outname]);
|
|
}
|
|
|
|
-(void) writeSymtabShutdown
|
|
{
|
|
PLItem *field_dict = [parse getObjectForKey:[self outname]];
|
|
|
|
if ([parse string] == "skip") {
|
|
return;
|
|
}
|
|
|
|
fprintf (output_file, "\tHash_DelTable (%s_symtab.tab);\n",
|
|
[self outname]);
|
|
fprintf (output_file, "\t%s_symtab.tab = nullptr;\n", [self outname]);
|
|
}
|
|
|
|
-(void) writeSymtabEntry
|
|
{
|
|
if (!write_symtab || [parse string] == "skip") {
|
|
return;
|
|
}
|
|
fprintf (output_file,
|
|
"\tHash_Add (parser_table, &%s_parser);\n",
|
|
[self outname]);
|
|
}
|
|
|
|
-(string) outname
|
|
{
|
|
if (outname) {
|
|
return outname;
|
|
}
|
|
if (alias) {
|
|
return [alias name];
|
|
}
|
|
return [self name];
|
|
}
|
|
|
|
-(string) cexprType
|
|
{
|
|
return [self outname] + "_type";
|
|
}
|
|
|
|
-(string) parseType
|
|
{
|
|
return "QFMultiType | (1 << QFString) | (1 << QFDictionary)";
|
|
}
|
|
|
|
-(string) parseFunc
|
|
{
|
|
return "parse_" + [self outname];
|
|
}
|
|
|
|
-(string) parseData
|
|
{
|
|
return "0";
|
|
}
|
|
@end
|