decomp: add docstring support to relevant places in all-types (#1753)

* decomp: add `docstring` support to relevant places in `all-types`

* decomp: output method docstring into the `defmethod` instead

* goalc: handle docstrings in `define[-extern]` (gracefully ignore for now)

* decomp: output docstrings for bitfield deftypes too

* goalc: fix `defenum` parsing when coming from the compiler (no symbol metadata to store to)

* lsp/tests: fix ups

* lint: formatting

* goalc: handle edge-case of defining a string constant

* cleanup leftovers, fix codacy issues, rename struct
This commit is contained in:
Tyler Wilding
2022-08-23 00:32:07 -04:00
committed by GitHub
parent 4164a755b1
commit 090b2984a2
17 changed files with 221 additions and 122 deletions
+7 -22
View File
@@ -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",
+10 -7
View File
@@ -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<goos::TextDb::ShortInfo> definition_info;
std::optional<std::string> 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<std::string> 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);*/
};
/*!
+49 -34
View File
@@ -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<std::string>& 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<std::string>& 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<std::string>& 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<std::string>& 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<std::string>& 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;
+11 -3
View File
@@ -162,20 +162,28 @@ class TypeSystem {
MethodInfo declare_method(const std::string& type_name,
const std::string& method_name,
const std::optional<std::string>& docstring,
bool no_virtual,
const TypeSpec& ts,
bool override_type);
MethodInfo declare_method(Type* type,
const std::string& method_name,
const std::optional<std::string>& 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<std::string>& docstring);
MethodInfo define_method(Type* type,
const std::string& method_name,
const TypeSpec& ts,
const std::optional<std::string>& docstring);
MethodInfo add_new_method(Type* type,
const TypeSpec& ts,
const std::optional<std::string>& 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;
+11 -1
View File
@@ -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");
+3 -1
View File
@@ -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);
+17 -2
View File
@@ -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<std::string> 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<goos::HeapObject*, goos::Object>* constants) {
DefinitionMetadata symbol_metadata;
std::unordered_map<goos::HeapObject*, goos::Object> 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<BasicType>(parent_type_name, name, false, 0);
new_type->m_metadata = symbol_metadata;
auto pto = dynamic_cast<BasicType*>(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<StructureType>(parent_type_name, name, false, false, false, 0);
new_type->m_metadata = symbol_metadata;
auto pto = dynamic_cast<StructureType*>(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<BitFieldType>(
parent_type_name, name, pto->get_size_in_memory(), pto->get_load_signed());
new_type->m_metadata = symbol_metadata;
auto parent_value = dynamic_cast<ValueType*>(pto);
ASSERT(parent_value);
new_type->inherit(parent_value);
+14 -11
View File
@@ -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<std::string>& docstring) const {
std::vector<goos::Object> 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<goos::Object> 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);
}
}
+2 -1
View File
@@ -410,7 +410,8 @@ class SetFormFormElement : public FormElement {
std::optional<TypeSpec> cast_for_set = {},
std::optional<TypeSpec> 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<std::string>& docstring) const;
void apply(const std::function<void(FormElement*)>& f) override;
void apply_form(const std::function<void(Form*)>& f) override;
bool is_sequence_point() const override;
+20 -1
View File
@@ -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<SetFormFormElement*>(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";
}
}
+23 -14
View File
@@ -54,28 +54,32 @@ void DecompilerTypeSystem::parse_type_defs(const std::vector<std::string>& 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<std::string>& 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<goos::TextDb::ShortInfo>& 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)) {
+6 -4
View File
@@ -14,10 +14,12 @@ class DecompilerTypeSystem {
public:
DecompilerTypeSystem(GameVersion version);
TypeSystem ts;
std::unordered_map<std::string, TypeSpec> symbol_types;
std::unordered_set<std::string> symbols;
std::unordered_map<std::string, goos::TextDb::ShortInfo> symbol_definition_info;
std::unordered_map<std::string, DefinitionMetadata> symbol_metadata_map;
std::vector<std::string> symbol_add_order;
std::unordered_map<std::string, u64> type_flags;
std::unordered_map<std::string, std::string> type_parents;
std::unordered_map<std::string, int> bad_format_strings;
@@ -34,13 +36,13 @@ class DecompilerTypeSystem {
void add_symbol(const std::string& name,
const std::string& base_type,
const std::optional<goos::TextDb::ShortInfo>& 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<goos::TextDb::ShortInfo>& definition_info);
const DefinitionMetadata& symbol_metadata);
void parse_type_defs(const std::vector<std::string>& file_path);
TypeSpec parse_type_spec(const std::string& str) const;
void add_type_flags(const std::string& name, u64 flags);
+13
View File
@@ -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);
+8
View File
@@ -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;
+5 -3
View File
@@ -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();
}
+2 -2
View File
@@ -40,10 +40,10 @@ std::optional<goos::TextDb::ShortInfo> 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,
+20 -16
View File
@@ -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<BasicType>("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<BasicType>("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<BasicType>("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");