Skip to content

Generate helper class for structs that have more than 22 fields #122

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 17, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions bindgen/ir/IR.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &s, const IR &ir) {
}

for (const auto &u : ir.unions) {
if (ir.shouldOutput(u)) {
if (ir.shouldOutput(u) && u->hasHelperMethods()) {
s << "\n" << u->generateHelperClass();
}
}
Expand All @@ -184,9 +184,10 @@ void IR::generate(const std::string &excludePrefix) {
}

bool IR::hasHelperMethods() const {
if (hasOutputtedDeclaration(unions)) {
/* all unions have helper methods */
return true;
for (const auto &u : unions) {
if (shouldOutput(u) && u->hasHelperMethods()) {
return true;
}
}

for (const auto &s : structs) {
Expand Down
111 changes: 97 additions & 14 deletions bindgen/ir/Struct.cpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
#include "Struct.h"
#include "../Utils.h"
#include "types/ArrayType.h"
#include "types/PointerType.h"
#include "types/PrimitiveType.h"
#include <sstream>

Field::Field(std::string name, std::shared_ptr<Type> type)
: TypeAndName(std::move(name), std::move(type)) {}

Field::Field(std::string name, std::shared_ptr<Type> type, uint64_t offset)
: TypeAndName(std::move(name), std::move(type)), offset(offset) {}

uint64_t Field::getOffset() const { return offset; }

StructOrUnion::StructOrUnion(std::string name,
std::vector<std::shared_ptr<Field>> fields,
std::shared_ptr<Location> location)
Expand Down Expand Up @@ -41,6 +47,8 @@ std::shared_ptr<Location> StructOrUnion::getLocation() const {
return location;
}

bool StructOrUnion::hasHelperMethods() const { return !fields.empty(); }

Struct::Struct(std::string name, std::vector<std::shared_ptr<Field>> fields,
uint64_t typeSize, std::shared_ptr<Location> location)
: StructOrUnion(std::move(name), std::move(fields), std::move(location)),
Expand All @@ -64,19 +72,15 @@ std::shared_ptr<TypeDef> Struct::generateTypeDef() {

std::string Struct::generateHelperClass() const {
assert(hasHelperMethods());
/* struct is not empty and not represented as an array */
std::stringstream s;
std::string type = getTypeAlias();
s << " implicit class " << type << "_ops(val p: native.Ptr[" << type
<< "])"
<< " extends AnyVal {\n";
unsigned fieldIndex = 0;
for (const auto &field : fields) {
if (!field->getName().empty()) {
s << generateGetter(fieldIndex) << "\n";
s << generateSetter(fieldIndex) << "\n";
}
fieldIndex++;
if (fields.size() <= SCALA_NATIVE_MAX_STRUCT_FIELDS) {
s << generateHelperClassMethodsForStructRepresentation();
} else {
s << generateHelperClassMethodsForArrayRepresentation();
}
s << " }\n\n";

Expand All @@ -88,8 +92,26 @@ std::string Struct::generateHelperClass() const {
return s.str();
}

bool Struct::hasHelperMethods() const {
return !fields.empty() && fields.size() < SCALA_NATIVE_MAX_STRUCT_FIELDS;
std::string Struct::generateHelperClassMethodsForStructRepresentation() const {
std::stringstream s;
for (unsigned fieldIndex = 0; fieldIndex < fields.size(); fieldIndex++) {
if (!fields[fieldIndex]->getName().empty()) {
s << generateGetterForStructRepresentation(fieldIndex);
s << generateSetterForStructRepresentation(fieldIndex);
}
}
return s.str();
}

std::string Struct::generateHelperClassMethodsForArrayRepresentation() const {
std::stringstream s;
for (unsigned fieldIndex = 0; fieldIndex < fields.size(); fieldIndex++) {
if (!fields[fieldIndex]->getName().empty()) {
s << generateGetterForArrayRepresentation(fieldIndex);
s << generateSetterForArrayRepresentation(fieldIndex);
}
}
return s.str();
}

std::string Struct::getTypeAlias() const { return "struct_" + name; }
Expand Down Expand Up @@ -127,7 +149,8 @@ bool Struct::operator==(const Type &other) const {
return false;
}

std::string Struct::generateSetter(unsigned fieldIndex) const {
std::string
Struct::generateSetterForStructRepresentation(unsigned fieldIndex) const {
std::shared_ptr<Field> field = fields[fieldIndex];
std::string setter = handleReservedWords(field->getName(), "_=");
std::string parameterType = field->getType()->str();
Expand All @@ -139,11 +162,12 @@ std::string Struct::generateSetter(unsigned fieldIndex) const {
}
std::stringstream s;
s << " def " << setter << "(value: " + parameterType + "): Unit = !p._"
<< std::to_string(fieldIndex + 1) << " = " << value;
<< std::to_string(fieldIndex + 1) << " = " << value << "\n";
return s.str();
}

std::string Struct::generateGetter(unsigned fieldIndex) const {
std::string
Struct::generateGetterForStructRepresentation(unsigned fieldIndex) const {
std::shared_ptr<Field> field = fields[fieldIndex];
std::string getter = handleReservedWords(field->getName());
std::string returnType = field->getType()->str();
Expand All @@ -156,10 +180,68 @@ std::string Struct::generateGetter(unsigned fieldIndex) const {
methodBody = "!p._" + std::to_string(fieldIndex + 1);
}
std::stringstream s;
s << " def " << getter << ": " << returnType << " = " << methodBody;
s << " def " << getter << ": " << returnType << " = " << methodBody
<< "\n";
return s.str();
}

std::string
Struct::generateSetterForArrayRepresentation(unsigned int fieldIndex) const {
std::shared_ptr<Field> field = fields[fieldIndex];
std::string setter = handleReservedWords(field->getName(), "_=");
std::string parameterType;
std::string value = "value";
std::string castedField = "p._1";

PointerType pointerToFieldType = PointerType(field->getType());
if (field->getOffset() > 0) {
castedField = "(" + castedField + " + " +
std::to_string(field->getOffset()) + ")";
}
castedField = "!" + castedField + ".cast[" + pointerToFieldType.str() + "]";
if (isAliasForType<ArrayType>(field->getType().get()) ||
isAliasForType<Struct>(field->getType().get())) {
parameterType = pointerToFieldType.str();
value = "!" + value;
} else {
parameterType = field->getType()->str();
}
std::stringstream s;
s << " def " << setter
<< "(value: " + parameterType + "): Unit = " << castedField << " = "
<< value << "\n";
return s.str();
}

std::string
Struct::generateGetterForArrayRepresentation(unsigned fieldIndex) const {
std::shared_ptr<Field> field = fields[fieldIndex];
std::string getter = handleReservedWords(field->getName());
std::string returnType;
std::string methodBody;

PointerType pointerToFieldType = PointerType(field->getType());
if (field->getOffset() > 0) {
methodBody = "(p._1 + " + std::to_string(field->getOffset()) + ")";
} else {
methodBody = "p._1";
}
methodBody = methodBody + ".cast[" + pointerToFieldType.str() + "]";

if (isAliasForType<ArrayType>(field->getType().get()) ||
isAliasForType<Struct>(field->getType().get())) {
returnType = pointerToFieldType.str();
} else {
methodBody = "!" + methodBody;
returnType = field->getType()->str();
}
std::stringstream s;
s << " def " << getter << ": " << returnType << " = " << methodBody
<< "\n";
return s.str();
return "";
}

Union::Union(std::string name, std::vector<std::shared_ptr<Field>> fields,
uint64_t maxSize, std::shared_ptr<Location> location)
: StructOrUnion(std::move(name), std::move(fields), std::move(location)),
Expand All @@ -171,6 +253,7 @@ std::shared_ptr<TypeDef> Union::generateTypeDef() {
}

std::string Union::generateHelperClass() const {
assert(hasHelperMethods());
std::stringstream s;
std::string type = getTypeAlias();
s << " implicit class " << type << "_pos"
Expand Down
41 changes: 32 additions & 9 deletions bindgen/ir/Struct.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@
class Field : public TypeAndName {
public:
Field(std::string name, std::shared_ptr<Type> type);

Field(std::string name, std::shared_ptr<Type> type, uint64_t offset);

uint64_t getOffset() const;

protected:
/**
* Offset in bytes from address of struct/union.
*/
uint64_t offset = 0;
};

class StructOrUnion {
Expand All @@ -31,6 +41,8 @@ class StructOrUnion {

virtual std::string getTypeAlias() const = 0;

bool hasHelperMethods() const;

protected:
std::string name;
std::vector<std::shared_ptr<Field>> fields;
Expand All @@ -50,25 +62,36 @@ class Struct : public StructOrUnion,

std::string getTypeAlias() const override;

/**
* @return true if helper methods will be generated for this struct
*/
bool hasHelperMethods() const;

bool usesType(const std::shared_ptr<Type> &type,
bool stopOnTypeDefs) const override;

std::string str() const override;

bool operator==(const Type &other) const override;

std::string generateSetter(unsigned fieldIndex) const;

std::string generateGetter(unsigned fieldIndex) const;

private:
/* type size is needed if number of fields is bigger than 22 */
uint64_t typeSize;

/**
* @return helper class methods for struct that is represented as CStruct.
*/
std::string generateHelperClassMethodsForStructRepresentation() const;

/**
* @return helper class methods for struct that is represented as CArray.
*/
std::string generateHelperClassMethodsForArrayRepresentation() const;

std::string
generateSetterForStructRepresentation(unsigned fieldIndex) const;

std::string
generateGetterForStructRepresentation(unsigned fieldIndex) const;

std::string generateSetterForArrayRepresentation(unsigned fieldIndex) const;

std::string generateGetterForArrayRepresentation(unsigned fieldIndex) const;
};

class Union : public StructOrUnion,
Expand Down
13 changes: 8 additions & 5 deletions bindgen/visitor/TreeVisitor.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "TreeVisitor.h"
#include "clang/AST/RecordLayout.h"
#include <stdio.h>

bool TreeVisitor::VisitFunctionDecl(clang::FunctionDecl *func) {
Expand Down Expand Up @@ -127,18 +128,20 @@ void TreeVisitor::handleStruct(clang::RecordDecl *record, std::string name) {
llvm::errs().flush();
}

int fieldCnt = 0;
std::vector<std::shared_ptr<Field>> fields;
const clang::ASTRecordLayout &recordLayout =
astContext->getASTRecordLayout(record);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❤️ really cool idea.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😄


for (const clang::FieldDecl *field : record->fields()) {
std::shared_ptr<Type> ftype =
typeTranslator.translate(field->getType(), &name);
fields.push_back(
std::make_shared<Field>(field->getNameAsString(), ftype));
uint64_t recordOffsetInBits =
recordLayout.getFieldOffset(field->getFieldIndex());
assert(recordOffsetInBits % 8 == 0);
fields.push_back(std::make_shared<Field>(
field->getNameAsString(), ftype, recordOffsetInBits / 8));

cycleDetection.AddDependency(newName, field->getType());

fieldCnt++;
}

if (cycleDetection.isCyclic(newName)) {
Expand Down
31 changes: 31 additions & 0 deletions tests/samples/Struct.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,34 @@ char getCharFromAnonymousStruct(struct structWithAnonymousStruct *s) {
char getIntFromAnonymousStruct(struct structWithAnonymousStruct *s) {
return s->anonymousStruct.i;
}

int struct_test_long(struct bigStruct *s, enum struct_op op, long value) {
switch (op) {
case STRUCT_SET:
s->one = value;
return 1;
case STRUCT_TEST:
return s->one == value;
}
}

int struct_test_double(struct bigStruct *s, enum struct_op op, double value) {
switch (op) {
case STRUCT_SET:
s->five = value;
return 1;
case STRUCT_TEST:
return s->five == value;
}
}

int struct_test_point(struct bigStruct *s, enum struct_op op,
struct point *value) {
switch (op) {
case STRUCT_SET:
s->six = *value;
return 1;
case STRUCT_TEST:
return (s->six.x == value->x) && (s->six.y == value->y);
}
}
11 changes: 9 additions & 2 deletions tests/samples/Struct.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ struct bigStruct {
int three;
float four;
double five;
point_s six;
int seven;
struct point six;
struct point *seven;
int eight;
int nine;
int ten;
Expand Down Expand Up @@ -59,3 +59,10 @@ struct structWithAnonymousStruct {
char getCharFromAnonymousStruct(struct structWithAnonymousStruct *s);

char getIntFromAnonymousStruct(struct structWithAnonymousStruct *s);

enum struct_op { STRUCT_SET, STRUCT_TEST };

int struct_test_long(struct bigStruct *s, enum struct_op op, long value);
int struct_test_double(struct bigStruct *s, enum struct_op op, double value);
int struct_test_point(struct bigStruct *s, enum struct_op op,
struct point *value);
Loading