feat(argparse): 支持列表类型参数解析
当参数指定为列表类型时,验证和处理逻辑现在会检查 vec_store 是否为空或大小为0,而不是检查 str_store。 fix(hir): 初始化聚合类型值的数据结构 在 HIR 值初始化过程中,为聚合类型添加适当的初始化逻辑, 确保其字段向量被正确初始化。 refactor(hir_builder): 优化指针类型创建和GEP操作实现 - 简化全局分配器中的指针类型创建代码 - 扩展 GEP 操作以支持结构体和联合体字段访问 - 添加字段偏移计算支持 feat(hir_dump): 增强HIR类型和值的线性转储功能 - 实现类型定义的线性转储输出 - 改进结构体和联合体的转储格式 - 优化聚合值的转储表示 perf(hir_layout): 优化类型布局计算性能 改进结构体、联合体和聚合类型的对齐和大小计算算法, 提高字段偏移计算的准确性。 fix(hir_module): 完善模块资源清理机制 在模块析构时正确释放结构体和联合体类型的字段向量内存。 docs(lir): 更新文档注释中的空值表示 将初始化数据参数的空值描述从 NULL 统一为 nullptr。 refactor(hir2lir): 改进HIR到LIR的类型转换逻辑 - 重构类型到LIR大小扩展的转换函数 - 修复STORE指令的类型推导逻辑 - 增强GEP指令的规模因子和偏移量计算 - 添加对结构体/联合体字段访问的支持 refactor(x86_isel): 优化x86-64地址加载指令生成 改进LOAD_ADDR指令生成,更好地支持结构体字段访问的零比例因子。 docs(ir2mcode): 统一空值表示文档注释 更新初始化数据参数的空值描述为nullptr。 refactor(lexer): 简化词法分析器非空白标记预览逻辑 优化非空白标记预览函数,减少不必要的标记消费和销毁操作。
This commit is contained in:
@@ -117,6 +117,9 @@ void scc_hir_value_init(scc_hir_value_t *in, const char *name,
|
||||
in->data.conv.operand = 0;
|
||||
in->data.conv.target_type = 0;
|
||||
break;
|
||||
case SCC_HIR_VALUE_TAG_AGGREGATE:
|
||||
scc_vec_init(in->data.aggregate.fields);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
|
||||
@@ -492,10 +492,9 @@ scc_hir_value_ref_t scc_hir_builder_global_alloca(scc_hir_builder_t *builder,
|
||||
scc_hir_type_ref_t type,
|
||||
scc_hir_value_ref_t value) {
|
||||
SCC_HIR_BUILDER_CHECK_NO_BORROW(builder);
|
||||
// 全局变量的实际类型是指向 type 的指针
|
||||
scc_hir_type_t ptr_type = {.tag = SCC_HIR_TYPE_PTR,
|
||||
.data.pointer.base = type};
|
||||
scc_hir_type_ref_t ptr_type_ref = scc_hir_builder_type(builder, &ptr_type);
|
||||
scc_hir_type_ref_t ptr_type_ref = scc_hir_builder_type(
|
||||
builder,
|
||||
&(scc_hir_type_t){.tag = SCC_HIR_TYPE_PTR, .data.pointer.base = type});
|
||||
|
||||
// FIXME
|
||||
char *name = scc_malloc(32);
|
||||
@@ -612,28 +611,52 @@ scc_hir_value_ref_t scc_hir_builder_get_elem_ptr(scc_hir_builder_t *builder,
|
||||
builder, type_ref,
|
||||
scc_hir_module_get_type_by_value(GET_MODULE(builder), target));
|
||||
Assert(type_ref != nullptr);
|
||||
scc_hir_type_t type = *type_ref; // 拷贝一份,避免后续借用
|
||||
scc_hir_type_t val_type = *type_ref; // 拷贝一份,避免后续借用
|
||||
SCC_HIR_BUILDER_END_BORROW(builder); // type_ref
|
||||
|
||||
if (type.tag == SCC_HIR_TYPE_PTR) {
|
||||
if (val_type.tag == SCC_HIR_TYPE_PTR) {
|
||||
scc_hir_type_ref_t base_ref = val_type.data.pointer.base;
|
||||
scc_hir_type_t *base_type = nullptr;
|
||||
SCC_HIR_BUILDER_BEGIN_BORROW(
|
||||
builder, base_type,
|
||||
scc_hir_module_get_type(GET_MODULE(builder),
|
||||
type_ref->data.pointer.base));
|
||||
if (base_type->tag == SCC_HIR_TYPE_ARRAY) {
|
||||
scc_hir_type_t type = (scc_hir_type_t){
|
||||
.tag = SCC_HIR_TYPE_PTR,
|
||||
.data.pointer.base = base_type->data.array.base,
|
||||
};
|
||||
SCC_HIR_BUILDER_END_BORROW(builder); // base_type
|
||||
get_ptr_node.type = scc_hir_builder_type(builder, &type);
|
||||
scc_hir_module_get_type(GET_MODULE(builder), base_ref));
|
||||
Assert(base_type != nullptr);
|
||||
scc_hir_type_tag_t base_tag = base_type->tag;
|
||||
|
||||
if (base_tag == SCC_HIR_TYPE_ARRAY) {
|
||||
// 数组 GEP:返回指向元素类型的指针
|
||||
scc_hir_type_ref_t elem_ref = base_type->data.array.base;
|
||||
SCC_HIR_BUILDER_END_BORROW(builder);
|
||||
get_ptr_node.type =
|
||||
scc_hir_builder_type(builder, &(scc_hir_type_t){
|
||||
.tag = SCC_HIR_TYPE_PTR,
|
||||
.data.pointer.base = elem_ref,
|
||||
});
|
||||
} else if (base_tag == SCC_HIR_TYPE_STRUCT ||
|
||||
base_tag == SCC_HIR_TYPE_UNION) {
|
||||
// 结构体/联合体 GEP:index 是字段编号,返回指向字段类型的指针
|
||||
usize field_idx = 0;
|
||||
scc_hir_value_t *idx_val =
|
||||
scc_hir_module_get_value(GET_MODULE(builder), index);
|
||||
if (idx_val && idx_val->tag == SCC_HIR_VALUE_TAG_INTEGER) {
|
||||
field_idx = (usize)idx_val->data.integer.data.digit;
|
||||
}
|
||||
Assert(field_idx < scc_vec_size(base_type->data.aggregate.fields));
|
||||
scc_hir_type_ref_t field_ref =
|
||||
scc_vec_at(base_type->data.aggregate.fields, field_idx);
|
||||
SCC_HIR_BUILDER_END_BORROW(builder);
|
||||
get_ptr_node.type = scc_hir_builder_type(
|
||||
builder, &(scc_hir_type_t){
|
||||
.tag = SCC_HIR_TYPE_PTR,
|
||||
.data.pointer.base = field_ref,
|
||||
});
|
||||
} else {
|
||||
SCC_HIR_BUILDER_END_BORROW(builder); // base_type
|
||||
get_ptr_node.type = scc_hir_builder_type(builder, &type);
|
||||
// 其他类型:保持原指针类型
|
||||
SCC_HIR_BUILDER_END_BORROW(builder);
|
||||
get_ptr_node.type = scc_hir_builder_type(builder, &val_type);
|
||||
}
|
||||
} else {
|
||||
get_ptr_node.type = scc_hir_builder_type(builder, &type);
|
||||
get_ptr_node.type = scc_hir_builder_type(builder, &val_type);
|
||||
}
|
||||
|
||||
scc_hir_value_ref_t value_ref =
|
||||
|
||||
@@ -79,17 +79,16 @@ static const char *get_type_tag_str(scc_hir_type_tag_t tag) {
|
||||
|
||||
static void dump_type_linear_with_visited(scc_hir_dump_t *ctx,
|
||||
scc_hir_type_ref_t type_ref,
|
||||
scc_hashtable_t *visited) {
|
||||
scc_hashtable_t *visited,
|
||||
cbool extened) {
|
||||
if (!ctx || !type_ref) {
|
||||
LOG_ERROR("invalid parameter");
|
||||
return;
|
||||
}
|
||||
// 检查循环
|
||||
if (scc_hashtable_get(visited, (void *)(usize)type_ref)) {
|
||||
scc_tree_dump_append(ctx->dump_ctx, " <recursive>");
|
||||
return;
|
||||
extened = false;
|
||||
}
|
||||
scc_hashtable_set(visited, (void *)(usize)type_ref, (void *)1);
|
||||
|
||||
scc_hir_type_t *type = scc_hir_module_get_type(GET_MODULE(ctx), type_ref);
|
||||
if (!type) {
|
||||
@@ -117,12 +116,14 @@ static void dump_type_linear_with_visited(scc_hir_dump_t *ctx,
|
||||
break;
|
||||
case SCC_HIR_TYPE_ARRAY:
|
||||
scc_tree_dump_append(ctx->dump_ctx, "[");
|
||||
dump_type_linear_with_visited(ctx, type->data.array.base, visited);
|
||||
dump_type_linear_with_visited(ctx, type->data.array.base, visited,
|
||||
extened);
|
||||
scc_tree_dump_append_fmt(ctx->dump_ctx, ", %zu]", type->data.array.len);
|
||||
break;
|
||||
case SCC_HIR_TYPE_PTR:
|
||||
scc_tree_dump_append(ctx->dump_ctx, "*");
|
||||
dump_type_linear_with_visited(ctx, type->data.pointer.base, visited);
|
||||
dump_type_linear_with_visited(ctx, type->data.pointer.base, visited,
|
||||
extened);
|
||||
break;
|
||||
case SCC_HIR_TYPE_FUNC:
|
||||
scc_tree_dump_append(ctx->dump_ctx, "(");
|
||||
@@ -130,25 +131,39 @@ static void dump_type_linear_with_visited(scc_hir_dump_t *ctx,
|
||||
if (i > 0)
|
||||
scc_tree_dump_append(ctx->dump_ctx, ", ");
|
||||
dump_type_linear_with_visited(
|
||||
ctx, scc_vec_at(type->data.function.params, i), visited);
|
||||
ctx, scc_vec_at(type->data.function.params, i), visited,
|
||||
extened);
|
||||
}
|
||||
if (type->data.function.ret_type) {
|
||||
scc_tree_dump_append(ctx->dump_ctx, ") -> ");
|
||||
dump_type_linear_with_visited(ctx, type->data.function.ret_type,
|
||||
visited);
|
||||
visited, extened);
|
||||
} else {
|
||||
scc_tree_dump_append(ctx->dump_ctx, ")");
|
||||
}
|
||||
break;
|
||||
case SCC_HIR_TYPE_STRUCT:
|
||||
case SCC_HIR_TYPE_UNION:
|
||||
scc_tree_dump_append_fmt(ctx->dump_ctx, "%s {",
|
||||
type->tag == SCC_HIR_TYPE_STRUCT ? "struct"
|
||||
: "union");
|
||||
for (usize i = 0; i < scc_vec_size(type->data.aggregate.fields); i++) {
|
||||
dump_type_linear_with_visited(
|
||||
ctx, scc_vec_at(type->data.aggregate.fields, i), visited);
|
||||
scc_tree_dump_append(ctx->dump_ctx, ";");
|
||||
scc_hashtable_set(visited, (void *)(usize)type_ref, (void *)1);
|
||||
const char *tag_name =
|
||||
type->tag == SCC_HIR_TYPE_STRUCT ? "struct" : "union";
|
||||
if (type->name) {
|
||||
scc_tree_dump_append_fmt(ctx->dump_ctx, "%s %s {", tag_name,
|
||||
type->name);
|
||||
} else {
|
||||
scc_tree_dump_append_fmt(ctx->dump_ctx, "%s T%u {", tag_name,
|
||||
type_ref);
|
||||
}
|
||||
if (extened) {
|
||||
for (usize i = 0; i < scc_vec_size(type->data.aggregate.fields);
|
||||
i++) {
|
||||
dump_type_linear_with_visited(
|
||||
ctx, scc_vec_at(type->data.aggregate.fields, i), visited,
|
||||
extened);
|
||||
scc_tree_dump_append(ctx->dump_ctx, ";");
|
||||
}
|
||||
} else {
|
||||
scc_tree_dump_append(ctx->dump_ctx, "...");
|
||||
}
|
||||
scc_tree_dump_append(ctx->dump_ctx, "}");
|
||||
break;
|
||||
@@ -162,9 +177,29 @@ void scc_hir_dump_type_linear(scc_hir_dump_t *ctx,
|
||||
scc_hir_type_ref_t type_ref) {
|
||||
scc_hashtable_t visited;
|
||||
scc_hashtable_usize_init(&visited);
|
||||
dump_type_linear_with_visited(ctx, type_ref, &visited);
|
||||
dump_type_linear_with_visited(ctx, type_ref, &visited, false);
|
||||
scc_hashtable_drop(&visited);
|
||||
}
|
||||
static void scc_hir_dump_types_linear(scc_hir_dump_t *ctx) {
|
||||
scc_hir_module_t *module = GET_MODULE(ctx);
|
||||
// 遍历所有类型(跳过索引0,保留给无效引用)
|
||||
for (usize i = 1; i < scc_vec_size(module->types); i++) {
|
||||
scc_hir_type_t *type = &module->types.data[i];
|
||||
if (type->tag == SCC_HIR_TYPE_unknown)
|
||||
continue;
|
||||
|
||||
scc_hashtable_t visited;
|
||||
scc_hashtable_usize_init(&visited);
|
||||
|
||||
scc_tree_dump_begin_line(ctx->dump_ctx);
|
||||
scc_tree_dump_node(ctx->dump_ctx, "T%u", (unsigned)i);
|
||||
scc_tree_dump_append(ctx->dump_ctx, " = ");
|
||||
dump_type_linear_with_visited(ctx, (scc_hir_type_ref_t)i, &visited,
|
||||
true);
|
||||
|
||||
scc_hashtable_drop(&visited);
|
||||
}
|
||||
}
|
||||
|
||||
static void format_ref_or_value(scc_hir_dump_t *ctx,
|
||||
scc_hir_value_ref_t value_ref) {
|
||||
@@ -249,12 +284,18 @@ void scc_hir_dump_value_linear(scc_hir_dump_t *ctx,
|
||||
// 值已经在 format 中输出,这里不需要再做
|
||||
break;
|
||||
case SCC_HIR_VALUE_TAG_AGGREGATE:
|
||||
// 聚合类型:递归输出每个元素(每个占一行)
|
||||
scc_vec_foreach(value->data.aggregate.fields, i) {
|
||||
scc_hir_dump_value_linear(
|
||||
ctx, scc_vec_at(value->data.aggregate.fields, i));
|
||||
scc_tree_dump_append(ctx->dump_ctx, "\n");
|
||||
scc_tree_dump_append(ctx->dump_ctx, "{");
|
||||
if (scc_vec_size(value->data.aggregate.fields) > 0) {
|
||||
scc_tree_dump_push(ctx->dump_ctx, true);
|
||||
scc_vec_foreach(value->data.aggregate.fields, i) {
|
||||
scc_tree_dump_begin_line(ctx->dump_ctx);
|
||||
scc_hir_dump_value_linear(
|
||||
ctx, scc_vec_at(value->data.aggregate.fields, i));
|
||||
}
|
||||
scc_tree_dump_pop(ctx->dump_ctx);
|
||||
scc_tree_dump_begin_line(ctx->dump_ctx);
|
||||
}
|
||||
scc_tree_dump_append(ctx->dump_ctx, "}");
|
||||
return;
|
||||
case SCC_HIR_VALUE_TAG_ALLOC:
|
||||
scc_tree_dump_append(ctx->dump_ctx, "alloc");
|
||||
@@ -325,8 +366,9 @@ void scc_hir_dump_value_linear(scc_hir_dump_t *ctx,
|
||||
value->data.arg_ref.idx);
|
||||
break;
|
||||
case SCC_HIR_VALUE_TAG_GLOBAL_ALLOC:
|
||||
scc_tree_dump_append_fmt(ctx->dump_ctx, "global %s", value->name);
|
||||
scc_tree_dump_append_fmt(ctx->dump_ctx, "global %s:", value->name);
|
||||
scc_tree_dump_begin_line(ctx->dump_ctx);
|
||||
scc_tree_dump_append(ctx->dump_ctx, " ");
|
||||
if (value->data.global_alloc.value) {
|
||||
scc_hir_dump_value_linear(ctx, value->data.global_alloc.value);
|
||||
} else {
|
||||
@@ -422,6 +464,7 @@ void scc_hir_dump_func_linear(scc_hir_dump_t *ctx, scc_hir_func_ref_t func_ref,
|
||||
}
|
||||
|
||||
void scc_hir_dump_cprog_linear(scc_hir_dump_t *ctx) {
|
||||
scc_hir_dump_types_linear(ctx);
|
||||
for (usize i = 0; i < scc_vec_size(ctx->cprog->global_vals); i++) {
|
||||
scc_tree_dump_begin_line(ctx->dump_ctx);
|
||||
scc_hir_dump_value_linear(ctx, scc_vec_at(ctx->cprog->global_vals, i));
|
||||
|
||||
@@ -45,7 +45,8 @@ int scc_hir_type_align(scc_hir_module_t *mod, scc_hir_type_ref_t ref,
|
||||
scc_hir_type_ref_t fr = scc_vec_at(type->data.aggregate.fields, i);
|
||||
int fa_bits = scc_hir_type_align(mod, fr, abi);
|
||||
int fs_bits = scc_hir_type_size(mod, fr, abi);
|
||||
int fa2_bits = scc_type_abi_get_field_align(abi, fs_bits / 8, fa_bits / 8) * 8;
|
||||
int fa2_bits =
|
||||
scc_type_abi_get_field_align(abi, fs_bits / 8, fa_bits / 8) * 8;
|
||||
if (fa2_bits > max_align_bits)
|
||||
max_align_bits = fa2_bits;
|
||||
}
|
||||
@@ -116,14 +117,16 @@ int scc_hir_type_size(scc_hir_module_t *mod, scc_hir_type_ref_t ref,
|
||||
scc_hir_type_ref_t fr = scc_vec_at(type->data.aggregate.fields, i);
|
||||
int fs_bits = scc_hir_type_size(mod, fr, abi);
|
||||
int fa_bits = scc_hir_type_align(mod, fr, abi);
|
||||
int fa2_bits = scc_type_abi_get_field_align(abi, fs_bits / 8, fa_bits / 8) * 8;
|
||||
int fa2_bits =
|
||||
scc_type_abi_get_field_align(abi, fs_bits / 8, fa_bits / 8) * 8;
|
||||
offset_bits = SCC_ALIGN_UP(offset_bits, fa2_bits);
|
||||
offset_bits += fs_bits;
|
||||
if (fa2_bits > max_align_bits)
|
||||
max_align_bits = fa2_bits;
|
||||
}
|
||||
return SCC_ALIGN_UP(offset_bits,
|
||||
scc_type_abi_get_aggregate_align(abi, max_align_bits / 8) * 8);
|
||||
return SCC_ALIGN_UP(
|
||||
offset_bits,
|
||||
scc_type_abi_get_aggregate_align(abi, max_align_bits / 8) * 8);
|
||||
}
|
||||
|
||||
case SCC_HIR_TYPE_UNION: {
|
||||
@@ -137,8 +140,9 @@ int scc_hir_type_size(scc_hir_module_t *mod, scc_hir_type_ref_t ref,
|
||||
if (fa_bits > max_align_bits)
|
||||
max_align_bits = fa_bits;
|
||||
}
|
||||
return SCC_ALIGN_UP(max_size_bits,
|
||||
scc_type_abi_get_aggregate_align(abi, max_align_bits / 8) * 8);
|
||||
return SCC_ALIGN_UP(
|
||||
max_size_bits,
|
||||
scc_type_abi_get_aggregate_align(abi, max_align_bits / 8) * 8);
|
||||
}
|
||||
|
||||
default:
|
||||
@@ -149,8 +153,8 @@ int scc_hir_type_size(scc_hir_module_t *mod, scc_hir_type_ref_t ref,
|
||||
int scc_hir_field_offset(scc_hir_module_t *mod, scc_hir_type_ref_t ref,
|
||||
int field_idx, const scc_type_abi_t *abi) {
|
||||
scc_hir_type_t *type = scc_hir_module_get_type(mod, ref);
|
||||
if (!type || (type->tag != SCC_HIR_TYPE_STRUCT &&
|
||||
type->tag != SCC_HIR_TYPE_UNION))
|
||||
if (!type ||
|
||||
(type->tag != SCC_HIR_TYPE_STRUCT && type->tag != SCC_HIR_TYPE_UNION))
|
||||
return 0;
|
||||
|
||||
if (type->tag == SCC_HIR_TYPE_UNION)
|
||||
@@ -162,10 +166,12 @@ int scc_hir_field_offset(scc_hir_module_t *mod, scc_hir_type_ref_t ref,
|
||||
return 0;
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
scc_hir_type_ref_t fr = scc_vec_at(type->data.aggregate.fields, (usize)i);
|
||||
scc_hir_type_ref_t fr =
|
||||
scc_vec_at(type->data.aggregate.fields, (usize)i);
|
||||
int fs_bits = scc_hir_type_size(mod, fr, abi);
|
||||
int fa_bits = scc_hir_type_align(mod, fr, abi);
|
||||
int fa2_bits = scc_type_abi_get_field_align(abi, fs_bits / 8, fa_bits / 8) * 8;
|
||||
int fa2_bits =
|
||||
scc_type_abi_get_field_align(abi, fs_bits / 8, fa_bits / 8) * 8;
|
||||
offset_bits = SCC_ALIGN_UP(offset_bits, fa2_bits);
|
||||
if (i == field_idx)
|
||||
return offset_bits;
|
||||
@@ -178,9 +184,9 @@ scc_hir_aggregate_layout_t *
|
||||
scc_hir_aggregate_layout(scc_hir_module_t *mod, scc_hir_type_ref_t ref,
|
||||
const scc_type_abi_t *abi) {
|
||||
scc_hir_type_t *type = scc_hir_module_get_type(mod, ref);
|
||||
if (!type || (type->tag != SCC_HIR_TYPE_STRUCT &&
|
||||
type->tag != SCC_HIR_TYPE_UNION))
|
||||
return NULL;
|
||||
if (!type ||
|
||||
(type->tag != SCC_HIR_TYPE_STRUCT && type->tag != SCC_HIR_TYPE_UNION))
|
||||
return nullptr;
|
||||
|
||||
int fc = (int)scc_vec_size(type->data.aggregate.fields);
|
||||
|
||||
@@ -226,7 +232,8 @@ scc_hir_aggregate_layout(scc_hir_module_t *mod, scc_hir_type_ref_t ref,
|
||||
scc_vec_at(type->data.aggregate.fields, (usize)i);
|
||||
int fs_bits = scc_hir_type_size(mod, fr, abi);
|
||||
int fa_bits = scc_hir_type_align(mod, fr, abi);
|
||||
int fa2_bits = scc_type_abi_get_field_align(abi, fs_bits / 8, fa_bits / 8) * 8;
|
||||
int fa2_bits =
|
||||
scc_type_abi_get_field_align(abi, fs_bits / 8, fa_bits / 8) * 8;
|
||||
offset_bits = SCC_ALIGN_UP(offset_bits, fa2_bits);
|
||||
|
||||
al->fields[i].offset = offset_bits;
|
||||
@@ -238,8 +245,9 @@ scc_hir_aggregate_layout(scc_hir_module_t *mod, scc_hir_type_ref_t ref,
|
||||
max_align_bits = fa2_bits;
|
||||
}
|
||||
|
||||
al->size = SCC_ALIGN_UP(offset_bits,
|
||||
scc_type_abi_get_aggregate_align(abi, max_align_bits / 8) * 8);
|
||||
al->size = SCC_ALIGN_UP(
|
||||
offset_bits,
|
||||
scc_type_abi_get_aggregate_align(abi, max_align_bits / 8) * 8);
|
||||
al->align = scc_type_abi_get_aggregate_align(abi, max_align_bits / 8) * 8;
|
||||
return al;
|
||||
}
|
||||
|
||||
@@ -46,8 +46,16 @@ void scc_hir_module_drop(scc_hir_module_t *ctx) {
|
||||
|
||||
for (usize i = 1; i < ctx->types.size; i++) {
|
||||
scc_hir_type_t *type = &ctx->types.data[i];
|
||||
if (type->tag == SCC_HIR_TYPE_FUNC) {
|
||||
switch (type->tag) {
|
||||
case SCC_HIR_TYPE_FUNC:
|
||||
scc_vec_free(type->data.function.params);
|
||||
break;
|
||||
case SCC_HIR_TYPE_STRUCT:
|
||||
case SCC_HIR_TYPE_UNION:
|
||||
scc_vec_free(type->data.aggregate.fields);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user