Skip to content

Commit e093f71

Browse files
authored
Implement support for properties in Emscripten generator. (#1932)
* Implement support for properties in Emscripten generator. * CLI: Fix option validation to check for valid Emscripten platform. * Ignore functions returning tag types for now in JS generation. * Use a Lua bindings spec file for Emscripten tests. * Improve JS testing scripts. * Only run packing step for release CI builds.
1 parent fad95cf commit e093f71

File tree

14 files changed

+302
-96
lines changed

14 files changed

+302
-96
lines changed

.github/workflows/main.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ jobs:
3737
- uses: actions/checkout@v4
3838

3939
- name: Setup emsdk
40-
uses: mymindstorm/setup-emsdk@v11
40+
uses: mymindstorm/setup-emsdk@v14
4141
with:
4242
version: ${{ env.EMSCRIPTEN_VERSION }}
4343
actions-cache-folder: emsdk-cache-${{ runner.os }}
@@ -88,14 +88,15 @@ jobs:
8888
- name: Test (QuickJS)
8989
if: runner.os != 'Windows'
9090
shell: bash
91-
run: tests/quickjs/test.sh -dotnet_configuration $BUILD_CONFIGURATION
91+
run: tests/quickjs/test.sh --dotnet-config $BUILD_CONFIGURATION
9292

9393
- name: Test (Emscripten)
9494
if: runner.os != 'Windows'
9595
shell: bash
96-
run: tests/emscripten/test.sh -dotnet_configuration $BUILD_CONFIGURATION
96+
run: tests/emscripten/test.sh --dotnet-config $BUILD_CONFIGURATION
9797

9898
- name: Pack
99+
if: matrix.build-cfg == 'Release'
99100
shell: bash
100101
run: build/build.sh prepack -platform $PLATFORM -configuration $BUILD_CONFIGURATION
101102

src/CLI/Generator.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,13 @@ public bool ValidateOptions(List<string> messages)
9090

9191
options.Platform ??= Platform.Host;
9292

93+
if (options.Architecture is TargetArchitecture.WASM32 or TargetArchitecture.WASM64 &&
94+
options.Platform is not TargetPlatform.Emscripten)
95+
{
96+
messages.Add("Please set Emscripten platform for WASM architectures.");
97+
return false;
98+
}
99+
93100
if (string.IsNullOrEmpty(options.OutputDir))
94101
{
95102
options.OutputDir = Path.Combine(Directory.GetCurrentDirectory(), "gen");

src/Generator/Driver.cs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,12 @@ public void SetupPasses(ILibrary library)
252252

253253
passes.AddPass(new CleanInvalidDeclNamesPass());
254254
passes.AddPass(new FastDelegateToDelegatesPass());
255-
passes.AddPass(new FieldToPropertyPass());
255+
256+
if (Options.GeneratorKind != GeneratorKind.Emscripten)
257+
{
258+
passes.AddPass(new FieldToPropertyPass());
259+
}
260+
256261
passes.AddPass(new CheckIgnoredDeclsPass());
257262
passes.AddPass(new CheckEnumsPass());
258263
passes.AddPass(new MakeProtectedNestedTypesPublicPass());
@@ -283,9 +288,14 @@ public void SetupPasses(ILibrary library)
283288

284289
passes.AddPass(new CheckDuplicatedNamesPass());
285290

286-
if (Options.IsCLIGenerator || Options.IsCSharpGenerator)
291+
if (Options.IsCLIGenerator || Options.IsCSharpGenerator
292+
|| Options.GeneratorKind.ID is GeneratorKind.Emscripten_ID)
287293
{
288294
passes.RenameDeclsUpperCase(RenameTargets.Any & ~RenameTargets.Parameter);
295+
}
296+
297+
if (Options.IsCLIGenerator || Options.IsCSharpGenerator)
298+
{
289299
passes.AddPass(new CheckKeywordNamesPass());
290300
}
291301

src/Generator/Generators/Emscripten/EmscriptenSources.cs

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -117,11 +117,6 @@ public override void VisitClassConstructors(IEnumerable<Method> ctors)
117117
}
118118
}
119119

