Skip to content

Commit a10d3a3

Browse files
authored
Merge pull request #119 from kornilova-l/skip-illegal-scala-functions
Skip functions that pass arrays or structs by value
2 parents 329f350 + 80b0b22 commit a10d3a3

File tree

11 files changed

+128
-41
lines changed

11 files changed

+128
-41
lines changed

bindgen/Utils.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,14 @@ static inline std::string replaceChar(const std::string &str,
116116
* @return true if given type is of type T or is an alias for type T.
117117
*/
118118
template <typename T> static inline bool isAliasForType(Type *type) {
119-
if (isInstanceOf<TypeDef>(type)) {
120-
auto *typeDef = dynamic_cast<TypeDef *>(type);
119+
if (isInstanceOf<T>(type)) {
120+
return true;
121+
}
122+
auto *typeDef = dynamic_cast<TypeDef *>(type);
123+
if (typeDef) {
121124
return isAliasForType<T>(typeDef->getType().get());
122125
}
123-
return isInstanceOf<T>(type);
126+
return false;
124127
}
125128

126129
#endif // UTILS_H

bindgen/ir/Function.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "Function.h"
22
#include "../Utils.h"
3+
#include "Struct.h"
34

45
Parameter::Parameter(std::string name, std::shared_ptr<Type> type)
56
: TypeAndName(std::move(name), type) {}
@@ -71,3 +72,19 @@ Function::~Function() {
7172
delete parameter;
7273
}
7374
}
75+
76+
bool Function::isLegalScalaNativeFunction() const {
77+
/* Return type and parameters types cannot be array types because array type
78+
* in this case is always represented as a pointer to element type */
79+
if (isAliasForType<Struct>(retType.get()) ||
80+
isAliasForType<Union>(retType.get())) {
81+
return false;
82+
}
83+
for (const auto &parameter : parameters) {
84+
if (isAliasForType<Struct>(parameter->getType().get()) ||
85+
isAliasForType<Union>(parameter->getType().get())) {
86+
return false;
87+
}
88+
}
89+
return true;
90+
}

bindgen/ir/Function.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#define SCALA_NATIVE_BINDGEN_FUNCTION_H
33

44
#include "TypeAndName.h"
5+
#include "TypeDef.h"
56
#include <llvm/Support/raw_ostream.h>
67
#include <string>
78
#include <vector>
@@ -27,6 +28,12 @@ class Function {
2728

2829
void setScalaName(std::string scalaName);
2930

31+
/**
32+
* @return true if the function does not use values of structs or arrays
33+
* (note: unions are represented as arrays)
34+
*/
35+
bool isLegalScalaNativeFunction() const;
36+
3037
private:
3138
std::string getVarargsParameterName() const;
3239

bindgen/ir/IR.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,15 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &s, const IR &ir) {
122122
}
123123

124124
for (const auto &func : ir.functions) {
125-
s << *func;
125+
if (func->isLegalScalaNativeFunction()) {
126+
s << *func;
127+
} else {
128+
llvm::errs()
129+
<< "Warning: Function " << func->getName()
130+
<< " is skipped because Scala Native does not support "
131+
"passing structs and arrays by value.\n";
132+
llvm::errs().flush();
133+
}
126134
}
127135

128136
s << "}\n\n";

docs/src/paradox/limitations/index.md

Lines changed: 45 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,47 @@
11
# Limitations
22

3-
There are multiple unsupported cases that should be considered when generating bindings:
4-
5-
1. Currently bindgen does not support passing structs by value.
6-
For example, it will not be possible to call these two functions from Scala Native code:
7-
8-
```c
9-
struct MyStruct {
10-
int a;
11-
};
12-
13-
struct MyStruct returnStruct();
14-
15-
void handleStruct(struct MyStruct mystr);
16-
```
17-
To support such cases one should generate bindings for C wrapper functions that use pointers to structs instead of actual structs.
18-
2. `#define`s for literals and variables are supported. For other types of `#define`s,
19-
write wrapper functions that return defined values.
20-
21-
```c
22-
// Supported
23-
#define ESC 0x1b /* Defines for numerical and string literals. */
24-
extern const int pi_const;
25-
#define PI pi_const /* Defines aliasing extern variables. */
26-
27-
// Not supported (non-exhaustive list)
28-
#define COLS (getenv("COLS") ? atoi(getenv("COLS")) : 80)
29-
#define MAX(a, b) (a > b ? a : b)
30-
```
31-
32-
3. There is no way to reuse already generated bindings.
33-
Bindgen outputs bindings also for headers that were included in a given header. See @github[#2](#2).
34-
4. Type qualifiers `const`, `volatile` and `restrict` are not supported.
35-
5. Extern variables are read-only. See @github[scala-native/scala-native#202](scala-native/scala-native#202).
3+
There are multiple unsupported cases that should be considered when generating bindings.
4+
5+
## Passing structs by value
6+
7+
Scala Native does not support passing structs by value, bindgen skips such functions.
8+
```c
9+
struct MyStruct {
10+
int a;
11+
};
12+
13+
struct MyStruct returnStruct(); // skipped
14+
15+
void handleStruct(struct MyStruct mystr); // skipped
16+
```
17+
To support such cases one should generate bindings for C wrapper functions that use pointers to structs instead of
18+
actual structs.
19+
20+
## Limited support of `#define`s
21+
22+
`#define`s for literals and variables are supported. For other types of `#define`s,
23+
write wrapper functions that return defined values.
24+
25+
```c
26+
// Supported
27+
#define ESC 0x1b /* Defines for numerical and string literals. */
28+
extern const int pi_const;
29+
#define PI pi_const /* Defines aliasing extern variables. */
30+
31+
// Not supported (non-exhaustive list)
32+
#define COLS (getenv("COLS") ? atoi(getenv("COLS")) : 80)
33+
#define MAX(a, b) (a > b ? a : b)
34+
```
35+
36+
## Reusing generated bindings
37+
38+
There is no way to reuse already generated bindings.
39+
Bindgen outputs bindings also for headers that were included in a given header. See @github[#2](#2).
40+
41+
## Type qualifiers
42+
43+
Type qualifiers `const`, `volatile` and `restrict` are not supported.
44+
45+
## Updating extern variables
46+
47+
Extern variables are read-only. See @github[scala-native/scala-native#202](scala-native/scala-native#202).

tests/samples/Function.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,21 @@ char one_arg(int a);
44
void *two_args(float a, int b);
55
double anonymous_args(float, int);
66
double variadic_args(double a, char *varArgs, ...);
7+
8+
struct s {
9+
int val;
10+
};
11+
12+
void acceptsStructValue(struct s); // function is skipped with warning
13+
14+
typedef struct s s;
15+
16+
s returnsStructValue(); // function is skipped with warning
17+
18+
union u {
19+
int a;
20+
};
21+
22+
void acceptsUnionValue(union u); // function is skipped with warning
23+
24+
void acceptsArray(int[10]); // it's okay because the type is pointer to int

tests/samples/Function.scala

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,32 @@ import scala.scalanative.native._
66
@native.link("bindgentests")
77
@native.extern
88
object Function {
9+
type struct_s = native.CStruct1[native.CInt]
10+
type s = struct_s
11+
type union_u = native.CArray[Byte, native.Nat._4]
912
def no_args(): native.CInt = native.extern
1013
def void_arg(): native.CFloat = native.extern
1114
def one_arg(a: native.CInt): native.CChar = native.extern
1215
def two_args(a: native.CFloat, b: native.CInt): native.Ptr[Byte] = native.extern
1316
def anonymous_args(anonymous0: native.CFloat, anonymous1: native.CInt): native.CDouble = native.extern
1417
def variadic_args(a: native.CDouble, varArgs: native.CString, varArgs0: native.CVararg*): native.CDouble = native.extern
18+
def acceptsArray(anonymous0: native.Ptr[native.CInt]): Unit = native.extern
1519
}
20+
21+
import Function._
22+
23+
object FunctionHelpers {
24+
25+
implicit class struct_s_ops(val p: native.Ptr[struct_s]) extends AnyVal {
26+
def `val`: native.CInt = !p._1
27+
def `val_=`(value: native.CInt): Unit = !p._1 = value
28+
}
29+
30+
def struct_s()(implicit z: native.Zone): native.Ptr[struct_s] = native.alloc[struct_s]
31+
32+
implicit class union_u_pos(val p: native.Ptr[union_u]) extends AnyVal {
33+
def a: native.Ptr[native.CInt] = p.cast[native.Ptr[native.CInt]]
34+
def a_=(value: native.CInt): Unit = !p.cast[native.Ptr[native.CInt]] = value
35+
}
36+
}
37+

tests/samples/PrivateMembers.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ void __privateFunction();
3838

3939
// functions that should not be removed:
4040
__private_type *getPrivateType();
41-
void usesPrivateUnion(union __unionWithPrivateName);
41+
void usesPrivateUnion(union __unionWithPrivateName *);
4242
void usesPrivateStruct(struct structWithPrivateType *, struct normalStruct *);
4343
void usesPrivateEnum(enum __privateEnum *);
4444

tests/samples/PrivateMembers.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ object PrivateMembers {
1919
type privateStructWithTypedefPtr = native.Ptr[struct_privateStructWithTypedef]
2020
def getTypeThatUsesPrivateTypes(): pid_t = native.extern
2121
def getPrivateType(): native.Ptr[__private_type] = native.extern
22-
def usesPrivateUnion(anonymous0: union___unionWithPrivateName): Unit = native.extern
22+
def usesPrivateUnion(anonymous0: native.Ptr[union___unionWithPrivateName]): Unit = native.extern
2323
def usesPrivateStruct(anonymous0: native.Ptr[struct_structWithPrivateType], anonymous1: native.Ptr[struct_normalStruct]): Unit = native.extern
2424
def usesPrivateEnum(anonymous0: native.Ptr[enum___privateEnum]): Unit = native.extern
2525
}

tests/samples/ReservedWords.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ union lazy {
2020

2121
typedef union lazy lazy;
2222

23-
type with(match sealed, var implicit, lazy forSome);
23+
type *with(match sealed, var implicit, lazy *forSome);
2424

2525
typedef match def;
2626
typedef struct {

0 commit comments

Comments
 (0)