mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Refactored structs.
This commit is contained in:
@@ -47,7 +47,7 @@ void decl_set_external_name(Decl *decl)
|
||||
return;
|
||||
}
|
||||
char buffer[1024];
|
||||
uint32_t len = sprintf(buffer, "%s::%s", decl->module->name->module, decl->name);
|
||||
uint32_t len = sprintf(buffer, "%s.%s", decl->module->name->module, decl->name);
|
||||
assert(len);
|
||||
TokenType type = TOKEN_INVALID_TOKEN;
|
||||
decl->external_name = symtab_add(buffer, len, fnv1a(buffer, len), &type);
|
||||
@@ -89,6 +89,7 @@ Decl *decl_new_with_type(Token name, DeclKind decl_type, Visibility visibility)
|
||||
case DECL_CT_ELSE:
|
||||
case DECL_CT_ELIF:
|
||||
case DECL_ATTRIBUTE:
|
||||
case DECL_MEMBER:
|
||||
UNREACHABLE
|
||||
}
|
||||
Type *type = type_new(kind, name.string);
|
||||
@@ -110,8 +111,6 @@ const char *decl_var_to_string(VarDeclKind kind)
|
||||
return "local";
|
||||
case VARDECL_PARAM:
|
||||
return "param";
|
||||
case VARDECL_MEMBER:
|
||||
return "member";
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
@@ -137,7 +136,7 @@ Decl *struct_find_name(Decl *decl, const char* name)
|
||||
VECEACH(compare_members, i)
|
||||
{
|
||||
Decl *member = compare_members[i];
|
||||
if (!member->name)
|
||||
if (member->member_decl.anonymous)
|
||||
{
|
||||
Decl *found = struct_find_name(member, name);
|
||||
if (found) return found;
|
||||
@@ -337,7 +336,7 @@ void fprint_type_recursive(FILE *file, Type *type, int indent)
|
||||
return;
|
||||
case TYPE_MEMBER:
|
||||
DUMPF("(member %s", type->name);
|
||||
DUMPTYPE(type->decl->parent_struct->type);
|
||||
DUMPTYPE(type->decl->member_decl.parent->type);
|
||||
DUMPEND();
|
||||
case TYPE_TYPEDEF:
|
||||
DUMPF("(typedef %s", type->name);
|
||||
@@ -716,7 +715,7 @@ void fprint_decl_recursive(FILE *file, Decl *decl, int indent)
|
||||
switch (decl->decl_kind)
|
||||
{
|
||||
case DECL_VAR:
|
||||
DUMPF("(var-%s %s", decl_var_to_string(decl->var.kind), decl->name ?: "");
|
||||
DUMPF("(var-%s %s", decl_var_to_string(decl->var.kind), decl->name);
|
||||
DUMPTI(decl->var.type_info);
|
||||
switch (decl->var.kind)
|
||||
{
|
||||
@@ -726,8 +725,6 @@ void fprint_decl_recursive(FILE *file, Decl *decl, int indent)
|
||||
case VARDECL_PARAM:
|
||||
DUMPEXPR(decl->var.init_expr);
|
||||
break;
|
||||
case VARDECL_MEMBER:
|
||||
break;
|
||||
}
|
||||
DUMPEND();
|
||||
case DECL_MACRO:
|
||||
@@ -837,6 +834,10 @@ void fprint_decl_recursive(FILE *file, Decl *decl, int indent)
|
||||
DUMPF("(import %s", decl->name);
|
||||
// TODO
|
||||
DUMPEND();
|
||||
case DECL_MEMBER:
|
||||
DUMPF("(member %s", decl->name);
|
||||
DUMPTI(decl->member_decl.type_info);
|
||||
DUMPEND();
|
||||
case DECL_ATTRIBUTE:
|
||||
DUMPF("(attribute %s)", decl->name);
|
||||
if (decl->attr.domains & ATTR_FUNC)
|
||||
|
||||
@@ -248,7 +248,6 @@ typedef struct
|
||||
typedef struct
|
||||
{
|
||||
uint32_t abi_alignment;
|
||||
uint32_t id;
|
||||
uint64_t size;
|
||||
Decl **members;
|
||||
} StructDecl;
|
||||
@@ -259,11 +258,7 @@ typedef struct _VarDecl
|
||||
unsigned id : 16;
|
||||
VarDeclKind kind : 3;
|
||||
TypeInfo *type_info;
|
||||
union
|
||||
{
|
||||
Expr *init_expr;
|
||||
Decl *parent;
|
||||
};
|
||||
Expr *init_expr;
|
||||
void *backend_debug_ref;
|
||||
} VarDecl;
|
||||
|
||||
@@ -379,6 +374,14 @@ typedef struct
|
||||
Path *path; // For redefinition
|
||||
} GenericDecl;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned index : 32;
|
||||
bool anonymous : 1;
|
||||
Decl *parent;
|
||||
TypeInfo *type_info;
|
||||
Type *reference_type;
|
||||
} MemberDecl;
|
||||
|
||||
typedef struct _Decl
|
||||
{
|
||||
@@ -414,7 +417,6 @@ typedef struct _Decl
|
||||
{
|
||||
union
|
||||
{
|
||||
Decl* parent_struct;
|
||||
Decl** methods;
|
||||
};
|
||||
union
|
||||
@@ -438,6 +440,7 @@ typedef struct _Decl
|
||||
Decl** ct_else_decl;
|
||||
Expr *incr_array_decl;
|
||||
TypeInfo *throws;
|
||||
MemberDecl member_decl;
|
||||
};
|
||||
} Decl;
|
||||
|
||||
|
||||
@@ -131,6 +131,7 @@ void context_register_global_decl(Context *context, Decl *decl)
|
||||
case DECL_CT_ELSE:
|
||||
case DECL_CT_ELIF:
|
||||
case DECL_ATTRIBUTE:
|
||||
case DECL_MEMBER:
|
||||
UNREACHABLE
|
||||
break;
|
||||
case DECL_CT_IF:
|
||||
|
||||
@@ -184,6 +184,7 @@ typedef enum
|
||||
DECL_ENUM_CONSTANT,
|
||||
DECL_TYPEDEF,
|
||||
DECL_STRUCT,
|
||||
DECL_MEMBER,
|
||||
DECL_UNION,
|
||||
DECL_ENUM,
|
||||
DECL_ERROR,
|
||||
@@ -545,7 +546,6 @@ typedef enum
|
||||
VARDECL_GLOBAL = 1,
|
||||
VARDECL_LOCAL = 2,
|
||||
VARDECL_PARAM = 3,
|
||||
VARDECL_MEMBER = 4,
|
||||
} VarDeclKind;
|
||||
|
||||
typedef enum
|
||||
|
||||
@@ -343,6 +343,7 @@ static void gencontext_emit_decl(GenContext *context, Decl *decl)
|
||||
case DECL_CT_ELSE:
|
||||
case DECL_CT_ELIF:
|
||||
case DECL_ATTRIBUTE:
|
||||
case DECL_MEMBER:
|
||||
UNREACHABLE
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ static inline LLVMMetadataRef gencontext_create_debug_type_from_decl(GenContext
|
||||
case DECL_ERROR_CONSTANT:
|
||||
case DECL_ARRAY_VALUE:
|
||||
case DECL_IMPORT:
|
||||
case DECL_MEMBER:
|
||||
UNREACHABLE;
|
||||
case DECL_FUNC:
|
||||
{
|
||||
|
||||
@@ -123,30 +123,18 @@ static inline LLVMValueRef gencontext_emit_subscript_addr(GenContext *context, E
|
||||
|
||||
static LLVMValueRef gencontext_emit_member_addr(GenContext *context, LLVMValueRef value, Decl *parent, Decl *member)
|
||||
{
|
||||
unsigned index;
|
||||
Decl *current_parent;
|
||||
assert(member->resolve_status == RESOLVE_DONE);
|
||||
if (decl_is_struct_type(member))
|
||||
{
|
||||
index = member->strukt.id;
|
||||
current_parent = member->parent_struct;
|
||||
}
|
||||
else
|
||||
{
|
||||
index = member->var.id;
|
||||
current_parent = member->var.parent;
|
||||
}
|
||||
assert(current_parent);
|
||||
if (parent != current_parent)
|
||||
Decl *current_parent = member->member_decl.parent;
|
||||
if (current_parent->decl_kind == DECL_MEMBER && current_parent->member_decl.anonymous)
|
||||
{
|
||||
value = gencontext_emit_member_addr(context, value, parent, current_parent);
|
||||
}
|
||||
|
||||
if (current_parent->decl_kind == DECL_UNION)
|
||||
if (current_parent->type->canonical->type_kind == TYPE_UNION)
|
||||
{
|
||||
return LLVMBuildBitCast(context->builder, value, LLVMPointerType(llvm_type(member->type), 0), member->name ?: "anon");
|
||||
return LLVMBuildBitCast(context->builder, value, LLVMPointerType(llvm_type(member->type), 0), member->name);
|
||||
}
|
||||
return LLVMBuildStructGEP2(context->builder, llvm_type(current_parent->type), value, index, member->name ?: "anon");
|
||||
return LLVMBuildStructGEP2(context->builder, llvm_type(current_parent->type), value, member->member_decl.index, member->name);
|
||||
}
|
||||
|
||||
|
||||
@@ -384,11 +372,11 @@ static inline LLVMValueRef gencontext_emit_initializer_list_expr_addr(GenContext
|
||||
case DESIGNATED_IDENT:
|
||||
if (parent_type->canonical->type_kind == TYPE_UNION)
|
||||
{
|
||||
sub_ref = LLVMBuildBitCast(context->builder, sub_ref, LLVMPointerType(llvm_type(path->type), 0), "unionref");
|
||||
sub_ref = LLVMBuildBitCast(context->builder, sub_ref, LLVMPointerType(llvm_type(path->type), 0), path->type->name);
|
||||
}
|
||||
else
|
||||
{
|
||||
sub_ref = LLVMBuildStructGEP2(context->builder, llvm_type(parent_type), sub_ref, path->index, "structref");
|
||||
sub_ref = LLVMBuildStructGEP2(context->builder, llvm_type(parent_type), sub_ref, path->index, path->type->name);
|
||||
}
|
||||
break;
|
||||
case DESIGNATED_SUBSCRIPT:
|
||||
|
||||
@@ -300,6 +300,7 @@ void gencontext_emit_extern_decl(GenContext *context, Decl *decl)
|
||||
TODO
|
||||
case DECL_ERROR_CONSTANT:
|
||||
TODO
|
||||
case DECL_MEMBER:
|
||||
case DECL_ARRAY_VALUE:
|
||||
case DECL_IMPORT:
|
||||
case DECL_MACRO:
|
||||
|
||||
@@ -23,6 +23,7 @@ static inline LLVMTypeRef llvm_type_from_decl(LLVMContextRef context, Decl *decl
|
||||
case DECL_ERROR_CONSTANT:
|
||||
case DECL_ARRAY_VALUE:
|
||||
case DECL_IMPORT:
|
||||
case DECL_MEMBER:
|
||||
UNREACHABLE;
|
||||
case DECL_FUNC:
|
||||
{
|
||||
@@ -46,7 +47,7 @@ static inline LLVMTypeRef llvm_type_from_decl(LLVMContextRef context, Decl *decl
|
||||
{
|
||||
vec_add(types, llvm_get_type(context, decl->strukt.members[i]->type));
|
||||
}
|
||||
LLVMTypeRef type = LLVMStructCreateNamed(context, decl->external_name);
|
||||
LLVMTypeRef type = LLVMStructCreateNamed(context, decl->name);
|
||||
LLVMStructSetBody(type, types, vec_size(types), decl->is_packed);
|
||||
return type;
|
||||
}
|
||||
|
||||
@@ -955,6 +955,17 @@ static inline bool parse_opt_parameter_type_list(Context *context, Visibility pa
|
||||
|
||||
#pragma mark --- Parse types
|
||||
|
||||
void add_struct_member(Decl *parent, Decl *parent_struct, Decl *member, TypeInfo *type)
|
||||
{
|
||||
unsigned index = vec_size(parent_struct->strukt.members);
|
||||
vec_add(parent_struct->strukt.members, member);
|
||||
member->member_decl.index = index;
|
||||
member->member_decl.reference_type = type_new(TYPE_MEMBER, member->name);
|
||||
member->member_decl.reference_type->decl = member;
|
||||
member->member_decl.type_info = type;
|
||||
member->member_decl.parent = parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expect pointer to after '{'
|
||||
*
|
||||
@@ -973,14 +984,16 @@ static inline bool parse_opt_parameter_type_list(Context *context, Visibility pa
|
||||
* | struct_or_union opt_attributes struct_body
|
||||
* ;
|
||||
*
|
||||
* @param parent the direct parent.
|
||||
* @param visible_parent the visible parent when checking duplicate symbols.
|
||||
* @param parent the parent if this is the body of member
|
||||
* @param struct_parent the struct this is the body of
|
||||
* @param visible_parent the visible struct parent for checking duplicates.
|
||||
*/
|
||||
bool parse_struct_body(Context *context, Decl *parent, Decl *visible_parent)
|
||||
bool parse_struct_body(Context *context, Decl *parent, Decl *parent_struct, Decl *visible_parent)
|
||||
{
|
||||
|
||||
CONSUME_OR(TOKEN_LBRACE, false);
|
||||
|
||||
assert(decl_is_struct_type(parent_struct));
|
||||
while (context->tok.type != TOKEN_RBRACE)
|
||||
{
|
||||
TokenType token_type = context->tok.type;
|
||||
@@ -988,17 +1001,19 @@ bool parse_struct_body(Context *context, Decl *parent, Decl *visible_parent)
|
||||
{
|
||||
DeclKind decl_kind = decl_from_token(token_type);
|
||||
Decl *member;
|
||||
Token name_replacement = context->tok;
|
||||
name_replacement.string = "anon";
|
||||
Decl *strukt_type = decl_new_with_type(name_replacement, decl_kind, visible_parent->visibility);
|
||||
if (context->next_tok.type != TOKEN_IDENT)
|
||||
{
|
||||
Token name_replacement = context->tok;
|
||||
name_replacement.string = NULL;
|
||||
member = decl_new_with_type(name_replacement, decl_kind, parent->visibility);
|
||||
member = decl_new(DECL_MEMBER, name_replacement, visible_parent->visibility);
|
||||
member->member_decl.anonymous = true;
|
||||
advance(context);
|
||||
}
|
||||
else
|
||||
{
|
||||
advance(context);
|
||||
member = decl_new_with_type(context->tok, decl_kind, parent->visibility);
|
||||
member = decl_new(DECL_MEMBER, context->tok, visible_parent->visibility);
|
||||
Decl *other = struct_find_name(visible_parent, context->tok.string);
|
||||
if (other)
|
||||
{
|
||||
@@ -1007,26 +1022,25 @@ bool parse_struct_body(Context *context, Decl *parent, Decl *visible_parent)
|
||||
decl_poison(visible_parent);
|
||||
decl_poison(other);
|
||||
decl_poison(member);
|
||||
return false;
|
||||
}
|
||||
advance_and_verify(context, TOKEN_IDENT);
|
||||
}
|
||||
if (!parse_attributes(context, member)) return false;
|
||||
member->parent_struct = parent;
|
||||
member->strukt.id = vec_size(parent->strukt.members);
|
||||
parent->strukt.members = VECADD(parent->strukt.members, member);
|
||||
if (!parse_struct_body(context, member, context->tok.type == TOKEN_IDENT ? member : visible_parent))
|
||||
if (!parse_attributes(context, strukt_type)) return false;
|
||||
if (!parse_struct_body(context, member, strukt_type, context->tok.type == TOKEN_IDENT ? strukt_type : visible_parent))
|
||||
{
|
||||
decl_poison(visible_parent);
|
||||
return false;
|
||||
}
|
||||
VECADD(context->types, strukt_type);
|
||||
add_struct_member(parent, parent_struct, member, type_info_new_base(strukt_type->type, strukt_type->span));
|
||||
continue;
|
||||
}
|
||||
TypeInfo *type = TRY_TYPE_OR(parse_type(context), false);
|
||||
|
||||
while (1)
|
||||
{
|
||||
EXPECT_OR(TOKEN_IDENT, false);
|
||||
Decl *member = decl_new_var(context->tok, type, VARDECL_MEMBER, parent->visibility);
|
||||
Decl *member = decl_new(DECL_MEMBER, context->tok, visible_parent->visibility);
|
||||
Decl *other = struct_find_name(visible_parent, member->name);
|
||||
if (other)
|
||||
{
|
||||
@@ -1036,10 +1050,7 @@ bool parse_struct_body(Context *context, Decl *parent, Decl *visible_parent)
|
||||
decl_poison(other);
|
||||
decl_poison(member);
|
||||
}
|
||||
unsigned index = vec_size(parent->strukt.members);
|
||||
parent->strukt.members = VECADD(parent->strukt.members, member);
|
||||
member->var.id = index;
|
||||
member->var.parent = parent;
|
||||
add_struct_member(parent, parent_struct, member, type);
|
||||
advance(context);
|
||||
if (context->tok.type != TOKEN_COMMA) break;
|
||||
}
|
||||
@@ -1074,7 +1085,7 @@ static inline Decl *parse_struct_declaration(Context *context, Visibility visibi
|
||||
return poisoned_decl;
|
||||
}
|
||||
|
||||
if (!parse_struct_body(context, decl, decl))
|
||||
if (!parse_struct_body(context, decl, decl, decl))
|
||||
{
|
||||
return poisoned_decl;
|
||||
}
|
||||
|
||||
@@ -281,6 +281,7 @@ bool fpfp(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_typ
|
||||
*/
|
||||
bool fpxi(Expr *left, Type *canonical, Type *type, CastType cast_type)
|
||||
{
|
||||
if (cast_type == CAST_TYPE_OPTIONAL_IMPLICIT) return true;
|
||||
if (cast_type != CAST_TYPE_EXPLICIT) EXIT_T_MISMATCH();
|
||||
RETURN_NON_CONST_CAST(CAST_FPUI);
|
||||
|
||||
|
||||
@@ -90,48 +90,9 @@ static inline bool sema_analyse_struct_member(Context *context, Decl *decl)
|
||||
{
|
||||
assert(decl->resolve_status == RESOLVE_NOT_DONE);
|
||||
decl->resolve_status = RESOLVE_RUNNING;
|
||||
if (decl->decl_kind == DECL_STRUCT || decl->decl_kind == DECL_UNION)
|
||||
{
|
||||
DEBUG_LOG("Beginning analysis of inner struct/union");
|
||||
VECEACH(decl->strukt.members, i)
|
||||
{
|
||||
Decl *member = decl->strukt.members[i];
|
||||
if (!decl_ok(member))
|
||||
{
|
||||
decl_poison(decl);
|
||||
continue;
|
||||
}
|
||||
if (!sema_analyse_struct_member(context, decl->strukt.members[i]))
|
||||
{
|
||||
if (decl_ok(decl))
|
||||
{
|
||||
decl_poison(decl);
|
||||
continue;
|
||||
}
|
||||
decl_poison(decl);
|
||||
}
|
||||
}
|
||||
if (decl->decl_kind == DECL_UNION)
|
||||
{
|
||||
sema_set_union_size(decl);
|
||||
}
|
||||
else
|
||||
{
|
||||
sema_set_struct_size(decl);
|
||||
}
|
||||
DEBUG_LOG("Analysis complete.");
|
||||
decl->resolve_status = RESOLVE_DONE;
|
||||
return decl_ok(decl);
|
||||
}
|
||||
assert(decl->decl_kind == DECL_VAR);
|
||||
assert(decl->var.kind == VARDECL_MEMBER);
|
||||
if (!sema_resolve_type_info(context, decl->var.type_info))
|
||||
{
|
||||
decl_poison(decl);
|
||||
return false;
|
||||
}
|
||||
decl->type = decl->var.type_info->type;
|
||||
assert(decl->var.type_info->type);
|
||||
assert(decl->decl_kind == DECL_MEMBER);
|
||||
if (!sema_resolve_type_info(context, decl->member_decl.type_info)) return decl_poison(decl);
|
||||
decl->type = decl->member_decl.type_info->type;
|
||||
decl->resolve_status = RESOLVE_DONE;
|
||||
return true;
|
||||
}
|
||||
@@ -767,12 +728,12 @@ bool sema_analyse_decl(Context *context, Decl *decl)
|
||||
case DECL_ARRAY_VALUE:
|
||||
case DECL_CT_ELSE:
|
||||
case DECL_CT_ELIF:
|
||||
case DECL_MEMBER:
|
||||
UNREACHABLE
|
||||
case DECL_CT_IF:
|
||||
// Handled elsewhere
|
||||
UNREACHABLE
|
||||
}
|
||||
decl->resolve_status = RESOLVE_DONE;
|
||||
DEBUG_LOG("<<< Analysis of %s successful.", decl->name);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -672,8 +672,7 @@ static Decl *strukt_recursive_search_member(Decl *strukt, const char *name)
|
||||
VECEACH(strukt->strukt.members, i)
|
||||
{
|
||||
Decl *member = strukt->strukt.members[i];
|
||||
if (member->name == name) return member;
|
||||
if (!member->name && type_is_structlike(member->type->canonical))
|
||||
if (member->member_decl.anonymous)
|
||||
{
|
||||
Decl *result = strukt_recursive_search_member(member->type->canonical->decl, name);
|
||||
if (result)
|
||||
@@ -681,6 +680,10 @@ static Decl *strukt_recursive_search_member(Decl *strukt, const char *name)
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (member->name == name) return member;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@@ -852,17 +855,7 @@ static DesignatedPath *sema_analyse_init_identifier_string(Context *context, Des
|
||||
VECEACH(members, i)
|
||||
{
|
||||
Decl *member = members[i];
|
||||
if (member->name == string)
|
||||
{
|
||||
DesignatedPath *sub_path = CALLOCS(DesignatedPath);
|
||||
sub_path->type = member->type;
|
||||
sub_path->kind = DESIGNATED_IDENT;
|
||||
sub_path->index = i;
|
||||
parent_path->sub_path = sub_path;
|
||||
*has_found_match = true;
|
||||
return sub_path;
|
||||
}
|
||||
if (!member->name)
|
||||
if (member->member_decl.anonymous)
|
||||
{
|
||||
DesignatedPath temp_path;
|
||||
temp_path.type = member->type;
|
||||
@@ -876,6 +869,16 @@ static DesignatedPath *sema_analyse_init_identifier_string(Context *context, Des
|
||||
*has_found_match = true;
|
||||
return found;
|
||||
}
|
||||
if (member->name == string)
|
||||
{
|
||||
DesignatedPath *sub_path = CALLOCS(DesignatedPath);
|
||||
sub_path->type = member->type;
|
||||
sub_path->kind = DESIGNATED_IDENT;
|
||||
sub_path->index = i;
|
||||
parent_path->sub_path = sub_path;
|
||||
*has_found_match = true;
|
||||
return sub_path;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -136,6 +136,7 @@ static bool sema_resolve_type_identifier(Context *context, TypeInfo *type_info)
|
||||
case DECL_CT_IF:
|
||||
case DECL_CT_ELIF:
|
||||
case DECL_ATTRIBUTE:
|
||||
case DECL_MEMBER:
|
||||
UNREACHABLE
|
||||
}
|
||||
UNREACHABLE
|
||||
|
||||
@@ -176,6 +176,7 @@ static void type_append_signature_name_user_defined(Decl *decl, char *dst, size_
|
||||
case DECL_CT_ELSE:
|
||||
case DECL_CT_ELIF:
|
||||
case DECL_ATTRIBUTE:
|
||||
case DECL_MEMBER:
|
||||
UNREACHABLE
|
||||
case DECL_STRUCT:
|
||||
case DECL_UNION:
|
||||
@@ -230,6 +231,7 @@ size_t type_size(Type *canonical)
|
||||
return alignment_error_code;
|
||||
case TYPE_STRUCT:
|
||||
case TYPE_UNION:
|
||||
assert(canonical->decl->resolve_status == RESOLVE_DONE);
|
||||
return canonical->decl->strukt.size;
|
||||
case TYPE_VOID:
|
||||
return 1;
|
||||
|
||||
Reference in New Issue
Block a user