120-
public override bool VisitProperty(Property property)
121-
{
122-
return true;
123-
}
124-
125120
public override bool VisitMethodDecl(Method method)
126121
{
127122
Indent();
@@ -130,9 +125,33 @@ public override bool VisitMethodDecl(Method method)
130125
return ret;
131126
}
132127

128+
public override bool VisitProperty(Property property)
129+
{
130+
if (property.Field != null)
131+
return property.Field.Visit(this);
132+
133+
if (!property.GetMethod.IsConst)
134+
{
135+
Console.WriteLine($"Cannot bind non-const property getter method: {property.GetMethod.QualifiedOriginalName}");
136+
return false;
137+
}
138+
139+
var @class = property.Namespace as Class;
140+
Indent();
141+
Write($".property(\"{property.Name}\", &{@class.QualifiedOriginalName}::{property.GetMethod.OriginalName}");
142+
143+
if (property.HasSetter)
144+
Write($", &{@class.QualifiedOriginalName}::{property.SetMethod.OriginalName}");
145+
146+
WriteLine(")");
147+
Unindent();
148+
149+
return true;
150+
}
151+
133152
public override bool VisitFieldDecl(Field field)
134153
{
135-
WriteLineIndent($".field(\"{field.Name}\", &{field.Class.QualifiedOriginalName}::{field.OriginalName})");
154+
WriteLineIndent($".property(\"{field.Name}\", &{field.Class.QualifiedOriginalName}::{field.OriginalName})");
136155
return true;
137156
}
138157

src/Generator/Options.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,8 @@ public bool DoAllModulesHaveLibraries() =>
205205

206206
public bool IsCLIGenerator => GeneratorKind == GeneratorKind.CLI;
207207

208+
public bool IsJSGenerator => GeneratorKind == GeneratorKind.Emscripten || GeneratorKind == GeneratorKind.QuickJS;
209+
208210
public readonly List<string> DependentNameSpaces = new List<string>();
209211
public bool MarshalCharAsManagedChar { get; set; }
210212
public bool MarshalConstCharArrayAsString { get; set; } = true;

src/Generator/Passes/CheckIgnoredDecls.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,14 @@ public override bool VisitFunctionDecl(Function function)
184184
return false;
185185
}
186186

187+
if (Options.IsJSGenerator && function is Method { Kind: CXXMethodKind.Normal } && ret.Type.GetFinalPointee().IsClass())
188+
{
189+
function.ExplicitlyIgnore();
190+
Diagnostics.Debug("Function '{0}' was ignored due to {1} return decl not yet implemented in JS generators",
191+
function.Name, msg);
192+
return false;
193+
}
194+
187195
foreach (var param in function.Parameters)
188196
{
189197
if (HasInvalidDecl(param, out msg))

tests/Classes.h

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ class Class
66
{
77
public:
88
void ReturnsVoid() {}
9-
int ReturnsInt() { return 0; }
10-
// Class* PassAndReturnsClassPtr(Class* obj) { return obj; }
9+
int ReturnsInt() const { return 0; }
10+
Class* PassAndReturnsClassPtr(Class* obj) { return obj; }
1111
};
1212

1313
class ClassWithField
@@ -18,7 +18,21 @@ class ClassWithField
1818
{
1919
}
2020
int Field;
21-
int ReturnsField() { return Field; }
21+
int ReturnsField() const { return Field; }
22+
};
23+
24+
class ClassWithProperty
25+
{
26+
public:
27+
ClassWithProperty()
28+
: Field(10)
29+
{
30+
}
31+
int GetField() const { return Field; }
32+
void SetField(int value) { Field = value; }
33+
34+
private:
35+
int Field;
2236
};
2337

2438
class ClassWithOverloads
@@ -41,4 +55,4 @@ class ClassWithExternalInheritance : public ClassFromAnotherUnit
4155
};
4256

4357
// void FunctionPassClassByRef(Class* klass) { }
44-
// Class* FunctionReturnsClassByRef() { return new Class(); }
58+
// Class* FunctionReturnsClassByRef() { return new Class(); }

tests/emscripten/bindings.lua

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
generator "emscripten"
2+
platform "emscripten"
3+
architecture "wasm32"
4+
5+
includedirs
6+
{
7+
"..",
8+
"../../include",
9+
}
10+
11+
output "gen"
12+
13+
module "tests"
14+
namespace "test"
15+
headers
16+
{
17+
"Builtins.h",
18+
"Classes.h",
19+
"Classes2.h",
20+
"Delegates.h",
21+
"Enums.h",
22+
"Overloads.h"
23+
}

