/* * Copyright (c) 2019 Andrew Kelley * * This file is part of zig, which is MIT licensed. * See http://opensource.org/licenses/MIT */ #include "dump_analysis.hpp" #include "compiler.hpp" #include "analyze.hpp" #include "config.h" #include "ir.hpp" #include "codegen.hpp" enum JsonWriterState { JsonWriterStateInvalid, JsonWriterStateValue, JsonWriterStateArrayStart, JsonWriterStateArray, JsonWriterStateObjectStart, JsonWriterStateObject, }; #define JSON_MAX_DEPTH 10 struct JsonWriter { size_t state_index; FILE *f; const char *one_indent; const char *nl; JsonWriterState state[JSON_MAX_DEPTH]; }; static void jw_init(JsonWriter *jw, FILE *f, const char *one_indent, const char *nl) { jw->state_index = 1; jw->f = f; jw->one_indent = one_indent; jw->nl = nl; jw->state[0] = JsonWriterStateInvalid; jw->state[1] = JsonWriterStateValue; } static void jw_nl_indent(JsonWriter *jw) { assert(jw->state_index >= 1); fprintf(jw->f, "%s", jw->nl); for (size_t i = 0; i < jw->state_index - 1; i += 1) { fprintf(jw->f, "%s", jw->one_indent); } } static void jw_push_state(JsonWriter *jw, JsonWriterState state) { jw->state_index += 1; assert(jw->state_index < JSON_MAX_DEPTH); jw->state[jw->state_index] = state; } static void jw_pop_state(JsonWriter *jw) { assert(jw->state_index != 0); jw->state_index -= 1; } static void jw_begin_array(JsonWriter *jw) { assert(jw->state[jw->state_index] == JsonWriterStateValue); fprintf(jw->f, "["); jw->state[jw->state_index] = JsonWriterStateArrayStart; } static void jw_begin_object(JsonWriter *jw) { assert(jw->state[jw->state_index] == JsonWriterStateValue); fprintf(jw->f, "{"); jw->state[jw->state_index] = JsonWriterStateObjectStart; } static void jw_array_elem(JsonWriter *jw) { switch (jw->state[jw->state_index]) { case JsonWriterStateInvalid: case JsonWriterStateValue: case JsonWriterStateObjectStart: case JsonWriterStateObject: zig_unreachable(); case JsonWriterStateArray: fprintf(jw->f, ","); ZIG_FALLTHROUGH; case JsonWriterStateArrayStart: jw->state[jw->state_index] = JsonWriterStateArray; jw_push_state(jw, JsonWriterStateValue); jw_nl_indent(jw); return; } zig_unreachable(); } static void jw_write_escaped_string(JsonWriter *jw, const char *s) { fprintf(jw->f, "\""); for (;; s += 1) { switch (*s) { case 0: fprintf(jw->f, "\""); return; case '"': fprintf(jw->f, "\\\""); continue; case '\t': fprintf(jw->f, "\\t"); continue; case '\r': fprintf(jw->f, "\\r"); continue; case '\n': fprintf(jw->f, "\\n"); continue; case '\b': fprintf(jw->f, "\\b"); continue; case '\f': fprintf(jw->f, "\\f"); continue; case '\\': fprintf(jw->f, "\\\\"); continue; default: fprintf(jw->f, "%c", *s); continue; } } } static void jw_object_field(JsonWriter *jw, const char *name) { switch (jw->state[jw->state_index]) { case JsonWriterStateInvalid: case JsonWriterStateValue: case JsonWriterStateArray: case JsonWriterStateArrayStart: zig_unreachable(); case JsonWriterStateObject: fprintf(jw->f, ","); ZIG_FALLTHROUGH; case JsonWriterStateObjectStart: jw->state[jw->state_index] = JsonWriterStateObject; jw_push_state(jw, JsonWriterStateValue); jw_nl_indent(jw); jw_write_escaped_string(jw, name); fprintf(jw->f, ": "); return; } zig_unreachable(); } static void jw_end_array(JsonWriter *jw) { switch (jw->state[jw->state_index]) { case JsonWriterStateInvalid: case JsonWriterStateValue: case JsonWriterStateObjectStart: case JsonWriterStateObject: zig_unreachable(); case JsonWriterStateArrayStart: fprintf(jw->f, "]"); jw_pop_state(jw); return; case JsonWriterStateArray: jw_nl_indent(jw); jw_pop_state(jw); fprintf(jw->f, "]"); return; } zig_unreachable(); } static void jw_end_object(JsonWriter *jw) { switch (jw->state[jw->state_index]) { case JsonWriterStateInvalid: zig_unreachable(); case JsonWriterStateValue: zig_unreachable(); case JsonWriterStateArray: zig_unreachable(); case JsonWriterStateArrayStart: zig_unreachable(); case JsonWriterStateObjectStart: fprintf(jw->f, "}"); jw_pop_state(jw); return; case JsonWriterStateObject: jw_nl_indent(jw); jw_pop_state(jw); fprintf(jw->f, "}"); return; } zig_unreachable(); } static void jw_null(JsonWriter *jw) { assert(jw->state[jw->state_index] == JsonWriterStateValue); fprintf(jw->f, "null"); jw_pop_state(jw); } static void jw_bool(JsonWriter *jw, bool x) { assert(jw->state[jw->state_index] == JsonWriterStateValue); if (x) { fprintf(jw->f, "true"); } else { fprintf(jw->f, "false"); } jw_pop_state(jw); } static void jw_int(JsonWriter *jw, int64_t x) { assert(jw->state[jw->state_index] == JsonWriterStateValue); if (x > 4503599627370496 || x < -4503599627370496) { fprintf(jw->f, "\"%" ZIG_PRI_i64 "\"", x); } else { fprintf(jw->f, "%" ZIG_PRI_i64, x); } jw_pop_state(jw); } static void jw_bigint(JsonWriter *jw, const BigInt *x) { assert(jw->state[jw->state_index] == JsonWriterStateValue); Buf *str = buf_alloc(); bigint_append_buf(str, x, 10); if (bigint_fits_in_bits(x, 52, true)) { fprintf(jw->f, "%s", buf_ptr(str)); } else { fprintf(jw->f, "\"%s\"", buf_ptr(str)); } jw_pop_state(jw); buf_destroy(str); } static void jw_string(JsonWriter *jw, const char *s) { assert(jw->state[jw->state_index] == JsonWriterStateValue); jw_write_escaped_string(jw, s); jw_pop_state(jw); } static void tree_print(FILE *f, ZigType *ty, size_t indent); static int compare_type_abi_sizes_desc(const void *a, const void *b) { uint64_t size_a = (*(ZigType * const*)(a))->abi_size; uint64_t size_b = (*(ZigType * const*)(b))->abi_size; if (size_a > size_b) return -1; if (size_a < size_b) return 1; return 0; } static void start_child(FILE *f, size_t indent) { fprintf(f, "\n"); for (size_t i = 0; i < indent; i += 1) { fprintf(f, " "); } } static void start_peer(FILE *f, size_t indent) { fprintf(f, ",\n"); for (size_t i = 0; i < indent; i += 1) { fprintf(f, " "); } } static void tree_print_struct(FILE *f, ZigType *struct_type, size_t indent) { ZigList children = {}; uint64_t sum_from_fields = 0; for (size_t i = 0; i < struct_type->data.structure.src_field_count; i += 1) { TypeStructField *field = struct_type->data.structure.fields[i]; children.append(field->type_entry); sum_from_fields += field->type_entry->abi_size; } qsort(children.items, children.length, sizeof(ZigType *), compare_type_abi_sizes_desc); start_peer(f, indent); fprintf(f, "\"padding\": \"%" ZIG_PRI_u64 "\"", struct_type->abi_size - sum_from_fields); start_peer(f, indent); fprintf(f, "\"fields\": ["); for (size_t i = 0; i < children.length; i += 1) { if (i == 0) { start_child(f, indent + 1); } else { start_peer(f, indent + 1); } fprintf(f, "{"); ZigType *child_type = children.at(i); tree_print(f, child_type, indent + 2); start_child(f, indent + 1); fprintf(f, "}"); } start_child(f, indent); fprintf(f, "]"); } static void tree_print(FILE *f, ZigType *ty, size_t indent) { start_child(f, indent); fprintf(f, "\"type\": \"%s\"", buf_ptr(&ty->name)); start_peer(f, indent); fprintf(f, "\"sizef\": \""); zig_pretty_print_bytes(f, ty->abi_size); fprintf(f, "\""); start_peer(f, indent); fprintf(f, "\"size\": \"%" ZIG_PRI_usize "\"", ty->abi_size); switch (ty->id) { case ZigTypeIdFnFrame: return tree_print_struct(f, ty->data.frame.locals_struct, indent); case ZigTypeIdStruct: return tree_print_struct(f, ty, indent); default: start_child(f, indent); return; } } void zig_print_stack_report(CodeGen *g, FILE *f) { if (g->largest_frame_fn == nullptr) { fprintf(f, "{\"error\": \"No async function frames in entire compilation.\"}\n"); return; } fprintf(f, "{"); tree_print(f, g->largest_frame_fn->frame_type, 1); start_child(f, 0); fprintf(f, "}\n"); } struct AnalDumpCtx { CodeGen *g; JsonWriter jw; ZigList type_list; HashMap type_map; ZigList pkg_list; HashMap pkg_map; ZigList file_list; HashMap file_map; ZigList decl_list; HashMap decl_map; ZigList fn_list; HashMap fn_map; ZigList node_list; HashMap node_map; ZigList err_list; HashMap err_map; }; static uint32_t anal_dump_get_type_id(AnalDumpCtx *ctx, ZigType *ty); static void anal_dump_value(AnalDumpCtx *ctx, AstNode *source_node, ZigType *ty, ZigValue *value); static void anal_dump_poke_value(AnalDumpCtx *ctx, AstNode *source_node, ZigType *ty, ZigValue *value) { Error err; if (value->type != ty) { return; } if ((err = ir_resolve_lazy(ctx->g, source_node, value))) { codegen_report_errors_and_exit(ctx->g); } if (value->special == ConstValSpecialUndef) { return; } if (value->special == ConstValSpecialRuntime) { return; } switch (ty->id) { case ZigTypeIdMetaType: { ZigType *val_ty = value->data.x_type; (void)anal_dump_get_type_id(ctx, val_ty); return; } default: return; } zig_unreachable(); } static uint32_t anal_dump_get_type_id(AnalDumpCtx *ctx, ZigType *ty) { uint32_t type_id = ctx->type_list.length; auto existing_entry = ctx->type_map.put_unique(ty, type_id); if (existing_entry == nullptr) { ctx->type_list.append(ty); } else { type_id = existing_entry->value; } return type_id; } static uint32_t anal_dump_get_pkg_id(AnalDumpCtx *ctx, ZigPackage *pkg) { assert(pkg != nullptr); uint32_t pkg_id = ctx->pkg_list.length; auto existing_entry = ctx->pkg_map.put_unique(pkg, pkg_id); if (existing_entry == nullptr) { ctx->pkg_list.append(pkg); } else { pkg_id = existing_entry->value; } return pkg_id; } static uint32_t anal_dump_get_file_id(AnalDumpCtx *ctx, Buf *file) { uint32_t file_id = ctx->file_list.length; auto existing_entry = ctx->file_map.put_unique(file, file_id); if (existing_entry == nullptr) { ctx->file_list.append(file); } else { file_id = existing_entry->value; } return file_id; } static uint32_t anal_dump_get_node_id(AnalDumpCtx *ctx, AstNode *node) { uint32_t node_id = ctx->node_list.length; auto existing_entry = ctx->node_map.put_unique(node, node_id); if (existing_entry == nullptr) { ctx->node_list.append(node); } else { node_id = existing_entry->value; } return node_id; } static uint32_t anal_dump_get_fn_id(AnalDumpCtx *ctx, ZigFn *fn) { uint32_t fn_id = ctx->fn_list.length; auto existing_entry = ctx->fn_map.put_unique(fn, fn_id); if (existing_entry == nullptr) { ctx->fn_list.append(fn); // poke the fn (void)anal_dump_get_type_id(ctx, fn->type_entry); (void)anal_dump_get_node_id(ctx, fn->proto_node); } else { fn_id = existing_entry->value; } return fn_id; } static uint32_t anal_dump_get_err_id(AnalDumpCtx *ctx, ErrorTableEntry *err) { uint32_t err_id = ctx->err_list.length; auto existing_entry = ctx->err_map.put_unique(err, err_id); if (existing_entry == nullptr) { ctx->err_list.append(err); } else { err_id = existing_entry->value; } return err_id; } static uint32_t anal_dump_get_decl_id(AnalDumpCtx *ctx, Tld *tld) { uint32_t decl_id = ctx->decl_list.length; auto existing_entry = ctx->decl_map.put_unique(tld, decl_id); if (existing_entry == nullptr) { ctx->decl_list.append(tld); if (tld->import != nullptr) { (void)anal_dump_get_type_id(ctx, tld->import); } // poke the types switch (tld->id) { case TldIdVar: { TldVar *tld_var = reinterpret_cast(tld); ZigVar *var = tld_var->var; if (var != nullptr) { (void)anal_dump_get_type_id(ctx, var->var_type); if (var->const_value != nullptr) { anal_dump_poke_value(ctx, var->decl_node, var->var_type, var->const_value); } } break; } case TldIdFn: { TldFn *tld_fn = reinterpret_cast(tld); ZigFn *fn = tld_fn->fn_entry; if (fn != nullptr) { (void)anal_dump_get_type_id(ctx, fn->type_entry); } break; } default: break; } } else { decl_id = existing_entry->value; } return decl_id; } static void anal_dump_type_ref(AnalDumpCtx *ctx, ZigType *ty) { uint32_t type_id = anal_dump_get_type_id(ctx, ty); jw_int(&ctx->jw, type_id); } static void anal_dump_pkg_ref(AnalDumpCtx *ctx, ZigPackage *pkg) { uint32_t pkg_id = anal_dump_get_pkg_id(ctx, pkg); jw_int(&ctx->jw, pkg_id); } static void anal_dump_file_ref(AnalDumpCtx *ctx, Buf *file) { uint32_t file_id = anal_dump_get_file_id(ctx, file); jw_int(&ctx->jw, file_id); } static void anal_dump_node_ref(AnalDumpCtx *ctx, AstNode *node) { uint32_t node_id = anal_dump_get_node_id(ctx, node); jw_int(&ctx->jw, node_id); } static void anal_dump_fn_ref(AnalDumpCtx *ctx, ZigFn *fn) { uint32_t fn_id = anal_dump_get_fn_id(ctx, fn); jw_int(&ctx->jw, fn_id); } static void anal_dump_err_ref(AnalDumpCtx *ctx, ErrorTableEntry *err) { uint32_t err_id = anal_dump_get_err_id(ctx, err); jw_int(&ctx->jw, err_id); } static void anal_dump_decl_ref(AnalDumpCtx *ctx, Tld *tld) { uint32_t decl_id = anal_dump_get_decl_id(ctx, tld); jw_int(&ctx->jw, decl_id); } static void anal_dump_pkg(AnalDumpCtx *ctx, ZigPackage *pkg) { JsonWriter *jw = &ctx->jw; Buf full_path_buf = BUF_INIT; os_path_join(&pkg->root_src_dir, &pkg->root_src_path, &full_path_buf); Buf *resolve_paths[] = { &full_path_buf, }; Buf *resolved_path = buf_alloc(); *resolved_path = os_path_resolve(resolve_paths, 1); auto import_entry = ctx->g->import_table.maybe_get(resolved_path); if (!import_entry) { return; } jw_array_elem(jw); jw_begin_object(jw); jw_object_field(jw, "name"); jw_string(jw, buf_ptr(&pkg->pkg_path)); jw_object_field(jw, "file"); anal_dump_file_ref(ctx, resolved_path); jw_object_field(jw, "main"); anal_dump_type_ref(ctx, import_entry->value); jw_object_field(jw, "table"); jw_begin_object(jw); auto it = pkg->package_table.entry_iterator(); for (;;) { auto *entry = it.next(); if (!entry) break; ZigPackage *child_pkg = entry->value; if (child_pkg != nullptr) { jw_object_field(jw, buf_ptr(entry->key)); anal_dump_pkg_ref(ctx, child_pkg); } } jw_end_object(jw); jw_end_object(jw); } static void anal_dump_decl(AnalDumpCtx *ctx, Tld *tld) { JsonWriter *jw = &ctx->jw; bool make_obj = tld->id == TldIdVar || tld->id == TldIdFn; if (make_obj) { jw_array_elem(jw); jw_begin_object(jw); jw_object_field(jw, "import"); anal_dump_type_ref(ctx, tld->import); jw_object_field(jw, "src"); anal_dump_node_ref(ctx, tld->source_node); jw_object_field(jw, "name"); jw_string(jw, buf_ptr(tld->name)); } switch (tld->id) { case TldIdVar: { TldVar *tld_var = reinterpret_cast(tld); ZigVar *var = tld_var->var; if (var != nullptr) { jw_object_field(jw, "kind"); if (var->src_is_const) { jw_string(jw, "const"); } else { jw_string(jw, "var"); } if (var->is_thread_local) { jw_object_field(jw, "threadlocal"); jw_bool(jw, true); } jw_object_field(jw, "type"); anal_dump_type_ref(ctx, var->var_type); if (var->const_value != nullptr) { jw_object_field(jw, "value"); anal_dump_value(ctx, var->decl_node, var->var_type, var->const_value); } } break; } case TldIdFn: { TldFn *tld_fn = reinterpret_cast(tld); ZigFn *fn = tld_fn->fn_entry; if (fn != nullptr) { jw_object_field(jw, "kind"); jw_string(jw, "const"); jw_object_field(jw, "type"); anal_dump_type_ref(ctx, fn->type_entry); jw_object_field(jw, "value"); anal_dump_fn_ref(ctx, fn); } break; } default: break; } if (make_obj) { jw_end_object(jw); } } static void anal_dump_file(AnalDumpCtx *ctx, Buf *file) { JsonWriter *jw = &ctx->jw; jw_string(jw, buf_ptr(file)); } static void anal_dump_value(AnalDumpCtx *ctx, AstNode *source_node, ZigType *ty, ZigValue *value) { Error err; if (value->type != ty) { jw_null(&ctx->jw); return; } if ((err = ir_resolve_lazy(ctx->g, source_node, value))) { codegen_report_errors_and_exit(ctx->g); } if (value->special == ConstValSpecialUndef) { jw_string(&ctx->jw, "undefined"); return; } if (value->special == ConstValSpecialRuntime) { jw_null(&ctx->jw); return; } switch (ty->id) { case ZigTypeIdMetaType: { ZigType *val_ty = value->data.x_type; anal_dump_type_ref(ctx, val_ty); return; } case ZigTypeIdFn: { if (value->data.x_ptr.special == ConstPtrSpecialFunction) { ZigFn *val_fn = value->data.x_ptr.data.fn.fn_entry; anal_dump_fn_ref(ctx, val_fn); } else { jw_null(&ctx->jw); } return; } default: jw_null(&ctx->jw); return; } zig_unreachable(); } static void anal_dump_pointer_attrs(AnalDumpCtx *ctx, ZigType *ty) { JsonWriter *jw = &ctx->jw; if (ty->data.pointer.explicit_alignment != 0) { jw_object_field(jw, "align"); jw_int(jw, ty->data.pointer.explicit_alignment); } if (ty->data.pointer.is_const) { jw_object_field(jw, "const"); jw_bool(jw, true); } if (ty->data.pointer.is_volatile) { jw_object_field(jw, "volatile"); jw_bool(jw, true); } if (ty->data.pointer.allow_zero) { jw_object_field(jw, "allowZero"); jw_bool(jw, true); } if (ty->data.pointer.host_int_bytes != 0) { jw_object_field(jw, "hostIntBytes"); jw_int(jw, ty->data.pointer.host_int_bytes); jw_object_field(jw, "bitOffsetInHost"); jw_int(jw, ty->data.pointer.bit_offset_in_host); } jw_object_field(jw, "elem"); anal_dump_type_ref(ctx, ty->data.pointer.child_type); } static void anal_dump_type(AnalDumpCtx *ctx, ZigType *ty) { JsonWriter *jw = &ctx->jw; jw_array_elem(jw); jw_begin_object(jw); jw_object_field(jw, "kind"); jw_int(jw, type_id_index(ty)); switch (ty->id) { case ZigTypeIdMetaType: case ZigTypeIdBool: case ZigTypeIdEnumLiteral: break; case ZigTypeIdStruct: { if (ty->data.structure.special == StructSpecialSlice) { jw_object_field(jw, "len"); jw_int(jw, 2); anal_dump_pointer_attrs(ctx, ty->data.structure.fields[slice_ptr_index]->type_entry); break; } jw_object_field(jw, "name"); jw_string(jw, buf_ptr(&ty->name)); jw_object_field(jw, "src"); anal_dump_node_ref(ctx, ty->data.structure.decl_node); { jw_object_field(jw, "pubDecls"); jw_begin_array(jw); ScopeDecls *decls_scope = ty->data.structure.decls_scope; auto it = decls_scope->decl_table.entry_iterator(); for (;;) { auto *entry = it.next(); if (!entry) break; Tld *tld = entry->value; if (tld->visib_mod == VisibModPub) { jw_array_elem(jw); anal_dump_decl_ref(ctx, tld); } } jw_end_array(jw); } { jw_object_field(jw, "privDecls"); jw_begin_array(jw); ScopeDecls *decls_scope = ty->data.structure.decls_scope; auto it = decls_scope->decl_table.entry_iterator(); for (;;) { auto *entry = it.next(); if (!entry) break; Tld *tld = entry->value; if (tld->visib_mod == VisibModPrivate) { jw_array_elem(jw); anal_dump_decl_ref(ctx, tld); } } jw_end_array(jw); } if (ty->data.structure.src_field_count != 0) { jw_object_field(jw, "fields"); jw_begin_array(jw); for(size_t i = 0; i < ty->data.structure.src_field_count; i += 1) { jw_array_elem(jw); anal_dump_type_ref(ctx, ty->data.structure.fields[i]->type_entry); } jw_end_array(jw); } if (ty->data.structure.root_struct != nullptr) { Buf *path_buf = ty->data.structure.root_struct->path; jw_object_field(jw, "file"); anal_dump_file_ref(ctx, path_buf); } break; } case ZigTypeIdUnion: { jw_object_field(jw, "name"); jw_string(jw, buf_ptr(&ty->name)); jw_object_field(jw, "src"); anal_dump_node_ref(ctx, ty->data.unionation.decl_node); { jw_object_field(jw, "pubDecls"); jw_begin_array(jw); ScopeDecls *decls_scope = ty->data.unionation.decls_scope; auto it = decls_scope->decl_table.entry_iterator(); for (;;) { auto *entry = it.next(); if (!entry) break; Tld *tld = entry->value; if (tld->visib_mod == VisibModPub) { jw_array_elem(jw); anal_dump_decl_ref(ctx, tld); } } jw_end_array(jw); } { jw_object_field(jw, "privDecls"); jw_begin_array(jw); ScopeDecls *decls_scope = ty->data.unionation.decls_scope; auto it = decls_scope->decl_table.entry_iterator(); for (;;) { auto *entry = it.next(); if (!entry) break; Tld *tld = entry->value; if (tld->visib_mod == VisibModPrivate) { jw_array_elem(jw); anal_dump_decl_ref(ctx, tld); } } jw_end_array(jw); } if (ty->data.unionation.src_field_count != 0) { jw_object_field(jw, "fields"); jw_begin_array(jw); for(size_t i = 0; i < ty->data.unionation.src_field_count; i += 1) { jw_array_elem(jw); anal_dump_type_ref(ctx, ty->data.unionation.fields[i].type_entry); } jw_end_array(jw); } break; } case ZigTypeIdEnum: { jw_object_field(jw, "name"); jw_string(jw, buf_ptr(&ty->name)); jw_object_field(jw, "src"); anal_dump_node_ref(ctx, ty->data.enumeration.decl_node); { jw_object_field(jw, "pubDecls"); jw_begin_array(jw); ScopeDecls *decls_scope = ty->data.enumeration.decls_scope; auto it = decls_scope->decl_table.entry_iterator(); for (;;) { auto *entry = it.next(); if (!entry) break; Tld *tld = entry->value; if (tld->visib_mod == VisibModPub) { jw_array_elem(jw); anal_dump_decl_ref(ctx, tld); } } jw_end_array(jw); } { jw_object_field(jw, "privDecls"); jw_begin_array(jw); ScopeDecls *decls_scope = ty->data.enumeration.decls_scope; auto it = decls_scope->decl_table.entry_iterator(); for (;;) { auto *entry = it.next(); if (!entry) break; Tld *tld = entry->value; if (tld->visib_mod == VisibModPrivate) { jw_array_elem(jw); anal_dump_decl_ref(ctx, tld); } } jw_end_array(jw); } if (ty->data.enumeration.src_field_count != 0) { jw_object_field(jw, "fields"); jw_begin_array(jw); for(size_t i = 0; i < ty->data.enumeration.src_field_count; i += 1) { jw_array_elem(jw); jw_bigint(jw, &ty->data.enumeration.fields[i].value); } jw_end_array(jw); } break; } case ZigTypeIdFloat: { jw_object_field(jw, "bits"); jw_int(jw, ty->data.floating.bit_count); break; } case ZigTypeIdInt: { if (ty->data.integral.is_signed) { jw_object_field(jw, "i"); } else { jw_object_field(jw, "u"); } jw_int(jw, ty->data.integral.bit_count); break; } case ZigTypeIdFn: { jw_object_field(jw, "name"); jw_string(jw, buf_ptr(&ty->name)); jw_object_field(jw, "generic"); jw_bool(jw, ty->data.fn.is_generic); if (ty->data.fn.fn_type_id.return_type != nullptr) { jw_object_field(jw, "ret"); anal_dump_type_ref(ctx, ty->data.fn.fn_type_id.return_type); } if (ty->data.fn.fn_type_id.param_count != 0) { jw_object_field(jw, "args"); jw_begin_array(jw); for (size_t i = 0; i < ty->data.fn.fn_type_id.param_count; i += 1) { jw_array_elem(jw); if (ty->data.fn.fn_type_id.param_info[i].type != nullptr) { anal_dump_type_ref(ctx, ty->data.fn.fn_type_id.param_info[i].type); } else { jw_null(jw); } } jw_end_array(jw); } break; } case ZigTypeIdOptional: { jw_object_field(jw, "child"); anal_dump_type_ref(ctx, ty->data.maybe.child_type); break; } case ZigTypeIdPointer: { switch (ty->data.pointer.ptr_len) { case PtrLenSingle: break; case PtrLenUnknown: jw_object_field(jw, "len"); jw_int(jw, 1); break; case PtrLenC: jw_object_field(jw, "len"); jw_int(jw, 3); break; } anal_dump_pointer_attrs(ctx, ty); break; } case ZigTypeIdErrorSet: { if (type_is_global_error_set(ty)) { break; } jw_object_field(jw, "name"); jw_string(jw, buf_ptr(&ty->name)); if (ty->data.error_set.infer_fn != nullptr) { jw_object_field(jw, "fn"); anal_dump_fn_ref(ctx, ty->data.error_set.infer_fn); } jw_object_field(jw, "errors"); jw_begin_array(jw); for (uint32_t i = 0; i < ty->data.error_set.err_count; i += 1) { jw_array_elem(jw); ErrorTableEntry *err = ty->data.error_set.errors[i]; anal_dump_err_ref(ctx, err); } jw_end_array(jw); break; } case ZigTypeIdErrorUnion: { jw_object_field(jw, "err"); anal_dump_type_ref(ctx, ty->data.error_union.err_set_type); jw_object_field(jw, "payload"); anal_dump_type_ref(ctx, ty->data.error_union.payload_type); break; } case ZigTypeIdArray: { jw_object_field(jw, "len"); jw_int(jw, ty->data.array.len); jw_object_field(jw, "elem"); anal_dump_type_ref(ctx, ty->data.array.child_type); break; } default: jw_object_field(jw, "name"); jw_string(jw, buf_ptr(&ty->name)); break; } jw_end_object(jw); } static void anal_dump_node(AnalDumpCtx *ctx, const AstNode *node) { JsonWriter *jw = &ctx->jw; jw_begin_object(jw); jw_object_field(jw, "file"); anal_dump_file_ref(ctx, node->owner->data.structure.root_struct->path); jw_object_field(jw, "line"); jw_int(jw, node->line); jw_object_field(jw, "col"); jw_int(jw, node->column); const Buf *doc_comments_buf = nullptr; const Buf *name_buf = nullptr; const ZigList *field_nodes = nullptr; bool is_var_args = false; bool is_noalias = false; bool is_comptime = false; switch (node->type) { case NodeTypeParamDecl: doc_comments_buf = &node->data.param_decl.doc_comments; name_buf = node->data.param_decl.name; is_var_args = node->data.param_decl.is_var_args; is_noalias = node->data.param_decl.is_noalias; is_comptime = node->data.param_decl.is_comptime; break; case NodeTypeFnProto: doc_comments_buf = &node->data.fn_proto.doc_comments; field_nodes = &node->data.fn_proto.params; is_var_args = node->data.fn_proto.is_var_args; break; case NodeTypeVariableDeclaration: doc_comments_buf = &node->data.variable_declaration.doc_comments; break; case NodeTypeErrorSetField: doc_comments_buf = &node->data.err_set_field.doc_comments; break; case NodeTypeStructField: doc_comments_buf = &node->data.struct_field.doc_comments; name_buf = node->data.struct_field.name; break; case NodeTypeContainerDecl: field_nodes = &node->data.container_decl.fields; doc_comments_buf = &node->data.container_decl.doc_comments; break; default: break; } if (doc_comments_buf != nullptr && doc_comments_buf->list.length != 0) { jw_object_field(jw, "docs"); jw_string(jw, buf_ptr(doc_comments_buf)); } if (name_buf != nullptr) { jw_object_field(jw, "name"); jw_string(jw, buf_ptr(name_buf)); } if (field_nodes != nullptr) { jw_object_field(jw, "fields"); jw_begin_array(jw); for (size_t i = 0; i < field_nodes->length; i += 1) { jw_array_elem(jw); anal_dump_node_ref(ctx, field_nodes->at(i)); } jw_end_array(jw); } if (is_var_args) { jw_object_field(jw, "varArgs"); jw_bool(jw, true); } if (is_comptime) { jw_object_field(jw, "comptime"); jw_bool(jw, true); } if (is_noalias) { jw_object_field(jw, "noalias"); jw_bool(jw, true); } jw_end_object(jw); } static void anal_dump_err(AnalDumpCtx *ctx, const ErrorTableEntry *err) { JsonWriter *jw = &ctx->jw; jw_begin_object(jw); jw_object_field(jw, "src"); anal_dump_node_ref(ctx, err->decl_node); jw_object_field(jw, "name"); jw_string(jw, buf_ptr(&err->name)); jw_end_object(jw); } static void anal_dump_fn(AnalDumpCtx *ctx, ZigFn *fn) { JsonWriter *jw = &ctx->jw; jw_begin_object(jw); jw_object_field(jw, "src"); anal_dump_node_ref(ctx, fn->proto_node); jw_object_field(jw, "type"); anal_dump_type_ref(ctx, fn->type_entry); jw_end_object(jw); } void zig_print_analysis_dump(CodeGen *g, FILE *f, const char *one_indent, const char *nl) { Error err; AnalDumpCtx ctx = {}; ctx.g = g; JsonWriter *jw = &ctx.jw; jw_init(jw, f, one_indent, nl); ctx.type_map.init(16); ctx.pkg_map.init(16); ctx.file_map.init(16); ctx.decl_map.init(16); ctx.node_map.init(16); ctx.fn_map.init(16); ctx.err_map.init(16); jw_begin_object(jw); jw_object_field(jw, "typeKinds"); jw_begin_array(jw); for (size_t i = 0; i < type_id_len(); i += 1) { jw_array_elem(jw); jw_string(jw, type_id_name(type_id_at_index(i))); } jw_end_array(jw); jw_object_field(jw, "params"); jw_begin_object(jw); { jw_object_field(jw, "zigId"); Buf *compiler_id; if ((err = get_compiler_id(&compiler_id))) { fprintf(stderr, "Unable to determine compiler id: %s\n", err_str(err)); exit(1); } jw_string(jw, buf_ptr(compiler_id)); jw_object_field(jw, "zigVersion"); jw_string(jw, ZIG_VERSION_STRING); jw_object_field(jw, "builds"); jw_begin_array(jw); jw_array_elem(jw); jw_begin_object(jw); jw_object_field(jw, "target"); Buf triple_buf = BUF_INIT; target_triple_zig(&triple_buf, g->zig_target); jw_string(jw, buf_ptr(&triple_buf)); jw_end_object(jw); jw_end_array(jw); jw_object_field(jw, "rootName"); jw_string(jw, buf_ptr(g->root_out_name)); } jw_end_object(jw); jw_object_field(jw, "rootPkg"); anal_dump_pkg_ref(&ctx, g->main_pkg); // Poke the functions for (size_t i = 0; i < g->fn_defs.length; i += 1) { ZigFn *fn = g->fn_defs.at(i); (void)anal_dump_get_fn_id(&ctx, fn); } jw_object_field(jw, "calls"); jw_begin_array(jw); { ZigList var_stack = {}; auto it = g->memoized_fn_eval_table.entry_iterator(); for (;;) { auto *entry = it.next(); if (!entry) break; var_stack.resize(0); ZigFn *fn = nullptr; Scope *scope = entry->key; while (scope != nullptr) { if (scope->id == ScopeIdVarDecl) { ZigVar *var = reinterpret_cast(scope)->var; var_stack.append(var); } else if (scope->id == ScopeIdFnDef) { fn = reinterpret_cast(scope)->fn_entry; break; } scope = scope->parent; } ZigValue *result = entry->value; assert(fn != nullptr); jw_array_elem(jw); jw_begin_object(jw); jw_object_field(jw, "fn"); anal_dump_fn_ref(&ctx, fn); jw_object_field(jw, "result"); { jw_begin_object(jw); jw_object_field(jw, "type"); anal_dump_type_ref(&ctx, result->type); jw_object_field(jw, "value"); anal_dump_value(&ctx, scope->source_node, result->type, result); jw_end_object(jw); } if (var_stack.length != 0) { jw_object_field(jw, "args"); jw_begin_array(jw); while (var_stack.length != 0) { ZigVar *var = var_stack.pop(); jw_array_elem(jw); jw_begin_object(jw); jw_object_field(jw, "type"); anal_dump_type_ref(&ctx, var->var_type); jw_object_field(jw, "value"); anal_dump_value(&ctx, scope->source_node, var->var_type, var->const_value); jw_end_object(jw); } jw_end_array(jw); } jw_end_object(jw); } var_stack.deinit(); } jw_end_array(jw); jw_object_field(jw, "packages"); jw_begin_array(jw); for (uint32_t i = 0; i < ctx.pkg_list.length; i += 1) { anal_dump_pkg(&ctx, ctx.pkg_list.at(i)); } jw_end_array(jw); jw_object_field(jw, "types"); jw_begin_array(jw); for (uint32_t i = 0; i < ctx.type_list.length; i += 1) { ZigType *ty = ctx.type_list.at(i); anal_dump_type(&ctx, ty); } jw_end_array(jw); jw_object_field(jw, "decls"); jw_begin_array(jw); for (uint32_t i = 0; i < ctx.decl_list.length; i += 1) { Tld *decl = ctx.decl_list.at(i); anal_dump_decl(&ctx, decl); } jw_end_array(jw); jw_object_field(jw, "fns"); jw_begin_array(jw); for (uint32_t i = 0; i < ctx.fn_list.length; i += 1) { ZigFn *fn = ctx.fn_list.at(i); jw_array_elem(jw); anal_dump_fn(&ctx, fn); } jw_end_array(jw); jw_object_field(jw, "errors"); jw_begin_array(jw); for (uint32_t i = 0; i < ctx.err_list.length; i += 1) { const ErrorTableEntry *err = ctx.err_list.at(i); jw_array_elem(jw); anal_dump_err(&ctx, err); } jw_end_array(jw); jw_object_field(jw, "astNodes"); jw_begin_array(jw); for (uint32_t i = 0; i < ctx.node_list.length; i += 1) { const AstNode *node = ctx.node_list.at(i); jw_array_elem(jw); anal_dump_node(&ctx, node); } jw_end_array(jw); jw_object_field(jw, "files"); jw_begin_array(jw); for (uint32_t i = 0; i < ctx.file_list.length; i += 1) { Buf *file = ctx.file_list.at(i); jw_array_elem(jw); anal_dump_file(&ctx, file); } jw_end_array(jw); jw_end_object(jw); }