Skip to content

Commit

Permalink
Update to v0.2.0:
Browse files Browse the repository at this point in the history
* Reduce the amount of symbols created. No need to create empty namespaces etc.

* Better error handling while parsing operators
  • Loading branch information
srydell committed Jun 14, 2022
1 parent 71a7ad0 commit ab0d131
Show file tree
Hide file tree
Showing 11 changed files with 209 additions and 98 deletions.
5 changes: 5 additions & 0 deletions docs/ReleaseNotes/v0.2.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,8 @@ Frontend::Objc::createModule(rootNamespace, moduleName);
Frontend::Swift::createModule(rootNamespace, moduleName);
```
* Reduce the amount of symbols created. No need to create empty namespaces etc.
## Misc ##
* Better error handling while parsing operators
90 changes: 42 additions & 48 deletions src/Objc/Builders/classBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <numeric>
#include <optional>
#include <set>
#include <spdlog/spdlog.h>
#include <string>
#include <string_view>

Expand Down Expand Up @@ -47,8 +48,7 @@ std::string getConversions(std::string_view objcName,

} // namespace

std::optional<Objc::Proxy::Class> buildClass(IR::Struct const& cppClass,
Objc::Cache& cache) {
Objc::Proxy::Class buildClass(IR::Struct const& cppClass, Objc::Cache& cache) {
Objc::Proxy::Class objcClass(
Objc::getClassName(cppClass, cache.m_moduleName),
cppClass.m_representation);
Expand Down Expand Up @@ -78,22 +78,18 @@ std::optional<Objc::Proxy::Class> buildClass(IR::Struct const& cppClass,
overloadedFunctions.find(function.m_representation) !=
overloadedFunctions.end();
bool isConstructor = false;
if (auto maybeObjcFunction = buildFunction(objcClass.getName(),
cppClass.m_representation,
function,
cache,
isConstructor,
isOverloaded)) {
auto& objcFunction = maybeObjcFunction.value();

if (function.m_isStatic) {
objcFunction.setAsStatic();
}

objcClass.addFunction(objcFunction);
} else {
return std::nullopt;
auto objcFunction = buildFunction(objcClass.getName(),
cppClass.m_representation,
function,
cache,
isConstructor,
isOverloaded);

if (function.m_isStatic) {
objcFunction.setAsStatic();
}

objcClass.addFunction(objcFunction);
}

auto overloadedOperators =
Expand All @@ -103,46 +99,44 @@ std::optional<Objc::Proxy::Class> buildClass(IR::Struct const& cppClass,
overloadedFunctions.find(function.m_representation) !=
overloadedFunctions.end();
bool isConstructor = false;
if (auto maybeObjcFunction = buildFunction(objcClass.getName(),
cppClass.m_representation,
function,
cache,
isConstructor,
isOverloaded)) {
if (auto maybeName = ObjcSwift::Helpers::getOperatorName(op)) {
auto& objcFunction = maybeObjcFunction.value();

if (function.m_isStatic) {
objcFunction.setAsStatic();
}

objcClass.addFunction(objcFunction);
auto objcFunction = buildFunction(objcClass.getName(),
cppClass.m_representation,
function,
cache,
isConstructor,
isOverloaded);
if (auto maybeName = ObjcSwift::Helpers::getOperatorName(op)) {
if (function.m_isStatic) {
objcFunction.setAsStatic();
}

objcClass.addFunction(objcFunction);
} else {
return std::nullopt;
spdlog::warn(
"Unknown operator {} found while creating an Objective-C version of class {}. Since it currently does not have a translation, it's being ignored.",
function.m_representation,
objcClass.getCppClassName());
}
}

for (auto const& constructor : cppClass.m_public.m_constructors) {
if (auto maybeObjcFunction = buildFunction(objcClass.getName(),
cppClass.m_representation,
constructor,
cache,
true)) {
auto& objcFunction = maybeObjcFunction.value();

if (constructor.m_isStatic) {
objcFunction.setAsStatic();
}
auto objcFunction = buildFunction(objcClass.getName(),
cppClass.m_representation,
constructor,
cache,
true);

if (constructor.m_isStatic) {
objcFunction.setAsStatic();
}

if (cppClass.m_public.m_constructors.size() > 1) {
objcFunction.setAsOverloaded(overloadIndex++);
}
if (cppClass.m_public.m_constructors.size() > 1) {
objcFunction.setAsOverloaded(overloadIndex++);
}

objcFunction.setAsConstructor();
objcFunction.setAsConstructor();

objcClass.addConstructor(objcFunction);
}
objcClass.addConstructor(objcFunction);
}

for (auto const& variable : cppClass.m_public.m_memberVariables) {
Expand Down
5 changes: 1 addition & 4 deletions src/Objc/Builders/classBuilder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,13 @@
#include "Objc/Proxy/class.hpp"
#include "Objc/cache.hpp"
#include <IR/ir.hpp>
#include <optional>

namespace Objc::Builders {

/**
* Transforms a IR::Struct to a Objc::Proxy::Class
* NOTE: If the struct does not have an explicit constructor,
* the default constructor is added
* Fails if any member functions takes unique_ptr as an argument
*/
std::optional<Objc::Proxy::Class> buildClass(IR::Struct const& cppClass,
Objc::Cache& cache);
Objc::Proxy::Class buildClass(IR::Struct const& cppClass, Objc::Cache& cache);
} // namespace Objc::Builders
13 changes: 6 additions & 7 deletions src/Objc/Builders/functionBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,12 @@

namespace Objc::Builders {

std::optional<Objc::Proxy::Function>
buildFunction(std::string const& objcClass,
std::string const& cppClass,
IR::Function const& cppFunction,
Objc::Cache& cache,
bool isConstructor,
bool isOverloaded) {
Objc::Proxy::Function buildFunction(std::string const& objcClass,
std::string const& cppClass,
IR::Function const& cppFunction,
Objc::Cache& cache,
bool isConstructor,
bool isOverloaded) {
Objc::Proxy::Function objcFunction(
Objc::getFunctionName(cppFunction, isConstructor, isOverloaded),
cppFunction.m_name,
Expand Down
14 changes: 6 additions & 8 deletions src/Objc/Builders/functionBuilder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,17 @@
#include "Objc/Proxy/function.hpp"
#include "Objc/cache.hpp"
#include <IR/ir.hpp>
#include <optional>
#include <string>

namespace Objc::Builders {

/**
* Transforms a IR::Function to a Objc::Proxy::Function
*/
std::optional<Objc::Proxy::Function>
buildFunction(std::string const& objcClass,
std::string const& cppClass,
IR::Function const& cppFunction,
Objc::Cache& cache,
bool isConstructor = false,
bool isOverloaded = false);
Objc::Proxy::Function buildFunction(std::string const& objcClass,
std::string const& cppClass,
IR::Function const& cppFunction,
Objc::Cache& cache,
bool isConstructor = false,
bool isOverloaded = false);
} // namespace Objc::Builders
107 changes: 77 additions & 30 deletions src/Objc/Builders/moduleFileBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <optional>
#include <queue>
#include <set>
#include <spdlog/spdlog.h>
#include <string>

namespace {
Expand All @@ -19,39 +20,34 @@ bool addNamespaceObjects(Objc::Proxy::ModuleFile& moduleFile,
IR::Namespace const& ns,
std::string const& objcName,
Objc::Cache& cache) {
bool hasAddedAnything = false;
for (auto const& e : ns.m_enums) {
moduleFile.addEnum(Objc::Builders::buildEnum(e, cache));
hasAddedAnything = true;
}

for (auto const& cls : ns.m_structs) {
if (auto maybeC = Objc::Builders::buildClass(cls, cache)) {
moduleFile.addClass(maybeC.value());
} else {
return false;
}
moduleFile.addClass(Objc::Builders::buildClass(cls, cache));
hasAddedAnything = true;
}

auto overloadedFunctions =
ObjcSwift::getOverloadedFunctions(ns.m_functions);
for (auto const& function : ns.m_functions) {
bool isConstructor = false;
bool isOverloaded =
overloadedFunctions.find(function.m_representation) !=
overloadedFunctions.end();
if (auto maybeF = Objc::Builders::buildFunction(objcName,
ns.m_representation,
function,
cache,
isConstructor,
isOverloaded)) {
auto f = maybeF.value();
// Global functions act as static functions
f.setAsStatic();
f.setAsStandalone();
moduleFile.addFunction(f);
} else {
return false;
}
overloadedFunctions.contains(function.m_representation);
auto f = Objc::Builders::buildFunction(objcName,
ns.m_representation,
function,
cache,
isConstructor,
isOverloaded);
// Global functions act as static functions
f.setAsStatic();
f.setAsStandalone();
moduleFile.addFunction(f);
hasAddedAnything = true;
}

for (auto const& variable : ns.m_variables) {
Expand All @@ -62,34 +58,85 @@ bool addNamespaceObjects(Objc::Proxy::ModuleFile& moduleFile,
attr.setAsStatic();
attr.setAsStandalone();
moduleFile.addAttribute(attr);
hasAddedAnything = true;
}

return hasAddedAnything;
}

// This namespace will not add any symbols
bool isEmpty(IR::Namespace const& ns) {
return ns.m_functions.empty() && ns.m_enums.empty() &&
ns.m_structs.empty() && ns.m_variables.empty();
}

/**
* A namespace will correspond to a Class in Objc.
* Every symbol in Objc will be prefixed by the library name.
* Ex: C++ - library = Store
* Cart getCart();
*
* Will result in the function with the call
* Objc
* [Store getCart];
* where Store is introduced as a class
*
* So if we add a non-empty namespace called "Store" together with "getCart"
* we will have a name clash. Since empty namespaces are not translated
* this checks either root is empty, or module exists and is non-empty
*/
bool hasNonEmptyGlobalNSAndModuleNS(IR::Namespace const& rootNamespace,
std::string_view libraryName) {
// Not creating a ns == libraryName
// No conflicts
if (isEmpty(rootNamespace)) {
return false;
}

return true;
// Is there a ns == libraryName?
if (auto moduleNs = std::find_if(rootNamespace.m_namespaces.begin(),
rootNamespace.m_namespaces.end(),
[libraryName](IR::Namespace const& ns) {
return ns.m_name == libraryName;
});
moduleNs != rootNamespace.m_namespaces.end()) {
// There is, is it empty?
return !isEmpty(*moduleNs);
}
return false;
}
} // namespace

namespace Objc::Builders {

std::optional<Objc::Proxy::ModuleFile>
buildModuleFile(IR::Namespace const& rootNamespace,
std::string const& rootModuleName) {
std::string const& libraryName) {
if (hasNonEmptyGlobalNSAndModuleNS(rootNamespace, libraryName)) {
spdlog::error(
R"(Non-empty global namespace and non-empty namespace with the same name as the library: {0}
Due to how Tolc introduces symbols when creating Objective-C bindings, it is not possible to have a non-empty global namespace while also having a non-empty namespace with the same name as the library.
Merge the global public functions/classes/enums with the functions/classes/enums under the namespace {0}.)",
libraryName);
return std::nullopt;
}
std::unique_ptr<Objc::Cache> cache = std::make_unique<Objc::Cache>();
cache->m_moduleName = rootModuleName;
cache->m_moduleName = libraryName;
Objc::Proxy::ModuleFile moduleFile;

std::queue<IR::Namespace const*> namespaces;
namespaces.push(&rootNamespace);

// If there is a namespace that has the same name as the rootModule
while (!namespaces.empty()) {
auto currentNamespace = namespaces.front();
if (auto m = Objc::Builders::buildModule(*currentNamespace, *cache)) {
if (!addNamespaceObjects(moduleFile,
*currentNamespace,
m.value().getName(),
*cache)) {
return std::nullopt;
if (addNamespaceObjects(moduleFile,
*currentNamespace,
m.value().getName(),
*cache)) {
moduleFile.addModule(m.value());
}
moduleFile.addModule(m.value());
}

// Go deeper into the nested namespaces
Expand Down
1 change: 1 addition & 0 deletions src/ObjcSwift/Helpers/operatorNames.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
namespace ObjcSwift::Helpers {

std::optional<std::string> getOperatorName(IR::Operator op) {
return std::nullopt;
using IR::Operator;
switch (op) {
case Operator::Addition:
Expand Down
1 change: 1 addition & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ foreach(
filesystem_paths.cpp
functions.cpp
globalVariables.cpp
libraryNSClash.cpp
lists.cpp
maps.cpp
memberVariables.cpp
Expand Down
5 changes: 5 additions & 0 deletions tests/TestUtil/include/TestUtil/parse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,10 @@ IR::Namespace parse(std::string code) {
REQUIRE(parsed.has_value());
return parsed.value();
}

std::optional<IR::Namespace> parseUnsafe(std::string code) {
return Parser::parseString(code, TestUtil::getParserConfig());
}

} // namespace TestUtil

Loading

0 comments on commit ab0d131

Please sign in to comment.