tests/emscripten/test.mjs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,14 @@ function classes() {
111111

112112
var classWithField = new test.ClassWithField();
113113
eq(classWithField.ReturnsField(), 10);
114-
//eq(classWithField.Field, 10);
114+
eq(classWithField.Field, 10);
115+
classWithField.Field = 20;
116+
eq(classWithField.ReturnsField(), 20);
117+
118+
var classWithProperty = new test.ClassWithProperty();
119+
eq(classWithProperty.Field, 10);
120+
classWithProperty.Field = 20;
121+
eq(classWithProperty.Field, 20);
115122
}
116123

117124
builtins();

tests/emscripten/test.sh

Lines changed: 48 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,46 @@
11
#!/usr/bin/env bash
22
set -e
3+
34
dir=$(cd "$(dirname "$0")"; pwd)
45
rootdir="$dir/../.."
5-
dotnet_configuration=Release
6-
configuration=debug
6+
dotnet_configuration=DebugOpt
7+
make_configuration=debug
78
platform=x64
89
jsinterp=$(which node)
910

10-
for arg in "$@"; do
11-
case $arg in
12-
--with-node=*)
13-
jsinterp="${arg#*=}"
14-
shift
15-
;;
16-
-configuration)
17-
configuration=$2
18-
shift
19-
;;
20-
-dotnet_configuration)
21-
dotnet_configuration=$2
22-
shift
23-
;;
24-
esac
11+
usage() {
12+
cat <<EOF
13+
Usage: $(basename $0) [--with-node=NODE] [--make-config CONFIG] [--dotnet-config CONFIG]
14+
EOF
15+
exit 1
16+
}
17+
18+
while [[ $# -gt 0 ]]; do
19+
case "$1" in
20+
--with-node=*)
21+
jsinterp="${1#*=}"
22+
shift
23+
;;
24+
--with-node)
25+
jsinterp="$2"
26+
shift 2
27+
;;
28+
--make-config|--make-configuration)
29+
make_configuration="$2"
30+
shift 2
31+
;;
32+
--dotnet-config|--dotnet-configuration)
33+
dotnet_configuration="$2"
34+
shift 2
35+
;;
36+
-h|--help)
37+
usage
38+
;;
39+
*)
40+
echo "Unknown option: $1" >&2
41+
usage
42+
;;
43+
esac
2544
done
2645

2746
if [ "$CI" = "true" ]; then
@@ -34,20 +53,21 @@ else
3453
reset=`tput sgr0`
3554
fi
3655

56+
# 1) Generate
3757
generate=true
38-
3958
if [ $generate = true ]; then
40-
echo "${green}Generating bindings${reset}"
41-
dotnet $rootdir/bin/${dotnet_configuration}/CppSharp.CLI.dll \
42-
--gen=emscripten --platform=emscripten --arch=wasm32 \
43-
-I$dir/.. -I$rootdir/include -o $dir/gen -m tests $dir/../*.h
59+
echo "${green}Generating bindings with .NET configuration $dotnet_configuration${reset}"
60+
dotnet "$rootdir/bin/${dotnet_configuration}/CppSharp.CLI.dll" --property=keywords \
61+
"$dir/bindings.lua"
4462
fi
4563

46-
echo "${green}Building generated binding files${reset}"
47-
premake=$rootdir/build/premake.sh
48-
config=$configuration $premake --file=$dir/premake5.lua gmake
49-
emmake make -C $dir/gen
50-
echo
64+
# 2) Build
65+
echo "${green}Building generated binding files (make config: $make_configuration)${reset}"
66+
premake="$rootdir/build/premake.sh"
67+
"$premake" --file=$dir/premake5.lua gmake2
68+
config=$make_configuration emmake make -C "$dir/gen"
5169

70+
# 3) Test
71+
echo
5272
echo "${green}Executing JS tests with Node${reset}"
53-
$jsinterp $dir/test.mjs
73+
"$jsinterp" "$dir/test.mjs"

0 commit comments

Comments
 (0)