#include "Transform.h"
#include <iostream>
#include "Logging.h"
#include "Mappings.h"
#include "Universe.h"
namespace objcgen {
static void resolve_static_instance_clash(Symbol& symbol, bool is_static)
{
symbol.rename(std::string(symbol.name()).append(is_static ? "Static" : "Instance"));
}
static void resolve_static_instance_clash(TypeDeclarationSymbol& decl, NonTypeSymbol& method, bool is_static)
{
assert(method.kind() == NonTypeSymbol::Kind::MemberMethod);
resolve_static_instance_clash(method, is_static);
for (auto& member : decl.members()) {
if (member.kind() == NonTypeSymbol::Kind::Property && member.is_static() == is_static &&
member.getter() == method.selector()) {
resolve_static_instance_clash(member, is_static);
break;
}
}
}
static void resolve_static_instance_clashes(TypeDeclarationSymbol& type);
static void resolve_static_instance_clashes(TypeDeclarationSymbol& subclass, TypeDeclarationSymbol& superclass)
{
resolve_static_instance_clashes(superclass);
for (auto& super_superclass : superclass.bases()) {
resolve_static_instance_clashes(subclass, super_superclass);
}
for (auto& submember : subclass.members()) {
if (submember.kind() == NonTypeSymbol::Kind::MemberMethod) {
for (const auto& supermember : superclass.members()) {
if (supermember.kind() == NonTypeSymbol::Kind::MemberMethod &&
supermember.selector() == submember.selector()) {
auto supername = supermember.name();
if (supername == submember.name()) {
if (submember.is_static()) {
if (!supermember.is_static()) {
resolve_static_instance_clash(subclass, submember, true);
}
} else if (supermember.is_static()) {
resolve_static_instance_clash(subclass, submember, false);
}
} else {
submember.rename(supername);
}
}
}
}
}
}
struct StaticInstancePair {
public:
void add(NonTypeSymbol& type) noexcept;
bool both() const noexcept
{
return static_ && instance_;
}
NonTypeSymbol* get_static() const noexcept
{
return static_;
}
NonTypeSymbol* get_instance() const noexcept
{
return instance_;
}
private:
NonTypeSymbol* static_;
NonTypeSymbol* instance_;
};
void StaticInstancePair::add(NonTypeSymbol& type) noexcept
{
if (type.is_static()) {
assert(!static_ && "Cannot be multiple static methods with the same name");
static_ = &type;
} else {
assert(!instance_ && "Cannot be multiple instance methods with the same name");
instance_ = &type;
}
}
* Resolve static/instance clashes in the `type` class hierarchy.
*
* That is, if any class in the `type` hierarchy contains static or instance
* methods conflicting by name with, correspondingly, instance or static methods
* of this very class or one of its bases (direct or indirect), the conflicts
* are resolved by appending the "Static" or "Instance" suffix to the method
* names.
*
* Each class/protocol is checked for clashes with each of its ancestors, from
* top to bottom sequentially. If a clash is found, the "Static" or "Instance"
* suffix is appended to the descendant's conflicting method name. Then clashes
* are resolved inside the class/protocol itself. The static method is renamed
* in this case.
*
* Such a procedure is performed for each vertex of the directed acyclic graph
* of `type` and all its ancestors (classes and protocols). The graph is
* traversed from the root(s) to `type`. After processing, each vertex is
* marked as resolved. It is not re-processed again in this and subsequent calls
* of the function.
*/
static void resolve_static_instance_clashes(TypeDeclarationSymbol& type)
{
if (type.are_static_instance_clashes_resolved()) {
return;
}
for (auto& supertype : type.bases()) {
resolve_static_instance_clashes(type, supertype);
}
std::unordered_map<std::string_view, StaticInstancePair> map;
for (auto& member : type.members()) {
if (member.kind() == NonTypeSymbol::Kind::MemberMethod) {
map[member.selector()].add(member);
}
}
for (const auto& [name, methods] : map) {
if (methods.both()) {
auto& static_method = *methods.get_static();
if (static_method.name() == methods.get_instance()->name()) {
resolve_static_instance_clash(type, static_method, true);
}
}
}
map.clear();
for (auto& member : type.members()) {
if (member.kind() == NonTypeSymbol::Kind::Property) {
map[member.selector()].add(member);
}
}
for (const auto& [name, props] : map) {
if (props.both()) {
auto& static_prop = *props.get_static();
if (static_prop.name() == props.get_instance()->name()) {
resolve_static_instance_clash(static_prop, true);
}
}
}
type.mark_static_instance_clashes_resolved();
}
* Removes duplicate method declarations. In Objective-C, it is allowed to
* declare a method more than once. In Cangjie, it is not.
*/
static void remove_duplicates(TypeDeclarationSymbol& type)
{
auto num_members = type.member_count();
for (size_t i = 0; i < num_members; ++i) {
const auto& member_i = type.member(i);
if (member_i.kind() == NonTypeSymbol::Kind::MemberMethod) {
for (size_t j = i + 1; j < num_members;) {
const auto& member_j = type.member(j);
if (member_j.kind() == NonTypeSymbol::Kind::MemberMethod &&
member_j.is_static() == member_i.is_static() && member_j.name() == member_i.name()) {
assert(member_j.parameter_count() == member_i.parameter_count());
type.member_remove(j);
--num_members;
} else {
++j;
}
}
}
}
}
static void rename_clashing_protocols(TypeDeclarationSymbol& type)
{
auto& universe = Universe::get();
for (;;) {
const auto& name = type.name();
bool clashing = false;
for (uint8_t ns = 0; ns < TYPE_NAMESPACE_COUNT; ++ns) {
auto namespaze = static_cast<TypeNamespace>(ns);
if (namespaze != TypeNamespace::Protocols) {
const auto* non_protocol_type = universe.type(namespaze, name);
if (non_protocol_type && non_protocol_type->package() == type.package()) {
auto new_name = name + "Protocol";
if (verbosity >= LogLevel::INFO) {
std::cerr << "Renaming clashing protocol `" << name << "` to `" << new_name << "`" << std::endl;
}
type.rename(new_name);
clashing = true;
break;
}
}
}
if (!clashing) {
break;
}
}
}
static void do_rename()
{
auto& universe = Universe::get();
auto type_definitions = universe.type_definitions();
for (auto&& type : type_definitions) {
if (type.is(NamedTypeSymbol::Kind::Protocol)) {
rename_clashing_protocols(type);
}
remove_duplicates(type);
for (auto&& member : type.members()) {
if (member.name().find(':') != std::string_view::npos) {
std::string new_name;
auto upcase = false;
for (auto c : member.name()) {
if (c == ':') {
upcase = true;
continue;
}
if (upcase) {
c = static_cast<char>(std::toupper(c));
upcase = false;
}
new_name += c;
}
member.rename(new_name);
}
}
}
for (auto& type : type_definitions) {
resolve_static_instance_clashes(type);
}
for (auto& top_level : universe.top_level()) {
if (top_level.is_global_function()) {
const auto& name = top_level.name();
const auto* type = universe.type(name);
if (type && type->package() == top_level.package()) {
top_level.rename(name + "Func");
}
}
}
}
static void set_type_mappings()
{
for (auto&& type : Universe::get().all_declarations()) {
for (auto mapping_ptr : mappings) {
auto& mapping = *mapping_ptr;
if (mapping.can_map(&type)) {
type.set_mapping(&mapping);
}
}
}
}
static void do_map(NonTypeSymbol& symbol, TypeDeclarationSymbol* decl)
{
for (auto&& parameter : symbol.parameters()) {
parameter.set_type(parameter.type()->map());
}
auto return_type = symbol.return_type()->map();
assert(return_type);
if (return_type->is_instancetype()) {
assert(decl);
return_type = decl;
}
symbol.set_return_type(return_type);
}
static void do_map()
{
auto& universe = Universe::get();
for (auto& top_level : universe.top_level()) {
do_map(top_level, nullptr);
}
for (auto&& decl : universe.all_declarations()) {
if (auto* type = dynamic_cast<TypeDeclarationSymbol*>(&decl)) {
for (auto&& member : type->members()) {
if (!member.is_property()) {
do_map(member, type);
}
}
} else if (auto* alias = dynamic_cast<TypeAliasSymbol*>(&decl)) {
auto* target = alias->target();
if (target) {
alias->set_target(target->map());
}
}
}
}
void apply_transforms()
{
do_rename();
set_type_mappings();
do_map();
}
}