diff --git a/.vs/launch.vs.json b/.vs/launch.vs.json index ef4e3f8772..37fef37d82 100644 --- a/.vs/launch.vs.json +++ b/.vs/launch.vs.json @@ -84,6 +84,13 @@ "name": "REPL", "args": ["--user-auto"] }, + { + "type": "default", + "project": "CMakeLists.txt", + "projectTarget": "goalc.exe (bin\\goalc.exe)", + "name": "REPL - Jak 2", + "args": ["--user-auto", "--game", "jak2"] + }, { "type": "default", "project": "CMakeLists.txt", @@ -102,17 +109,6 @@ "${workspaceRoot}/decompiler_out" ] }, - { - "type": "default", - "project": "CMakeLists.txt", - "projectTarget": "decompiler.exe (bin\\decompiler.exe)", - "name": "Decompiler - Jak 1 - Data Only", - "args": [ - "${workspaceRoot}/decompiler/config/jak1_ntsc_black_label.jsonc", - "${workspaceRoot}/iso_data", - "${workspaceRoot}/decompiler_out" - ] - }, { "type": "default", "project": "CMakeLists.txt", @@ -124,17 +120,6 @@ "${workspaceRoot}/decompiler_out" ] }, - { - "type": "default", - "project": "CMakeLists.txt", - "projectTarget": "decompiler.exe (bin\\decompiler.exe)", - "name": "Disassembler - Jak 1", - "args": [ - "${workspaceRoot}/decompiler/config/jak1_ntsc_black_label.jsonc", - "${workspaceRoot}/iso_data", - "${workspaceRoot}/decompiler_out" - ] - }, { "type": "default", "project": "CMakeLists.txt", diff --git a/common/type_system/Type.h b/common/type_system/Type.h index 83784736df..73db075e8c 100644 --- a/common/type_system/Type.h +++ b/common/type_system/Type.h @@ -12,10 +12,17 @@ #include "TypeSpec.h" #include "common/goal_constants.h" +#include "common/goos/TextDB.h" #include "common/util/Assert.h" class TypeSystem; +// Various metadata that can be associated with a symbol or form +struct DefinitionMetadata { + std::optional definition_info; + std::optional docstring; +}; + struct MethodInfo { int id = -1; std::string name; @@ -23,6 +30,7 @@ struct MethodInfo { std::string defined_in_type; bool no_virtual = false; bool overrides_method_type_of_parent = false; + std::optional docstring; bool operator==(const MethodInfo& other) const; bool operator!=(const MethodInfo& other) const { return !((*this) == other); } @@ -106,6 +114,8 @@ class Type { bool gen_inspect() const { return m_generate_inspect; } + DefinitionMetadata m_metadata; + protected: Type(std::string parent, std::string name, bool is_boxed, int heap_base); virtual std::string diff_impl(const Type& other) const = 0; @@ -123,13 +133,6 @@ class Type { std::string m_runtime_name; bool m_is_boxed = false; // does this have runtime type information? int m_heap_base = 0; - - // definition information - // TODO - LSP - .gc support - /*std::string m_defining_file; - int m_line_number; - int m_line_offset; - void update_definition_meta(const std::string& defining_file, int line_number, int line_offset);*/ }; /*! diff --git a/common/type_system/TypeSystem.cpp b/common/type_system/TypeSystem.cpp index d9e8d2a1ba..58285efb12 100644 --- a/common/type_system/TypeSystem.cpp +++ b/common/type_system/TypeSystem.cpp @@ -511,11 +511,12 @@ int TypeSystem::get_load_size_allow_partial_def(const TypeSpec& ts) const { MethodInfo TypeSystem::declare_method(const std::string& type_name, const std::string& method_name, + const std::optional& docstring, bool no_virtual, const TypeSpec& ts, bool override_type) { - return declare_method(lookup_type(make_typespec(type_name)), method_name, no_virtual, ts, - override_type); + return declare_method(lookup_type(make_typespec(type_name)), method_name, docstring, no_virtual, + ts, override_type); } /*! @@ -529,6 +530,7 @@ MethodInfo TypeSystem::declare_method(const std::string& type_name, */ MethodInfo TypeSystem::declare_method(Type* type, const std::string& method_name, + const std::optional& docstring, bool no_virtual, const TypeSpec& ts, bool override_type, @@ -537,7 +539,7 @@ MethodInfo TypeSystem::declare_method(Type* type, if (override_type) { throw_typesystem_error("Cannot use :replace option with a new method."); } - return add_new_method(type, ts); + return add_new_method(type, ts, docstring); } // look up the method @@ -557,7 +559,7 @@ MethodInfo TypeSystem::declare_method(Type* type, // use the existing ID. return type->add_method( - {existing_info.id, method_name, ts, type->get_name(), no_virtual, true}); + {existing_info.id, method_name, ts, type->get_name(), no_virtual, true, docstring}); } else { if (got_existing) { // make sure we aren't changing anything. @@ -587,16 +589,17 @@ MethodInfo TypeSystem::declare_method(Type* type, return existing_info; } else { // add a new method! - return type->add_method( - {get_next_method_id(type), method_name, ts, type->get_name(), no_virtual, false}); + return type->add_method({get_next_method_id(type), method_name, ts, type->get_name(), + no_virtual, false, docstring}); } } } MethodInfo TypeSystem::define_method(const std::string& type_name, const std::string& method_name, - const TypeSpec& ts) { - return define_method(lookup_type(make_typespec(type_name)), method_name, ts); + const TypeSpec& ts, + const std::optional& docstring) { + return define_method(lookup_type(make_typespec(type_name)), method_name, ts, docstring); } /*! @@ -610,9 +613,10 @@ MethodInfo TypeSystem::define_method(const std::string& type_name, */ MethodInfo TypeSystem::define_method(Type* type, const std::string& method_name, - const TypeSpec& ts) { + const TypeSpec& ts, + const std::optional& docstring) { if (method_name == "new") { - return add_new_method(type, ts); + return add_new_method(type, ts, docstring); } // look up the method @@ -640,7 +644,9 @@ MethodInfo TypeSystem::define_method(Type* type, * If it turns out that other child methods can specialize arguments (seems like a bad idea), this * may be generalized. */ -MethodInfo TypeSystem::add_new_method(Type* type, const TypeSpec& ts) { +MethodInfo TypeSystem::add_new_method(Type* type, + const TypeSpec& ts, + const std::optional& docstring) { MethodInfo existing; if (type->get_my_new_method(&existing)) { // it exists! @@ -653,7 +659,7 @@ MethodInfo TypeSystem::add_new_method(Type* type, const TypeSpec& ts) { return existing; } else { - return type->add_new_method({0, "new", ts, type->get_name()}); + return type->add_new_method({0, "new", ts, type->get_name(), false, false, docstring}); } } @@ -1038,26 +1044,27 @@ void TypeSystem::add_builtin_types(GameVersion version) { forward_declare_type_as("memory-usage-block", "basic"); // OBJECT - declare_method(obj_type, "new", false, + declare_method(obj_type, "new", {}, false, make_function_typespec({"symbol", "type", "int"}, "_type_"), false); - declare_method(obj_type, "delete", false, make_function_typespec({"_type_"}, "none"), false); - declare_method(obj_type, "print", false, make_function_typespec({"_type_"}, "_type_"), false); - declare_method(obj_type, "inspect", false, make_function_typespec({"_type_"}, "_type_"), false); - declare_method(obj_type, "length", false, make_function_typespec({"_type_"}, "int"), + declare_method(obj_type, "delete", {}, false, make_function_typespec({"_type_"}, "none"), false); + declare_method(obj_type, "print", {}, false, make_function_typespec({"_type_"}, "_type_"), false); + declare_method(obj_type, "inspect", {}, false, make_function_typespec({"_type_"}, "_type_"), + false); + declare_method(obj_type, "length", {}, false, make_function_typespec({"_type_"}, "int"), false); // todo - this integer type? - declare_method(obj_type, "asize-of", false, make_function_typespec({"_type_"}, "int"), false); - declare_method(obj_type, "copy", false, make_function_typespec({"_type_", "symbol"}, "_type_"), - false); - declare_method(obj_type, "relocate", false, make_function_typespec({"_type_", "int"}, "_type_"), - false); - declare_method(obj_type, "mem-usage", false, + declare_method(obj_type, "asize-of", {}, false, make_function_typespec({"_type_"}, "int"), false); + declare_method(obj_type, "copy", {}, false, + make_function_typespec({"_type_", "symbol"}, "_type_"), false); + declare_method(obj_type, "relocate", {}, false, + make_function_typespec({"_type_", "int"}, "_type_"), false); + declare_method(obj_type, "mem-usage", {}, false, make_function_typespec({"_type_", "memory-usage-block", "int"}, "_type_"), false); // STRUCTURE // structure new doesn't support dynamic sizing, which is kinda weird - it grabs the size from // the type. Dynamic structures use new-dynamic-structure, which is used exactly once ever. - declare_method(structure_type, "new", false, make_function_typespec({"symbol", "type"}, "_type_"), - false); + declare_method(structure_type, "new", {}, false, + make_function_typespec({"symbol", "type"}, "_type_"), false); // structure_type is a field-less StructureType, so we have to do this to match the runtime. // structure_type->override_size_in_memory(4); @@ -1066,7 +1073,7 @@ void TypeSystem::add_builtin_types(GameVersion version) { add_field_to_type(basic_type, "type", make_typespec("type")); // the default new basic doesn't support dynamic sizing. anything dynamic will override this // and then call (method object new) to do the dynamically-sized allocation. - declare_method(basic_type, "new", false, make_function_typespec({"symbol", "type"}, "_type_"), + declare_method(basic_type, "new", {}, false, make_function_typespec({"symbol", "type"}, "_type_"), false); // SYMBOL @@ -1075,11 +1082,11 @@ void TypeSystem::add_builtin_types(GameVersion version) { } add_field_to_type(symbol_type, "value", make_typespec("object")); // a new method which returns type none means new is illegal. - declare_method(symbol_type, "new", false, make_function_typespec({}, "none"), false); + declare_method(symbol_type, "new", {}, false, make_function_typespec({}, "none"), false); // TYPE builtin_structure_inherit(type_type); - declare_method(type_type, "new", false, + declare_method(type_type, "new", {}, false, make_function_typespec({"symbol", "type", "int"}, "_type_"), false); add_field_to_type(type_type, "symbol", make_typespec("symbol")); add_field_to_type(type_type, "parent", make_typespec("type")); @@ -1096,7 +1103,7 @@ void TypeSystem::add_builtin_types(GameVersion version) { add_field_to_type(string_type, "data", make_typespec("uint8"), false, true); // todo integer type // string is never deftype'd for the decompiler, so we need to manually give the constructor // type here. - declare_method(string_type, "new", false, + declare_method(string_type, "new", {}, false, make_function_typespec({"symbol", "type", "int", "string"}, "_type_"), false); // FUNCTION @@ -1125,7 +1132,7 @@ void TypeSystem::add_builtin_types(GameVersion version) { // todo builtin_structure_inherit(array_type); - declare_method(array_type, "new", false, + declare_method(array_type, "new", {}, false, make_function_typespec({"symbol", "type", "type", "int"}, "_type_"), false); // array has: number, number, type add_field_to_type(array_type, "length", make_typespec("int32")); @@ -1135,7 +1142,7 @@ void TypeSystem::add_builtin_types(GameVersion version) { // pair pair_type->override_offset(2); - declare_method(pair_type, "new", false, + declare_method(pair_type, "new", {}, false, make_function_typespec({"symbol", "type", "object", "object"}, "_type_"), false); add_field_to_type(pair_type, "car", make_typespec("object")); add_field_to_type(pair_type, "cdr", make_typespec("object")); @@ -1153,7 +1160,7 @@ void TypeSystem::add_builtin_types(GameVersion version) { add_field_to_type(file_stream_type, "mode", make_typespec("symbol")); add_field_to_type(file_stream_type, "name", make_typespec("string")); add_field_to_type(file_stream_type, "file", make_typespec("uint32")); - declare_method(file_stream_type, "new", false, + declare_method(file_stream_type, "new", {}, false, make_function_typespec({"symbol", "type", "string", "symbol"}, "_type_"), false); } @@ -1788,7 +1795,11 @@ std::string TypeSystem::generate_deftype_footer(const Type* type) const { std::string TypeSystem::generate_deftype_for_structure(const StructureType* st) const { std::string result; - result += fmt::format("(deftype {} ({})\n (", st->get_name(), st->get_parent()); + result += fmt::format("(deftype {} ({})\n", st->get_name(), st->get_parent()); + if (st->m_metadata.docstring) { + result += fmt::format(" \"{}\"\n", st->m_metadata.docstring.value()); + } + result += " ("; int longest_field_name = 0; int longest_type_name = 0; @@ -1879,7 +1890,11 @@ std::string TypeSystem::generate_deftype_for_structure(const StructureType* st) std::string TypeSystem::generate_deftype_for_bitfield(const BitFieldType* type) const { std::string result; - result += fmt::format("(deftype {} ({})\n (", type->get_name(), type->get_parent()); + result += fmt::format("(deftype {} ({})\n", type->get_name(), type->get_parent()); + if (type->m_metadata.docstring) { + result += fmt::format(" \"{}\"\n", type->m_metadata.docstring.value()); + } + result += " ("; int longest_field_name = 0; int longest_type_name = 0; diff --git a/common/type_system/TypeSystem.h b/common/type_system/TypeSystem.h index 054a065489..49e5908241 100644 --- a/common/type_system/TypeSystem.h +++ b/common/type_system/TypeSystem.h @@ -162,20 +162,28 @@ class TypeSystem { MethodInfo declare_method(const std::string& type_name, const std::string& method_name, + const std::optional& docstring, bool no_virtual, const TypeSpec& ts, bool override_type); MethodInfo declare_method(Type* type, const std::string& method_name, + const std::optional& docstring, bool no_virtual, const TypeSpec& ts, bool override_type, int id = -1); MethodInfo define_method(const std::string& type_name, const std::string& method_name, - const TypeSpec& ts); - MethodInfo define_method(Type* type, const std::string& method_name, const TypeSpec& ts); - MethodInfo add_new_method(Type* type, const TypeSpec& ts); + const TypeSpec& ts, + const std::optional& docstring); + MethodInfo define_method(Type* type, + const std::string& method_name, + const TypeSpec& ts, + const std::optional& docstring); + MethodInfo add_new_method(Type* type, + const TypeSpec& ts, + const std::optional& docstring); MethodInfo lookup_method(const std::string& type_name, const std::string& method_name) const; MethodInfo lookup_method(const std::string& type_name, int method_id) const; bool try_lookup_method(const Type* type, const std::string& method_name, MethodInfo* info) const; diff --git a/common/type_system/defenum.cpp b/common/type_system/defenum.cpp index b6faf17c02..bc5efa69df 100644 --- a/common/type_system/defenum.cpp +++ b/common/type_system/defenum.cpp @@ -43,7 +43,9 @@ std::string symbol_string(const goos::Object& obj) { } // namespace -EnumType* parse_defenum(const goos::Object& defenum, TypeSystem* ts) { +EnumType* parse_defenum(const goos::Object& defenum, + TypeSystem* ts, + DefinitionMetadata* symbol_metadata) { // default enum type will be int32. TypeSpec base_type = ts->make_typespec("int32"); bool is_bitfield = false; @@ -53,6 +55,14 @@ EnumType* parse_defenum(const goos::Object& defenum, TypeSystem* ts) { auto& enum_name_obj = car(iter); iter = cdr(iter); + // check for docstring + if (iter->is_pair() && car(iter).is_string()) { + // TODO - docstring - store and use docstring if coming from the compiler + if (symbol_metadata) { + symbol_metadata->docstring = car(iter).as_string()->data; + } + iter = cdr(iter); + } if (!enum_name_obj.is_symbol()) { throw std::runtime_error("defenum must be given a symbol as its name"); diff --git a/common/type_system/defenum.h b/common/type_system/defenum.h index 5ca25c6dec..97a60099d7 100644 --- a/common/type_system/defenum.h +++ b/common/type_system/defenum.h @@ -10,4 +10,6 @@ #include "common/goos/Object.h" -EnumType* parse_defenum(const goos::Object& defenum, TypeSystem* ts); +EnumType* parse_defenum(const goos::Object& defenum, + TypeSystem* ts, + DefinitionMetadata* symbol_metadata); diff --git a/common/type_system/deftype.cpp b/common/type_system/deftype.cpp index 3880214822..38ebc6e36b 100644 --- a/common/type_system/deftype.cpp +++ b/common/type_system/deftype.cpp @@ -201,6 +201,12 @@ void declare_method(Type* type, TypeSystem* type_system, const goos::Object& def // (name args return-type [:no-virtual] [:replace] [:state] [id]) auto method_name = symbol_string(car(obj)); obj = cdr(obj); + // check for docstring + std::optional docstring; + if (obj->is_pair() && car(obj).is_string()) { + docstring = car(obj).as_string()->data; + obj = cdr(obj); + } auto& args = car(obj); obj = cdr(obj); auto& return_type = car(obj); @@ -247,8 +253,8 @@ void declare_method(Type* type, TypeSystem* type_system, const goos::Object& def }); function_typespec.add_arg(parse_typespec(type_system, return_type)); - auto info = type_system->declare_method(type, method_name, no_virtual, function_typespec, - replace_method, id); + auto info = type_system->declare_method(type, method_name, docstring, no_virtual, + function_typespec, replace_method, id); // check the method assert if (id != -1) { @@ -566,6 +572,7 @@ TypeSpec parse_typespec(const TypeSystem* type_system, const goos::Object& src) DeftypeResult parse_deftype(const goos::Object& deftype, TypeSystem* ts, std::unordered_map* constants) { + DefinitionMetadata symbol_metadata; std::unordered_map no_consts; auto& constants_to_use = no_consts; if (constants != nullptr) { @@ -578,6 +585,11 @@ DeftypeResult parse_deftype(const goos::Object& deftype, iter = cdr(iter); auto& parent_list_obj = car(iter); iter = cdr(iter); + // check for docstring + if (iter->is_pair() && car(iter).is_string()) { + symbol_metadata.docstring = car(iter).as_string()->data; + iter = cdr(iter); + } auto& field_list_obj = car(iter); iter = cdr(iter); auto& options_obj = *iter; @@ -593,6 +605,7 @@ DeftypeResult parse_deftype(const goos::Object& deftype, if (is_type("basic", parent_type, ts)) { auto new_type = std::make_unique(parent_type_name, name, false, 0); + new_type->m_metadata = symbol_metadata; auto pto = dynamic_cast(ts->lookup_type(parent_type)); ASSERT(pto); if (pto->final()) { @@ -630,6 +643,7 @@ DeftypeResult parse_deftype(const goos::Object& deftype, ts->add_type(name, std::move(new_type)); } else if (is_type("structure", parent_type, ts)) { auto new_type = std::make_unique(parent_type_name, name, false, false, false, 0); + new_type->m_metadata = symbol_metadata; auto pto = dynamic_cast(ts->lookup_type(parent_type)); ASSERT(pto); new_type->inherit(pto); @@ -658,6 +672,7 @@ DeftypeResult parse_deftype(const goos::Object& deftype, ASSERT(pto); auto new_type = std::make_unique( parent_type_name, name, pto->get_size_in_memory(), pto->get_load_signed()); + new_type->m_metadata = symbol_metadata; auto parent_value = dynamic_cast(pto); ASSERT(parent_value); new_type->inherit(parent_value); diff --git a/decompiler/IR2/Form.cpp b/decompiler/IR2/Form.cpp index d312987516..f1857207f7 100644 --- a/decompiler/IR2/Form.cpp +++ b/decompiler/IR2/Form.cpp @@ -464,22 +464,25 @@ goos::Object SetFormFormElement::to_form_internal(const Env& env) const { } } -goos::Object SetFormFormElement::to_form_for_define(const Env& env) const { +goos::Object SetFormFormElement::to_form_for_define( + const Env& env, + const std::optional& docstring) const { + std::vector forms = {pretty_print::to_symbol("define"), m_dst->to_form(env)}; + if (docstring) { + forms.push_back(pretty_print::to_symbol(fmt::format("\"{}\"", docstring.value()))); + } if (m_cast_for_define) { // for vu-function, we just put a 0. These aren't supported if (*m_cast_for_define == TypeSpec("vu-function")) { - return pretty_print::build_list( - fmt::format("define"), m_dst->to_form(env), - pretty_print::build_list(fmt::format("the-as {}", m_cast_for_define->print()), - pretty_print::to_symbol("0"))); + forms.push_back(pretty_print::build_list(fmt::format("the-as {}", m_cast_for_define->print()), + pretty_print::to_symbol("0"))); + return pretty_print::build_list(forms); } - return pretty_print::build_list( - fmt::format("define"), m_dst->to_form(env), - pretty_print::build_list(fmt::format("the-as {}", m_cast_for_define->print()), - m_src->to_form(env))); + forms.push_back(pretty_print::build_list(fmt::format("the-as {}", m_cast_for_define->print()), + m_src->to_form(env))); + return pretty_print::build_list(forms); } else { - std::vector forms = {pretty_print::to_symbol("define"), m_dst->to_form(env), - m_src->to_form(env)}; + forms.push_back(m_src->to_form(env)); return pretty_print::build_list(forms); } } diff --git a/decompiler/IR2/Form.h b/decompiler/IR2/Form.h index d7fd877143..5a3f41d3c0 100644 --- a/decompiler/IR2/Form.h +++ b/decompiler/IR2/Form.h @@ -410,7 +410,8 @@ class SetFormFormElement : public FormElement { std::optional cast_for_set = {}, std::optional cast_for_define = {}); goos::Object to_form_internal(const Env& env) const override; - goos::Object to_form_for_define(const Env& env) const; + goos::Object to_form_for_define(const Env& env, + const std::optional& docstring) const; void apply(const std::function& f) override; void apply_form(const std::function& f) override; bool is_sequence_point() const override; diff --git a/decompiler/analysis/final_output.cpp b/decompiler/analysis/final_output.cpp index df6795c7b9..ff3d6ff6d1 100644 --- a/decompiler/analysis/final_output.cpp +++ b/decompiler/analysis/final_output.cpp @@ -111,6 +111,15 @@ std::string final_defun_out(const Function& func, top.push_back(arguments); auto top_form = pretty_print::build_list(top); + // docstring if available + if (dts.symbol_metadata_map.count(func.name()) != 0) { + auto meta = dts.symbol_metadata_map.at(func.name()); + if (meta.docstring) { + inline_body.insert(inline_body.begin(), + pretty_print::to_symbol(fmt::format("\"{}\"", meta.docstring.value()))); + } + } + append_body_to_function_definition(&top_form, inline_body, var_dec, func.type); return pretty_print::to_string(top_form); } @@ -126,6 +135,10 @@ std::string final_defun_out(const Function& func, top.push_back(arguments); auto top_form = pretty_print::build_list(top); + if (method_info.docstring) { + inline_body.insert(inline_body.begin(), pretty_print::to_symbol(fmt::format( + "\"{}\"", method_info.docstring.value()))); + } append_body_to_function_definition(&top_form, inline_body, var_dec, method_info.type); return pretty_print::to_string(top_form); } @@ -417,7 +430,13 @@ std::string write_from_top_level_form(Form* top_form, fmt::format(";; definition for symbol {}, type {}\n", sym_name, symbol_type.print()); auto setset = dynamic_cast(f.try_as_single_element()); ASSERT(setset); - result += pretty_print::to_string(setset->to_form_for_define(env)); + if (dts.symbol_metadata_map.count(sym_name) != 0) { + result += pretty_print::to_string( + setset->to_form_for_define(env, dts.symbol_metadata_map.at(sym_name).docstring)); + } else { + result += pretty_print::to_string(setset->to_form_for_define(env, {})); + } + result += "\n\n"; } } diff --git a/decompiler/util/DecompilerTypeSystem.cpp b/decompiler/util/DecompilerTypeSystem.cpp index bfc3008606..9f18c8fb1a 100644 --- a/decompiler/util/DecompilerTypeSystem.cpp +++ b/decompiler/util/DecompilerTypeSystem.cpp @@ -54,28 +54,32 @@ void DecompilerTypeSystem::parse_type_defs(const std::vector& file_ for_each_in_list(data, [&](goos::Object& o) { try { if (car(o).as_symbol()->name == "define-extern") { + auto symbol_metadata = DefinitionMetadata(); auto* rest = &cdr(o); auto sym_name = car(*rest); rest = &cdr(*rest); + // check for docstring + if (rest->is_pair() && car(*rest).is_string()) { + symbol_metadata.docstring = car(*rest).as_string()->data; + rest = &cdr(*rest); + } auto sym_type = car(*rest); if (!cdr(*rest).is_empty_list()) { throw std::runtime_error("malformed define-extern"); } - auto info = m_reader.db.get_short_info_for(o); - add_symbol(sym_name.as_symbol()->name, parse_typespec(&ts, sym_type), info); - + symbol_metadata.definition_info = m_reader.db.get_short_info_for(o); + add_symbol(sym_name.as_symbol()->name, parse_typespec(&ts, sym_type), symbol_metadata); } else if (car(o).as_symbol()->name == "deftype") { auto dtr = parse_deftype(cdr(o), &ts); - auto info = m_reader.db.get_short_info_for(o); + dtr.type_info->m_metadata.definition_info = m_reader.db.get_short_info_for(o); if (dtr.create_runtime_type) { - add_symbol(dtr.type.base_type(), "type", info); + add_symbol(dtr.type.base_type(), "type", dtr.type_info->m_metadata); } // declare the type's states globally for (auto& state : dtr.type_info->get_states_declared_for_type()) { // TODO - get definition info for the state definitions specifically - add_symbol(state.first, state.second, info); + add_symbol(state.first, state.second, dtr.type_info->m_metadata); } - } else if (car(o).as_symbol()->name == "declare-type") { auto* rest = &cdr(o); auto type_name = car(*rest); @@ -86,7 +90,12 @@ void DecompilerTypeSystem::parse_type_defs(const std::vector& file_ } ts.forward_declare_type_as(type_name.as_symbol()->name, type_kind.as_symbol()->name); } else if (car(o).as_symbol()->name == "defenum") { - parse_defenum(cdr(o), &ts); + auto symbol_metadata = DefinitionMetadata(); + parse_defenum(cdr(o), &ts, &symbol_metadata); + symbol_metadata.definition_info = m_reader.db.get_short_info_for(o); + auto* rest = &cdr(o); + const auto& enum_name = car(*rest).as_symbol()->name; + symbol_metadata_map[enum_name] = symbol_metadata; // so far, enums are never runtime types so there's no symbol for them. } else { throw std::runtime_error("Decompiler cannot parse " + car(o).print()); @@ -161,16 +170,16 @@ bool DecompilerTypeSystem::lookup_flags(const std::string& type, u64* dest) cons return false; } -void DecompilerTypeSystem::add_symbol( - const std::string& name, - const TypeSpec& type_spec, - const std::optional& definition_info) { +void DecompilerTypeSystem::add_symbol(const std::string& name, + const TypeSpec& type_spec, + const DefinitionMetadata& symbol_metadata) { add_symbol(name); auto skv = symbol_types.find(name); if (skv == symbol_types.end() || skv->second == type_spec) { symbol_types[name] = type_spec; - if (definition_info) { - symbol_definition_info[name] = definition_info.value(); + // TODO - could get rid of this if there is a way to go from TypeSpec -> full Type + if (symbol_metadata.definition_info) { + symbol_metadata_map[name] = symbol_metadata; } } else { if (ts.tc(type_spec, skv->second)) { diff --git a/decompiler/util/DecompilerTypeSystem.h b/decompiler/util/DecompilerTypeSystem.h index 76f02d5034..18f567b2bc 100644 --- a/decompiler/util/DecompilerTypeSystem.h +++ b/decompiler/util/DecompilerTypeSystem.h @@ -14,10 +14,12 @@ class DecompilerTypeSystem { public: DecompilerTypeSystem(GameVersion version); TypeSystem ts; + std::unordered_map symbol_types; std::unordered_set symbols; - std::unordered_map symbol_definition_info; + std::unordered_map symbol_metadata_map; std::vector symbol_add_order; + std::unordered_map type_flags; std::unordered_map type_parents; std::unordered_map bad_format_strings; @@ -34,13 +36,13 @@ class DecompilerTypeSystem { void add_symbol(const std::string& name, const std::string& base_type, - const std::optional& definition_info) { - add_symbol(name, TypeSpec(base_type), definition_info); + const DefinitionMetadata& symbol_metadata) { + add_symbol(name, TypeSpec(base_type), symbol_metadata); } void add_symbol(const std::string& name, const TypeSpec& type_spec, - const std::optional& definition_info); + const DefinitionMetadata& symbol_metadata); void parse_type_defs(const std::vector& file_path); TypeSpec parse_type_spec(const std::string& str) const; void add_type_flags(const std::string& name, u64 flags); diff --git a/goalc/compiler/compilation/Define.cpp b/goalc/compiler/compilation/Define.cpp index 2d3603a57e..4b9e4d64ab 100644 --- a/goalc/compiler/compilation/Define.cpp +++ b/goalc/compiler/compilation/Define.cpp @@ -13,6 +13,13 @@ */ Val* Compiler::compile_define(const goos::Object& form, const goos::Object& rest, Env* env) { auto args = get_va(form, rest); + // Grab the docstring (if it's there) and then rip it out so we can do the normal validation + if (args.unnamed.size() == 3 && args.unnamed.at(1).is_string()) { + // TODO - docstring - actually use it! + // std::string docstring = args.unnamed.at(1).as_string()->data; + args.unnamed.erase(args.unnamed.begin() + 1); + } + va_check(form, args, {goos::ObjectType::SYMBOL, {}}, {{"no-typecheck", {false, goos::ObjectType::SYMBOL}}}); auto& sym = args.unnamed.at(0); @@ -73,6 +80,12 @@ Val* Compiler::compile_define(const goos::Object& form, const goos::Object& rest Val* Compiler::compile_define_extern(const goos::Object& form, const goos::Object& rest, Env* env) { (void)env; auto args = get_va(form, rest); + // Grab the docstring (if it's there) and then rip it out so we can do the normal validation + if (args.unnamed.size() == 3 && args.unnamed.at(1).is_string()) { + // TODO - docstring - actually use it! + // std::string docstring = args.unnamed.at(1).as_string()->data; + args.unnamed.erase(args.unnamed.begin() + 1); + } va_check(form, args, {goos::ObjectType::SYMBOL, {}}, {}); auto& sym = args.unnamed.at(0); auto& typespec = args.unnamed.at(1); diff --git a/goalc/compiler/compilation/Macro.cpp b/goalc/compiler/compilation/Macro.cpp index ed68e198e9..b2cf1580e4 100644 --- a/goalc/compiler/compilation/Macro.cpp +++ b/goalc/compiler/compilation/Macro.cpp @@ -166,6 +166,14 @@ Val* Compiler::compile_define_constant(const goos::Object& form, auto sym = pair_car(*rest).as_symbol(); rest = &pair_cdr(*rest); + + // check for potential docstring + // TODO - docstring - actually do something with this! + if (rest->is_pair() && pair_car(*rest).is_string() && !pair_cdr(*rest).is_empty_list()) { + // std::string docstring = pair_car(*rest).as_string()->data; + rest = &pair_cdr(*rest); + } + auto value = pair_car(*rest); rest = &rest->as_pair()->cdr; diff --git a/goalc/compiler/compilation/Type.cpp b/goalc/compiler/compilation/Type.cpp index bbc3144bf3..660362d2c4 100644 --- a/goalc/compiler/compilation/Type.cpp +++ b/goalc/compiler/compilation/Type.cpp @@ -479,7 +479,7 @@ Val* Compiler::compile_defmethod(const goos::Object& form, const goos::Object& _ // todo, verify argument list types (check that first arg is _type_ for methods that aren't "new") lambda.debug_name = fmt::format("(method {} {})", method_name.print(), type_name.print()); - // skip docstring + // TODO - docstring - do something with the docstring! if (body->as_pair()->car.is_string() && !body->as_pair()->cdr.is_empty_list()) { body = &pair_cdr(*body); } @@ -613,7 +613,9 @@ Val* Compiler::compile_defmethod(const goos::Object& form, const goos::Object& _ m_symbol_info.add_method(symbol_string(method_name), symbol_string(type_name), form); - auto info = m_ts.define_method(symbol_string(type_name), symbol_string(method_name), lambda_ts); + // TODO! + auto info = + m_ts.define_method(symbol_string(type_name), symbol_string(method_name), lambda_ts, {}); auto type_obj = compile_get_symbol_value(form, symbol_string(type_name), env)->to_gpr(form, env); auto id_val = compile_integer(info.id, env)->to_gpr(form, env); auto method_val = place->to_gpr(form, env); @@ -1327,7 +1329,7 @@ Val* Compiler::compile_defenum(const goos::Object& form, const goos::Object& res (void)form; (void)env; - parse_defenum(rest, &m_ts); + parse_defenum(rest, &m_ts, {}); return get_none(); } diff --git a/lsp/state/workspace.cpp b/lsp/state/workspace.cpp index 18d20d176c..8459edac22 100644 --- a/lsp/state/workspace.cpp +++ b/lsp/state/workspace.cpp @@ -40,10 +40,10 @@ std::optional Workspace::get_symbol_info_from_all_types return {}; } const auto& dts = m_tracked_all_types_files[all_types_uri].m_dts; - if (dts.symbol_definition_info.count(symbol_name) == 0) { + if (dts.symbol_metadata_map.count(symbol_name) == 0) { return {}; } - return dts.symbol_definition_info.at(symbol_name); + return dts.symbol_metadata_map.at(symbol_name).definition_info; } void Workspace::start_tracking_file(const LSPSpec::DocumentUri& file_uri, diff --git a/test/test_type_system.cpp b/test/test_type_system.cpp index b6da5d6b0b..7a6541b9da 100644 --- a/test/test_type_system.cpp +++ b/test/test_type_system.cpp @@ -231,28 +231,32 @@ TEST(TypeSystem, AddMethodAndLookupMethod) { TypeSystem ts; ts.add_builtin_types(GameVersion::Jak1); - auto parent_info = ts.declare_method(ts.lookup_type("structure"), "test-method-1", false, - ts.make_function_typespec({"integer"}, "string"), false); + auto parent_info = + ts.declare_method(ts.lookup_type("structure"), "test-method-1", "test docstring", false, + ts.make_function_typespec({"integer"}, "string"), false); // when trying to add the same method to a child, should return the parent's method - auto child_info_same = ts.declare_method(ts.lookup_type("basic"), "test-method-1", false, - ts.make_function_typespec({"integer"}, "string"), false); + auto child_info_same = + ts.declare_method(ts.lookup_type("basic"), "test-method-1", "test docstring", false, + ts.make_function_typespec({"integer"}, "string"), false); EXPECT_EQ(parent_info.id, child_info_same.id); EXPECT_EQ(parent_info.id, GOAL_MEMUSAGE_METHOD + 1); // any amount of fiddling with method types should cause an error - EXPECT_ANY_THROW(ts.declare_method(ts.lookup_type("basic"), "test-method-1", false, - ts.make_function_typespec({"integer"}, "integer"), false)); - EXPECT_ANY_THROW(ts.declare_method(ts.lookup_type("basic"), "test-method-1", false, - ts.make_function_typespec({}, "string"), false)); - EXPECT_ANY_THROW(ts.declare_method(ts.lookup_type("basic"), "test-method-1", false, - ts.make_function_typespec({"integer", "string"}, "string"), + EXPECT_ANY_THROW(ts.declare_method(ts.lookup_type("basic"), "test-method-1", "test docstring", + false, ts.make_function_typespec({"integer"}, "integer"), + false)); + EXPECT_ANY_THROW(ts.declare_method(ts.lookup_type("basic"), "test-method-1", "test docstring", + false, ts.make_function_typespec({}, "string"), false)); + EXPECT_ANY_THROW( + ts.declare_method(ts.lookup_type("basic"), "test-method-1", "test docstring", false, + ts.make_function_typespec({"integer", "string"}, "string"), false)); + EXPECT_ANY_THROW(ts.declare_method(ts.lookup_type("basic"), "test-method-1", "test docstring", + false, ts.make_function_typespec({"string"}, "string"), false)); - EXPECT_ANY_THROW(ts.declare_method(ts.lookup_type("basic"), "test-method-1", false, - ts.make_function_typespec({"string"}, "string"), false)); - ts.declare_method(ts.lookup_type("basic"), "test-method-2", false, + ts.declare_method(ts.lookup_type("basic"), "test-method-2", "test docstring", false, ts.make_function_typespec({"integer"}, "string"), false); EXPECT_EQ(parent_info.id, ts.lookup_method("basic", "test-method-1").id); @@ -275,10 +279,10 @@ TEST(TypeSystem, NewMethod) { TypeSystem ts; ts.add_builtin_types(GameVersion::Jak1); ts.add_type("test-1", std::make_unique("basic", "test-1", false, 0)); - ts.declare_method(ts.lookup_type("test-1"), "new", false, + ts.declare_method(ts.lookup_type("test-1"), "new", "test docstring", false, ts.make_function_typespec({"symbol", "string"}, "test-1"), false); ts.add_type("test-2", std::make_unique("test-1", "test-2", false, 0)); - ts.declare_method(ts.lookup_type("test-2"), "new", false, + ts.declare_method(ts.lookup_type("test-2"), "new", "test docstring", false, ts.make_function_typespec({"symbol", "string", "symbol"}, "test-2"), false); EXPECT_EQ(ts.lookup_method("test-1", "new").type.print(), "(function symbol string test-1)"); @@ -297,7 +301,7 @@ TEST(TypeSystem, MethodSubstitute) { TypeSystem ts; ts.add_builtin_types(GameVersion::Jak1); ts.add_type("test-1", std::make_unique("basic", "test-1", false, 0)); - ts.declare_method(ts.lookup_type("test-1"), "new", false, + ts.declare_method(ts.lookup_type("test-1"), "new", "test docstring", false, ts.make_function_typespec({"symbol", "string", "_type_"}, "_type_"), false); auto final_type = ts.lookup_method("test-1", "new").type.substitute_for_method_call("test-1");