diff --git a/common/goal_constants.h b/common/goal_constants.h index bf9239e322..88d6896e2f 100644 --- a/common/goal_constants.h +++ b/common/goal_constants.h @@ -1,16 +1,24 @@ #ifndef JAK_GOAL_CONSTANTS_H #define JAK_GOAL_CONSTANTS_H +#include "common_types.h" + +constexpr s32 BINTEGER_OFFSET = 0; +constexpr s32 PAIR_OFFSET = 2; constexpr int POINTER_SIZE = 4; constexpr int BASIC_OFFSET = 4; constexpr int STRUCTURE_ALIGNMENT = 16; -enum class RegKind { - GPR_64, - FLOAT, - INT_128, - FLOAT_4X, - INVALID -}; +enum class RegKind { GPR_64, FLOAT, INT_128, FLOAT_4X, INVALID }; + +constexpr u32 GOAL_NEW_METHOD = 0; // method ID of GOAL new +constexpr u32 GOAL_DEL_METHOD = 1; // method ID of GOAL delete +constexpr u32 GOAL_PRINT_METHOD = 2; // method ID of GOAL print +constexpr u32 GOAL_INSPECT_METHOD = 3; // method ID of GOAL inspect +constexpr u32 GOAL_LENGTH_METHOD = 4; // method ID of GOAL length +constexpr u32 GOAL_ASIZE_METHOD = 5; // method ID of GOAL size +constexpr u32 GOAL_COPY_METHOD = 6; // method ID of GOAL copy +constexpr u32 GOAL_RELOC_METHOD = 7; // method ID of GOAL relocate +constexpr u32 GOAL_MEMUSAGE_METHOD = 8; // method ID of GOAL mem-usage #endif // JAK_GOAL_CONSTANTS_H diff --git a/common/type_system/Type.cpp b/common/type_system/Type.cpp index 3dd0fe1a80..189798c5eb 100644 --- a/common/type_system/Type.cpp +++ b/common/type_system/Type.cpp @@ -137,7 +137,7 @@ bool Type::is_equal(const Type& other) const { * parents. */ bool Type::has_parent() const { - return m_parent != "object" && !m_parent.empty(); + return m_name != "object" && !m_parent.empty(); } /*! @@ -218,52 +218,53 @@ std::string Type::print_method_info() const { } ///////////// -// NoneType +// NullType ///////////// -// Special Type representing nothing. -// it's an error to try to do anything with None. +// Special Type for both "none" and "_type_" types +// it's an error to try to do anything with Null. -NoneType::NoneType() : Type("", "none", false) {} +NullType::NullType(std::string name) : Type("", std::move(name), false) {} -bool NoneType::is_reference() const { - throw std::runtime_error("is_reference called on NoneType"); +bool NullType::is_reference() const { + throw std::runtime_error("is_reference called on NullType"); } -int NoneType::get_load_size() const { - throw std::runtime_error("get_load_size called on NoneType"); +int NullType::get_load_size() const { + throw std::runtime_error("get_load_size called on NullType"); } -bool NoneType::get_load_signed() const { - throw std::runtime_error("get_load_size called on NoneType"); +bool NullType::get_load_signed() const { + throw std::runtime_error("get_load_size called on NullType"); } -int NoneType::get_size_in_memory() const { - throw std::runtime_error("get_size_in_memory called on NoneType"); +int NullType::get_size_in_memory() const { + throw std::runtime_error("get_size_in_memory called on NullType"); } -RegKind NoneType::get_preferred_reg_kind() const { - throw std::runtime_error("get_preferred_reg_kind called on NoneType"); +RegKind NullType::get_preferred_reg_kind() const { + throw std::runtime_error("get_preferred_reg_kind called on NullType"); } -int NoneType::get_offset() const { +int NullType::get_offset() const { throw std::runtime_error("get_offset called on NoneType"); } -int NoneType::get_in_memory_alignment() const { - throw std::runtime_error("get_in_memory_alignment called on NoneType"); +int NullType::get_in_memory_alignment() const { + throw std::runtime_error("get_in_memory_alignment called on NullType"); } -int NoneType::get_inline_array_alignment() const { - throw std::runtime_error("get_inline_array_alignment called on NoneType"); +int NullType::get_inline_array_alignment() const { + throw std::runtime_error("get_inline_array_alignment called on NullType"); } -std::string NoneType::print() const { - return "none"; +std::string NullType::print() const { + return m_name; } -bool NoneType::operator==(const Type& other) const { - // there should be only one none type, so this is safe. +bool NullType::operator==(const Type& other) const { + // any redefinition by the user should be invalid, so this will always return false unless + // you're calling it on the same object. return this == &other; } diff --git a/common/type_system/Type.h b/common/type_system/Type.h index 5ce8dd69d0..162421ca8a 100644 --- a/common/type_system/Type.h +++ b/common/type_system/Type.h @@ -87,9 +87,9 @@ class Type { * Used only for "none" - this is a type that the compiler can use for "this has no value". * Attempting to do anything with a NoneType is an error. */ -class NoneType : public Type { +class NullType : public Type { public: - NoneType(); + NullType(std::string name); bool is_reference() const override; int get_load_size() const override; bool get_load_signed() const override; @@ -100,7 +100,7 @@ class NoneType : public Type { int get_in_memory_alignment() const override; std::string print() const override; bool operator==(const Type& other) const override; - ~NoneType() = default; + ~NullType() = default; }; /*! @@ -217,6 +217,9 @@ class StructureType : public ReferenceType { int get_in_memory_alignment() const override; int get_inline_array_alignment() const override; bool lookup_field(const std::string& name, Field* out); + bool is_dynamic() const { + return m_dynamic; + } ~StructureType() = default; protected: diff --git a/common/type_system/TypeSpec.cpp b/common/type_system/TypeSpec.cpp index f52cd558c3..6ddd8cca4d 100644 --- a/common/type_system/TypeSpec.cpp +++ b/common/type_system/TypeSpec.cpp @@ -34,4 +34,13 @@ bool TypeSpec::operator==(const TypeSpec& other) const { } return true; +} + +TypeSpec TypeSpec::substitute_for_method_call(const std::string& method_type) const { + TypeSpec result; + result.m_type = (m_type == "_type_") ? method_type : m_type; + for(const auto& x : m_arguments) { + result.m_arguments.push_back(x.substitute_for_method_call(method_type)); + } + return result; } \ No newline at end of file diff --git a/common/type_system/TypeSpec.h b/common/type_system/TypeSpec.h index cb42ba788b..892a364a37 100644 --- a/common/type_system/TypeSpec.h +++ b/common/type_system/TypeSpec.h @@ -33,11 +33,18 @@ class TypeSpec { void add_arg(const TypeSpec& ts) { m_arguments.push_back(ts); } const std::string base_type() const { return m_type; } + + bool has_single_arg() const { + return m_arguments.size() == 1; + } + const TypeSpec& get_single_arg() const { assert(m_arguments.size() == 1); return m_arguments.front(); } + TypeSpec substitute_for_method_call(const std::string& method_type) const; + private: std::string m_type; std::vector m_arguments; diff --git a/common/type_system/TypeSystem.cpp b/common/type_system/TypeSystem.cpp index 76e4005c59..3ad371cf21 100644 --- a/common/type_system/TypeSystem.cpp +++ b/common/type_system/TypeSystem.cpp @@ -5,8 +5,9 @@ #include TypeSystem::TypeSystem() { - // the "none" type is included by default. - add_type("none", std::make_unique()); + // the "none" and "_type_" types are included by default. + add_type("none", std::make_unique("none")); + add_type("_type_", std::make_unique("_type_")); } /*! @@ -39,7 +40,7 @@ Type* TypeSystem::add_type(const std::string& name, std::unique_ptr type) // newly defined! // none/object get to skip these checks because they are roots. - if (name != "object" && name != "none") { + if (name != "object" && name != "none" && name != "_type_") { if (m_forward_declared_types.find(type->get_parent()) != m_forward_declared_types.end()) { fmt::print("[TypeSystem] Type {} has incompletely defined parent {}\n", type->get_name(), type->get_parent()); @@ -83,11 +84,24 @@ std::string TypeSystem::get_runtime_type(const TypeSpec& ts) { DerefInfo TypeSystem::get_deref_info(const TypeSpec& ts) { DerefInfo info; + if(!ts.has_single_arg()) { + // not enough info. + info.can_deref = false; + return info; + } + // default to GPR info.reg = RegKind::GPR_64; info.mem_deref = true; if (ts.base_type() == "inline-array") { + auto result_type = lookup_type(ts.get_single_arg()); + auto result_structure_type = dynamic_cast(result_type); + if(!result_structure_type || result_structure_type->is_dynamic()) { + info.can_deref = false; + return info; + } + // it's an inline array of structures. We can "dereference". But really we don't do a memory // dereference, we just add stride*idx to the pointer. info.can_deref = true; // deref operators should work... @@ -95,7 +109,7 @@ DerefInfo TypeSystem::get_deref_info(const TypeSpec& ts) { info.result_type = ts.get_single_arg(); // what we're an inline-array of info.sign_extend = false; // not applicable anyway - auto result_type = lookup_type(info.result_type); + if (result_type->is_reference()) { info.stride = align(result_type->get_size_in_memory(), result_type->get_inline_array_alignment()); @@ -111,11 +125,13 @@ DerefInfo TypeSystem::get_deref_info(const TypeSpec& ts) { // in memory, an array of pointers info.stride = POINTER_SIZE; info.sign_extend = false; + info.load_size = POINTER_SIZE; } else { // an array of values, which should be loaded in the correct way to the correct register info.stride = result_type->get_size_in_memory(); info.sign_extend = result_type->get_load_signed(); info.reg = result_type->get_preferred_reg_kind(); + info.load_size = result_type->get_load_size(); assert(result_type->get_size_in_memory() == result_type->get_load_size()); } } else { @@ -513,6 +529,9 @@ void TypeSystem::add_builtin_types() { auto stack_frame_type = add_builtin_basic("basic", "stack-frame"); auto file_stream_type = add_builtin_basic("basic", "file-stream"); auto pointer_type = add_builtin_value_type("object", "pointer", 4); + auto inline_array_type = add_builtin_value_type("object", "inline-array", 4); + inline_array_type->set_runtime_type("pointer"); + auto number_type = add_builtin_value_type("object", "number", 8); // sign extend? auto float_type = add_builtin_value_type("number", "float", 4, false, false, RegKind::FLOAT); auto integer_type = add_builtin_value_type("number", "integer", 8, false, false); // sign extend? @@ -536,17 +555,17 @@ void TypeSystem::add_builtin_types() { // Methods and Fields // OBJECT - add_method(obj_type, "new", make_function_typespec({"symbol", "type", "int32"}, "object")); - add_method(obj_type, "delete", make_function_typespec({"object"}, "none")); - add_method(obj_type, "print", make_function_typespec({"object"}, "object")); - add_method(obj_type, "inspect", make_function_typespec({"object"}, "object")); + add_method(obj_type, "new", make_function_typespec({"symbol", "type", "int32"}, "_type_")); + add_method(obj_type, "delete", make_function_typespec({"_type_"}, "none")); + add_method(obj_type, "print", make_function_typespec({"_type_"}, "_type_")); + add_method(obj_type, "inspect", make_function_typespec({"_type_"}, "_type_")); add_method(obj_type, "length", - make_function_typespec({"object"}, "int32")); // todo - this integer type? - add_method(obj_type, "asize-of", make_function_typespec({"object"}, "int32")); - add_method(obj_type, "copy", make_function_typespec({"object", "symbol"}, "object")); - add_method(obj_type, "relocate", make_function_typespec({"object", "int32"}, "object")); + make_function_typespec({"_type_"}, "int32")); // todo - this integer type? + add_method(obj_type, "asize-of", make_function_typespec({"_type_"}, "int32")); + add_method(obj_type, "copy", make_function_typespec({"_type_", "symbol"}, "_type_")); + add_method(obj_type, "relocate", make_function_typespec({"_type_", "int32"}, "_type_")); add_method(obj_type, "mem-usage", - make_function_typespec({"object"}, "int32")); // todo - this is a guess. + make_function_typespec({"_type_"}, "int32")); // todo - this is a guess. // STRUCTURE // structure new doesn't support dynamic sizing, which is kinda weird - it grabs the size from diff --git a/common/type_system/TypeSystem.h b/common/type_system/TypeSystem.h index 71a865588b..600b2e44a4 100644 --- a/common/type_system/TypeSystem.h +++ b/common/type_system/TypeSystem.h @@ -23,6 +23,7 @@ struct DerefInfo { bool sign_extend = false; RegKind reg = RegKind::INVALID; int stride = -1; + int load_size = -1; TypeSpec result_type; }; diff --git a/doc/type_system.md b/doc/type_system.md index bbc7a5a2e2..64c965a69c 100644 --- a/doc/type_system.md +++ b/doc/type_system.md @@ -191,17 +191,18 @@ All type definitions should also define all the methods, in the order they appea Todo --------- - [x] Difference between "runtime" and "compile time" types? - - [ ] `inline-array` and `pointer` + - [x] `inline-array` and `pointer` - [x] Arrays which aren't `array`s and aren't fields. - [x] `lookup_field_info` (returning the correct field type for arrays/dynamics, info about how to deref) - [x] `deref_info` +- [ ] `int` and `uint` - [ ] Finish builtin types - [ ] Tests for... - [ ] Builtin types - - [ ] Methods - - [ ] Multiple definition checks - - [ ] Deref - - [ ] Array access + - [x] Methods + - [ ] Multiple definition checks for types + - [x] Multiple definition checks for methods + - [x] Deref/Array access - [ ] Field creation - [ ] Support for `_type_` / method specific stuff. (maybe this should live outside the type system?) - [ ] Ability to export type in `deftype` form. @@ -216,4 +217,13 @@ Todo - [ ] Ability to read a `deftype` form. - [ ] In the decompiler - [ ] In the compiler, with the ability to do constant propagation and put things like `(+ 1 2)` or `MY_CONSTANT` as compile-time array size constants by providing a function evaluating an `Object` to an `int`. -- [ ] Bitfield types \ No newline at end of file +- [ ] Bitfield types + + +Todo In the Long Term Future +---------------------------- +- [ ] Bitfield Types +- [ ] Utilities for number types? +- [ ] Tests for field layout/alignment rules (waiting on having good examples) +- [ ] Tests for field access (waiting on real examples) +- [ ] Signed/unsigned for builtin types. \ No newline at end of file diff --git a/game/kernel/klink.cpp b/game/kernel/klink.cpp index 38eba8ee74..5978131404 100644 --- a/game/kernel/klink.cpp +++ b/game/kernel/klink.cpp @@ -14,6 +14,7 @@ #include "kboot.h" #include "kprint.h" #include "common/symbols.h" +#include "common/goal_constants.h" namespace { // turn on printf's for debugging linking issues. diff --git a/game/kernel/kprint.cpp b/game/kernel/kprint.cpp index 59b086a667..0fde516a49 100644 --- a/game/kernel/kprint.cpp +++ b/game/kernel/kprint.cpp @@ -8,6 +8,7 @@ #include #include +#include "common/goal_constants.h" #include "common/common_types.h" #include "kprint.h" #include "kmachine.h" @@ -880,7 +881,7 @@ s32 format_impl(uint64_t* args) { if (sym.offset) { Ptr type = *sym.cast>(); if (type.offset) { - call_method_of_type(in, type, GOAL_PRINT_FUNC); + call_method_of_type(in, type, GOAL_PRINT_METHOD); } } else { throw std::runtime_error("failed to find symbol in format!"); @@ -901,7 +902,7 @@ s32 format_impl(uint64_t* args) { if (sym.offset) { Ptr type = *sym.cast>(); if (type.offset) { - call_method_of_type(in, type, GOAL_INSPECT_FUNC); + call_method_of_type(in, type, GOAL_INSPECT_METHOD); } } else { throw std::runtime_error("failed to find symbol in format!"); diff --git a/game/kernel/kscheme.cpp b/game/kernel/kscheme.cpp index eff441ee01..673781988f 100644 --- a/game/kernel/kscheme.cpp +++ b/game/kernel/kscheme.cpp @@ -18,6 +18,7 @@ #include "klink.h" #include "common/symbols.h" #include "common/versions.h" +#include "common/goal_constants.h" //! Controls link mode when EnableMethodSet = 0, MasterDebug = 1, DiskBoot = 0. Will enable a //! warning message if EnableMethodSet = 1 @@ -986,7 +987,7 @@ u64 print_object(u32 obj) { } else if ((obj & OFFSET_MASK) == PAIR_OFFSET) { return print_pair(obj); } else if ((obj & OFFSET_MASK) == BASIC_OFFSET) { - return call_method_of_type(obj, Ptr(*Ptr(obj - 4)), GOAL_PRINT_FUNC); + return call_method_of_type(obj, Ptr(*Ptr(obj - 4)), GOAL_PRINT_METHOD); } else { cprintf("#", obj & OFFSET_MASK, obj); } @@ -1194,7 +1195,7 @@ u64 copy_structure(u32 it, u32 unknown) { u64 copy_basic(u32 obj, u32 heap) { // determine size of basic. We call a method instead of using asize_of_basic in case the type has // overridden the default asize_of method. - u32 size = call_method_of_type(obj, Ptr(*Ptr(obj - BASIC_OFFSET)), GOAL_ASIZE_FUNC); + u32 size = call_method_of_type(obj, Ptr(*Ptr(obj - BASIC_OFFSET)), GOAL_ASIZE_METHOD); u32 result; if (*Ptr(heap - 4) == *(s7 + FIX_SYM_SYMBOL_TYPE)) { @@ -1224,7 +1225,7 @@ u64 inspect_object(u32 obj) { } else if ((obj & OFFSET_MASK) == PAIR_OFFSET) { return inspect_pair(obj); } else if ((obj & OFFSET_MASK) == BASIC_OFFSET) { - return call_method_of_type(obj, Ptr(*Ptr(obj - BASIC_OFFSET)), GOAL_INSPECT_FUNC); + return call_method_of_type(obj, Ptr(*Ptr(obj - BASIC_OFFSET)), GOAL_INSPECT_METHOD); } else { cprintf("#", obj & OFFSET_MASK, obj); } diff --git a/game/kernel/kscheme.h b/game/kernel/kscheme.h index 38c8df351d..8129ebd50d 100644 --- a/game/kernel/kscheme.h +++ b/game/kernel/kscheme.h @@ -18,22 +18,13 @@ extern Ptr SymbolTable2; extern Ptr LastSymbol; constexpr s32 GOAL_MAX_SYMBOLS = 0x2000; -constexpr s32 BINTEGER_OFFSET = 0; -constexpr s32 PAIR_OFFSET = 2; -constexpr s32 BASIC_OFFSET = 4; + + constexpr s32 SYM_INFO_OFFSET = 0xff34; constexpr u32 EMPTY_HASH = 0x8454B6E6; constexpr u32 OFFSET_MASK = 7; constexpr u32 CRC_POLY = 0x04c11db7; -constexpr u32 GOAL_NEW_FUNC = 0; // method ID of GOAL new -constexpr u32 GOAL_DEL_FUNC = 1; // method ID of GOAL delete -constexpr u32 GOAL_PRINT_FUNC = 2; // method ID of GOAL print -constexpr u32 GOAL_INSPECT_FUNC = 3; // method ID of GOAL inspect -constexpr u32 GOAL_LENGTH_FUNC = 4; // method ID of GOAL length -constexpr u32 GOAL_ASIZE_FUNC = 5; // method ID of GOAL size -constexpr u32 GOAL_COPY_FUNC = 6; // method ID of GOAL copy -constexpr u32 GOAL_RELOC_FUNC = 7; // method ID of GOAL relocate constexpr u32 DEFAULT_METHOD_COUNT = 12; constexpr u32 FALLBACK_UNKNOWN_METHOD_COUNT = 44; diff --git a/goalc/listener/CMakeLists.txt b/goalc/listener/CMakeLists.txt index 470fe89d37..f978b4d633 100644 --- a/goalc/listener/CMakeLists.txt +++ b/goalc/listener/CMakeLists.txt @@ -1,2 +1,2 @@ add_library(listener SHARED - Listener.cpp Deci2Server.cpp Deci2Server.h) + Listener.cpp) diff --git a/goalc/listener/Deci2Server.cpp b/goalc/listener/Deci2Server.cpp deleted file mode 100644 index 9b2a29f942..0000000000 --- a/goalc/listener/Deci2Server.cpp +++ /dev/null @@ -1,264 +0,0 @@ -/*! - * @file Deci2Server.cpp - * Basic implementation of a DECI2 server. - * Works with deci2.cpp (sceDeci2) to implement the networking on target - */ - -#include -#include -#include -#include -#include -#include - -#include "common/listener_common.h" -#include "common/versions.h" -#include "Deci2Server.h" - -Deci2Server::Deci2Server(std::function shutdown_callback) { - buffer = new char[BUFFER_SIZE]; - want_exit = std::move(shutdown_callback); -} - -Deci2Server::~Deci2Server() { - // if accept thread is running, kill it - if (accept_thread_running) { - kill_accept_thread = true; - accept_thread.join(); - accept_thread_running = false; - } - - delete[] buffer; - - if (server_fd >= 0) { - close(server_fd); - } - - if (new_sock >= 0) { - close(new_sock); - } -} - -/*! - * Start waiting for the Listener to connect - */ -bool Deci2Server::init() { - server_fd = socket(AF_INET, SOCK_STREAM, 0); - if (server_fd < 0) { - server_fd = -1; - return false; - } - - int opt = 1; - if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) { - printf("[Deci2Server] Failed to setsockopt 1\n"); - close(server_fd); - server_fd = -1; - return false; - } - - int one = 1; - if (setsockopt(server_fd, SOL_TCP, TCP_NODELAY, &one, sizeof(one))) { - printf("[Deci2Server] Failed to setsockopt 2\n"); - close(server_fd); - server_fd = -1; - return false; - } - - timeval timeout = {}; - timeout.tv_sec = 0; - timeout.tv_usec = 100000; - - if (setsockopt(server_fd, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout)) < 0) { - printf("[Deci2Server] Failed to setsockopt 3\n"); - close(server_fd); - server_fd = -1; - return false; - } - - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = INADDR_ANY; - addr.sin_port = htons(DECI2_PORT); - - if (bind(server_fd, (sockaddr*)&addr, sizeof(addr)) < 0) { - printf("[Deci2Server] Failed to bind\n"); - close(server_fd); - server_fd = -1; - return false; - } - - if (listen(server_fd, 0) < 0) { - printf("[Deci2Server] Failed to listen\n"); - close(server_fd); - server_fd = -1; - return false; - } - - server_initialized = true; - accept_thread_running = true; - kill_accept_thread = false; - accept_thread = std::thread(&Deci2Server::accept_thread_func, this); - return true; -} - -/*! - * Return true if the listener is connected. - */ -bool Deci2Server::check_for_listener() { - if (server_connected) { - if (accept_thread_running) { - accept_thread.join(); - accept_thread_running = false; - } - return true; - } else { - return false; - } -} - -/*! - * Send data from buffer. User must provide appropriate headers. - */ -void Deci2Server::send_data(void* buf, u16 len) { - lock(); - if (!server_connected) { - printf("[DECI2] send while not connected, not sending!\n"); - } else { - uint16_t prog = 0; - while (prog < len) { - auto wrote = write(new_sock, (char*)(buf) + prog, len - prog); - prog += wrote; - if (!server_connected || want_exit()) { - unlock(); - return; - } - } - } - unlock(); -} - -/*! - * Lock the DECI mutex. Should be done before modifying protocols. - */ -void Deci2Server::lock() { - deci_mutex.lock(); -} - -/*! - * Unlock the DECI mutex. Should be done after modifying protocols. - */ -void Deci2Server::unlock() { - deci_mutex.unlock(); -} - -/*! - * Wait for protocols to become ready. - * This avoids the case where we receive messages before protocol handlers are set up. - */ -void Deci2Server::wait_for_protos_ready() { - if (protocols_ready) - return; - std::unique_lock lk(deci_mutex); - cv.wait(lk, [&] { return protocols_ready; }); -} - -/*! - * Inform server that protocol handlers are ready. - * Will unblock wait_for_protos_ready and incoming messages will be dispatched to these - * protocols. You can change the protocol handlers, but you should lock the mutex before - * doing so. - */ -void Deci2Server::send_proto_ready(Deci2Driver* drivers, int* driver_count) { - lock(); - d2_drivers = drivers; - d2_driver_count = driver_count; - protocols_ready = true; - unlock(); - cv.notify_all(); -} - -void Deci2Server::run() { - int desired_size = (int)sizeof(Deci2Header); - int got = 0; - - while (got < desired_size) { - assert(got + desired_size < BUFFER_SIZE); - auto x = read(new_sock, buffer + got, desired_size - got); - if (want_exit()) { - return; - } - got += x > 0 ? x : 0; - } - - auto* hdr = (Deci2Header*)(buffer); - printf("[DECI2] Got message:\n"); - printf(" %d %d 0x%x %c -> %c\n", hdr->len, hdr->rsvd, hdr->proto, hdr->src, hdr->dst); - - hdr->rsvd = got; - - // see what protocol we got: - lock(); - - int handler = -1; - for (int i = 0; i < *d2_driver_count; i++) { - auto& prot = d2_drivers[i]; - if (prot.active && prot.protocol) { - if (handler != -1) { - printf("[DECI2] Warning: more than on protocol handler for this message!\n"); - } - handler = i; - } - } - - if (handler == -1) { - printf("[DECI2] Warning: no handler for this message, ignoring...\n"); - unlock(); - return; - // throw std::runtime_error("no handler!"); - } - - auto& driver = d2_drivers[handler]; - - int sent_to_program = 0; - while (!want_exit() && (hdr->rsvd < hdr->len || sent_to_program < hdr->rsvd)) { - // send what we have to the program - if (sent_to_program < hdr->rsvd) { - // driver.next_recv_size = 0; - // driver.next_recv = nullptr; - driver.recv_buffer = buffer + sent_to_program; - driver.available_to_receive = hdr->rsvd - sent_to_program; - (driver.handler)(DECI2_READ, driver.available_to_receive, driver.opt); - // memcpy(driver.next_recv, buffer + sent_to_program, driver.next_recv_size); - sent_to_program += driver.recv_size; - } - - // receive from network - if (hdr->rsvd < hdr->len) { - auto x = read(new_sock, buffer + hdr->rsvd, hdr->len - hdr->rsvd); - if (want_exit()) { - return; - } - got += x > 0 ? x : 0; - hdr->rsvd += got; - } - } - - (driver.handler)(DECI2_READDONE, 0, driver.opt); - unlock(); -} - -/*! - * Background thread for waiting for the listener. - */ -void Deci2Server::accept_thread_func() { - socklen_t l = sizeof(addr); - while (!kill_accept_thread) { - new_sock = accept(server_fd, (sockaddr*)&addr, &l); - if (new_sock >= 0) { - u32 versions[2] = {versions::GOAL_VERSION_MAJOR, versions::GOAL_VERSION_MINOR}; - send(new_sock, &versions, 8, 0); // todo, check result? - server_connected = true; - return; - } - } -} diff --git a/goalc/listener/Deci2Server.h b/goalc/listener/Deci2Server.h deleted file mode 100644 index 45e3727851..0000000000 --- a/goalc/listener/Deci2Server.h +++ /dev/null @@ -1,57 +0,0 @@ -/*! - * @file Deci2Server.h - * Basic implementation of a DECI2 server. - * Works with deci2.cpp (sceDeci2) to implement the networking on target - */ - -#ifndef JAK1_DECI2SERVER_H -#define JAK1_DECI2SERVER_H - -#include -#include -#include -#include -#include -#include "game/system/deci_common.h" // todo, move me! - - -class Deci2Server { - public: - static constexpr int BUFFER_SIZE = 32 * 1024 * 1024; - Deci2Server(std::function shutdown_callback); - ~Deci2Server(); - bool init(); - bool check_for_listener(); - void send_data(void* buf, u16 len); - - void lock(); - void unlock(); - void wait_for_protos_ready(); - void send_proto_ready(Deci2Driver* drivers, int* driver_count); - - void run(); - - - private: - void accept_thread_func(); - bool kill_accept_thread = false; - char* buffer = nullptr; - int server_fd; - sockaddr_in addr; - int new_sock; - bool server_initialized = false; - bool accept_thread_running = false; - bool server_connected = false; - std::function want_exit; - std::thread accept_thread; - - std::condition_variable cv; - bool protocols_ready = false; - std::mutex deci_mutex; - Deci2Driver* d2_drivers = nullptr; - int* d2_driver_count = nullptr; -}; - - - -#endif // JAK1_DECI2SERVER_H diff --git a/test/test_kernel.cpp b/test/test_kernel.cpp index 064449aed9..4fac96f587 100644 --- a/test/test_kernel.cpp +++ b/test/test_kernel.cpp @@ -3,6 +3,7 @@ #include #include "gtest/gtest.h" #include "common/symbols.h" +#include "common/goal_constants.h" #include "game/kernel/fileio.h" #include "game/kernel/kboot.h" #include "game/kernel/kprint.h" diff --git a/test/test_listener_deci2.cpp b/test/test_listener_deci2.cpp index 4efa4d5bd9..f7ba45a371 100644 --- a/test/test_listener_deci2.cpp +++ b/test/test_listener_deci2.cpp @@ -1,6 +1,6 @@ #include "gtest/gtest.h" #include "goalc/listener/Listener.h" -#include "goalc/listener/Deci2Server.h" +#include "game/system/Deci2Server.h" using namespace listener; diff --git a/test/test_type_system.cpp b/test/test_type_system.cpp index 0deabcddce..673924e7a8 100644 --- a/test/test_type_system.cpp +++ b/test/test_type_system.cpp @@ -3,23 +3,231 @@ #include "third-party/fmt/core.h" TEST(TypeSystem, Construction) { + // test that we can add all builtin types without any type errors TypeSystem ts; ts.add_builtin_types(); - fmt::print("{}", ts.print_all_type_information()); + + // useful for debugging. + // fmt::print("{}", ts.print_all_type_information()); } TEST(TypeSystem, DefaultMethods) { TypeSystem ts; ts.add_builtin_types(); + // check that default methods have the right ID's used by the kernel + ts.assert_method_id("object", "new", GOAL_NEW_METHOD); + ts.assert_method_id("object", "delete", GOAL_DEL_METHOD); + ts.assert_method_id("object", "print", GOAL_PRINT_METHOD); + ts.assert_method_id("object", "inspect", GOAL_INSPECT_METHOD); + ts.assert_method_id("object", "length", GOAL_LENGTH_METHOD); + ts.assert_method_id("object", "asize-of", GOAL_ASIZE_METHOD); + ts.assert_method_id("object", "copy", GOAL_COPY_METHOD); + ts.assert_method_id("object", "relocate", GOAL_RELOC_METHOD); + ts.assert_method_id("object", "mem-usage", GOAL_MEMUSAGE_METHOD); - ts.assert_method_id("object", "new", 0); - ts.assert_method_id("object", "delete", 1); - ts.assert_method_id("object", "print", 2); - ts.assert_method_id("object", "inspect", 3); - ts.assert_method_id("object", "length", 4); - ts.assert_method_id("object", "asize-of", 5); - ts.assert_method_id("object", "copy", 6); - ts.assert_method_id("object", "relocate", 7); - ts.assert_method_id("object", "mem-usage", 8); -} \ No newline at end of file + // check that they are inherited. + ts.assert_method_id("function", "new", GOAL_NEW_METHOD); + ts.assert_method_id("function", "delete", GOAL_DEL_METHOD); + ts.assert_method_id("function", "print", GOAL_PRINT_METHOD); + ts.assert_method_id("function", "inspect", GOAL_INSPECT_METHOD); + ts.assert_method_id("function", "length", GOAL_LENGTH_METHOD); + ts.assert_method_id("function", "asize-of", GOAL_ASIZE_METHOD); + ts.assert_method_id("function", "copy", GOAL_COPY_METHOD); + ts.assert_method_id("function", "relocate", GOAL_RELOC_METHOD); + ts.assert_method_id("function", "mem-usage", GOAL_MEMUSAGE_METHOD); +} + +TEST(TypeSystem, TypeSpec) { + TypeSystem ts; + ts.add_builtin_types(); + + // try some simple typespecs + auto string_typespec = ts.make_typespec("string"); + auto function_typespec = ts.make_typespec("function"); + EXPECT_EQ(string_typespec.print(), "string"); + EXPECT_EQ(string_typespec.base_type(), "string"); + EXPECT_TRUE(function_typespec == function_typespec); + EXPECT_FALSE(function_typespec == string_typespec); + + // try some pointer typespecs + auto pointer_function_typespec = ts.make_pointer_typespec("function"); + EXPECT_EQ(pointer_function_typespec.print(), "(pointer function)"); + EXPECT_EQ(pointer_function_typespec.get_single_arg(), ts.make_typespec("function")); + EXPECT_EQ(pointer_function_typespec.base_type(), "pointer"); + + // try some function typespecs + auto test_function_typespec = ts.make_function_typespec({"string", "symbol"}, "integer"); + EXPECT_EQ(test_function_typespec.base_type(), "function"); + EXPECT_EQ(test_function_typespec.print(), "(function string symbol integer)"); + + // try the none typespec + EXPECT_EQ(ts.make_typespec("none").base_type(), "none"); +} + +TEST(TypeSystem, TypeSpecEquality) { + TypeSystem ts; + ts.add_builtin_types(); + + auto pointer_to_function = ts.make_pointer_typespec("function"); + auto ia_to_function = ts.make_inline_array_typespec("function"); + auto pointer_to_string = ts.make_pointer_typespec("string"); + + EXPECT_TRUE(pointer_to_function == pointer_to_function); + EXPECT_FALSE(pointer_to_function == ia_to_function); + EXPECT_FALSE(pointer_to_string == pointer_to_function); +} + +TEST(TypeSystem, RuntimeTypes) { + TypeSystem ts; + ts.add_builtin_types(); + + // pointers and inline arrays should all become simple pointers + EXPECT_EQ(ts.get_runtime_type(ts.make_typespec("pointer")), "pointer"); + EXPECT_EQ(ts.get_runtime_type(ts.make_typespec("inline-array")), "pointer"); + EXPECT_EQ(ts.get_runtime_type(ts.make_pointer_typespec("function")), "pointer"); + EXPECT_EQ(ts.get_runtime_type(ts.make_inline_array_typespec("function")), "pointer"); + + // functions of any kind should become function + EXPECT_EQ(ts.get_runtime_type(ts.make_function_typespec({"integer", "string"}, "symbol")), + "function"); +} + +TEST(TypeSystem, ForwardDeclaration) { + TypeSystem ts; + ts.add_builtin_types(); + + // before forward declaring, lookup and creating a typespec should fail + EXPECT_ANY_THROW(ts.lookup_type("test-type")); + EXPECT_ANY_THROW(ts.make_typespec("test-type")); + + // after forward declaring, we should be able to create typespec, but not do a full lookup + ts.forward_declare_type("test-type"); + + EXPECT_TRUE(ts.make_typespec("test-type").print() == "test-type"); + EXPECT_ANY_THROW(ts.lookup_type("test-type")); +} + +TEST(TypeSystem, DerefInfoNoLoadInfoOrStride) { + // test the parts of deref info, other than the part where it tells you how to load or the stride. + TypeSystem ts; + ts.add_builtin_types(); + + // can't dereference a non-pointer + EXPECT_FALSE(ts.get_deref_info(ts.make_typespec("string")).can_deref); + // can't deref a pointer with no type + EXPECT_FALSE(ts.get_deref_info(ts.make_typespec("pointer")).can_deref); + EXPECT_FALSE(ts.get_deref_info(ts.make_typespec("inline-array")).can_deref); + + // test pointer to reference type + auto type_spec = + ts.make_pointer_typespec(ts.make_function_typespec({"string", "symbol"}, "int32")); + auto info = ts.get_deref_info(type_spec); + EXPECT_TRUE(info.can_deref); + EXPECT_TRUE(info.mem_deref); + EXPECT_FALSE(info.sign_extend); // it's a memory address being loaded + EXPECT_EQ(info.reg, RegKind::GPR_64); + EXPECT_EQ(info.stride, 4); + EXPECT_EQ(info.result_type.print(), "(function string symbol int32)"); + EXPECT_EQ(info.load_size, 4); + + // test pointer to value type + type_spec = ts.make_pointer_typespec("int64"); + info = ts.get_deref_info(type_spec); + EXPECT_TRUE(info.can_deref); + EXPECT_TRUE(info.mem_deref); + EXPECT_EQ(info.load_size, 8); + EXPECT_EQ(info.stride, 8); + EXPECT_EQ(info.sign_extend, true); + EXPECT_EQ(info.reg, RegKind::GPR_64); + EXPECT_EQ(info.result_type.print(), "int64"); + + // test inline-array (won't work because type is dynamically sized) + type_spec = + ts.make_inline_array_typespec("type"); + info = ts.get_deref_info(type_spec); + EXPECT_FALSE(info.can_deref); + + + // TODO - replace with a better type. + // TODO - maybe block basic or structure from being inline-array-able? + type_spec = + ts.make_inline_array_typespec("basic"); + auto type = ts.lookup_type("basic"); + info = ts.get_deref_info(type_spec); + EXPECT_TRUE(info.can_deref); + EXPECT_FALSE(info.mem_deref); + EXPECT_EQ(info.stride, (type->get_size_in_memory() + 15) & (~15)); + EXPECT_EQ(info.result_type.print(), "basic"); + EXPECT_EQ(info.load_size, -1); +} + +TEST(TypeSystem, AddMethodAndLookupMethod) { + TypeSystem ts; + ts.add_builtin_types(); + + auto parent_info = ts.add_method(ts.lookup_type("structure"), "test-method-1", + ts.make_function_typespec({"integer"}, "string")); + + // when trying to add the same method to a child, should return the parent's method + auto child_info_same = ts.add_method(ts.lookup_type("basic"), "test-method-1", + ts.make_function_typespec({"integer"}, "string")); + + 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.add_method(ts.lookup_type("basic"), "test-method-1", + ts.make_function_typespec({"integer"}, "integer"))); + EXPECT_ANY_THROW(ts.add_method(ts.lookup_type("basic"), "test-method-1", + ts.make_function_typespec({}, "string"))); + EXPECT_ANY_THROW(ts.add_method(ts.lookup_type("basic"), "test-method-1", + ts.make_function_typespec({"integer", "string"}, "string"))); + EXPECT_ANY_THROW(ts.add_method(ts.lookup_type("basic"), "test-method-1", + ts.make_function_typespec({"string"}, "string"))); + + ts.add_method(ts.lookup_type("basic"), "test-method-2", + ts.make_function_typespec({"integer"}, "string")); + + EXPECT_EQ(parent_info.id, ts.lookup_method("basic", "test-method-1").id); + EXPECT_EQ(parent_info.id, ts.lookup_method("structure", "test-method-1").id); + EXPECT_EQ(parent_info.id + 1, ts.lookup_method("basic", "test-method-2").id); + EXPECT_ANY_THROW(ts.lookup_method("structure", "test-method-2")); + + EXPECT_EQ(ts.lookup_method("basic", "test-method-1").defined_in_type, "structure"); + EXPECT_EQ(ts.lookup_method("basic", "test-method-1").type.print(), "(function integer string)"); + EXPECT_EQ(ts.lookup_method("basic", "test-method-1").name, "test-method-1"); +} + +TEST(TypeSystem, NewMethod) { + TypeSystem ts; + ts.add_builtin_types(); + ts.add_type("test-1", std::make_unique("basic", "test-1")); + ts.add_method(ts.lookup_type("test-1"), "new", ts.make_function_typespec({"symbol", "string"}, "test-1")); + ts.add_type("test-2", std::make_unique("test-1", "test-2")); + ts.add_method(ts.lookup_type("test-2"), "new", ts.make_function_typespec({"symbol", "string", "symbol"}, "test-2")); + + EXPECT_EQ(ts.lookup_method("test-1", "new").type.print(), "(function symbol string test-1)"); + EXPECT_EQ(ts.lookup_method("test-2", "new").type.print(), "(function symbol string symbol test-2)"); + + ts.add_type("test-3", std::make_unique("test-1", "test-3")); + EXPECT_EQ(ts.lookup_method("test-3", "new").type.print(), "(function symbol string test-1)"); + + ts.add_type("test-4", std::make_unique("test-2", "test-4")); + EXPECT_EQ(ts.lookup_method("test-4", "new").type.print(), "(function symbol string symbol test-2)"); +} + + +TEST(TypeSystem, MethodSubstitute) { + TypeSystem ts; + ts.add_builtin_types(); + ts.add_type("test-1", std::make_unique("basic", "test-1")); + ts.add_method(ts.lookup_type("test-1"), "new", ts.make_function_typespec({"symbol", "string", "_type_"}, "_type_")); + + auto final_type = ts.lookup_method("test-1", "new").type.substitute_for_method_call("test-1"); + EXPECT_EQ(final_type.print(), "(function symbol string test-1 test-1)"); +} + +// field lookup + +// TODO - a big test to make sure all the builtin types are what we expect. \ No newline at end of file