Browse Source

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.
main
João Matos 2 weeks ago committed by GitHub
parent
commit
e093f713b9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 7
      .github/workflows/main.yml
  2. 7
      src/CLI/Generator.cs
  3. 14
      src/Generator/Driver.cs
  4. 31
      src/Generator/Generators/Emscripten/EmscriptenSources.cs
  5. 2
      src/Generator/Options.cs
  6. 8
      src/Generator/Passes/CheckIgnoredDecls.cs
  7. 22
      tests/Classes.h
  8. 23
      tests/emscripten/bindings.lua
  9. 9
      tests/emscripten/test.mjs
  10. 76
      tests/emscripten/test.sh
  11. 64
      tests/napi/test.sh
  12. 75
      tests/quickjs/test.sh
  13. 2
      tests/test.sh
  14. 58
      tests/ts/test.sh

7
.github/workflows/main.yml

@ -37,7 +37,7 @@ jobs: @@ -37,7 +37,7 @@ jobs:
- uses: actions/checkout@v4
- name: Setup emsdk
uses: mymindstorm/setup-emsdk@v11
uses: mymindstorm/setup-emsdk@v14
with:
version: ${{ env.EMSCRIPTEN_VERSION }}
actions-cache-folder: emsdk-cache-${{ runner.os }}
@ -88,14 +88,15 @@ jobs: @@ -88,14 +88,15 @@ jobs:
- name: Test (QuickJS)
if: runner.os != 'Windows'
shell: bash
run: tests/quickjs/test.sh -dotnet_configuration $BUILD_CONFIGURATION
run: tests/quickjs/test.sh --dotnet-config $BUILD_CONFIGURATION
- name: Test (Emscripten)
if: runner.os != 'Windows'
shell: bash
run: tests/emscripten/test.sh -dotnet_configuration $BUILD_CONFIGURATION
run: tests/emscripten/test.sh --dotnet-config $BUILD_CONFIGURATION
- name: Pack
if: matrix.build-cfg == 'Release'
shell: bash
run: build/build.sh prepack -platform $PLATFORM -configuration $BUILD_CONFIGURATION

7
src/CLI/Generator.cs

@ -90,6 +90,13 @@ namespace CppSharp @@ -90,6 +90,13 @@ namespace CppSharp
options.Platform ??= Platform.Host;
if (options.Architecture is TargetArchitecture.WASM32 or TargetArchitecture.WASM64 &&
options.Platform is not TargetPlatform.Emscripten)
{
messages.Add("Please set Emscripten platform for WASM architectures.");
return false;
}
if (string.IsNullOrEmpty(options.OutputDir))
{
options.OutputDir = Path.Combine(Directory.GetCurrentDirectory(), "gen");

14
src/Generator/Driver.cs

@ -252,7 +252,12 @@ namespace CppSharp @@ -252,7 +252,12 @@ namespace CppSharp
passes.AddPass(new CleanInvalidDeclNamesPass());
passes.AddPass(new FastDelegateToDelegatesPass());
passes.AddPass(new FieldToPropertyPass());
if (Options.GeneratorKind != GeneratorKind.Emscripten)
{
passes.AddPass(new FieldToPropertyPass());
}
passes.AddPass(new CheckIgnoredDeclsPass());
passes.AddPass(new CheckEnumsPass());
passes.AddPass(new MakeProtectedNestedTypesPublicPass());
@ -283,9 +288,14 @@ namespace CppSharp @@ -283,9 +288,14 @@ namespace CppSharp
passes.AddPass(new CheckDuplicatedNamesPass());
if (Options.IsCLIGenerator || Options.IsCSharpGenerator)
if (Options.IsCLIGenerator || Options.IsCSharpGenerator
|| Options.GeneratorKind.ID is GeneratorKind.Emscripten_ID)
{
passes.RenameDeclsUpperCase(RenameTargets.Any & ~RenameTargets.Parameter);
}
if (Options.IsCLIGenerator || Options.IsCSharpGenerator)
{
passes.AddPass(new CheckKeywordNamesPass());
}

31
src/Generator/Generators/Emscripten/EmscriptenSources.cs

@ -117,11 +117,6 @@ namespace CppSharp.Generators.Emscripten @@ -117,11 +117,6 @@ namespace CppSharp.Generators.Emscripten
}
}
public override bool VisitProperty(Property property)
{
return true;
}
public override bool VisitMethodDecl(Method method)
{
Indent();
@ -130,9 +125,33 @@ namespace CppSharp.Generators.Emscripten @@ -130,9 +125,33 @@ namespace CppSharp.Generators.Emscripten
return ret;
}
public override bool VisitProperty(Property property)
{
if (property.Field != null)
return property.Field.Visit(this);
if (!property.GetMethod.IsConst)
{
Console.WriteLine($"Cannot bind non-const property getter method: {property.GetMethod.QualifiedOriginalName}");
return false;
}
var @class = property.Namespace as Class;
Indent();
Write($".property(\"{property.Name}\", &{@class.QualifiedOriginalName}::{property.GetMethod.OriginalName}");
if (property.HasSetter)
Write($", &{@class.QualifiedOriginalName}::{property.SetMethod.OriginalName}");
WriteLine(")");
Unindent();
return true;
}
public override bool VisitFieldDecl(Field field)
{
WriteLineIndent($".field(\"{field.Name}\", &{field.Class.QualifiedOriginalName}::{field.OriginalName})");
WriteLineIndent($".property(\"{field.Name}\", &{field.Class.QualifiedOriginalName}::{field.OriginalName})");
return true;
}

2
src/Generator/Options.cs

@ -205,6 +205,8 @@ namespace CppSharp @@ -205,6 +205,8 @@ namespace CppSharp
public bool IsCLIGenerator => GeneratorKind == GeneratorKind.CLI;
public bool IsJSGenerator => GeneratorKind == GeneratorKind.Emscripten || GeneratorKind == GeneratorKind.QuickJS;
public readonly List<string> DependentNameSpaces = new List<string>();
public bool MarshalCharAsManagedChar { get; set; }
public bool MarshalConstCharArrayAsString { get; set; } = true;

8
src/Generator/Passes/CheckIgnoredDecls.cs

@ -184,6 +184,14 @@ namespace CppSharp.Passes @@ -184,6 +184,14 @@ namespace CppSharp.Passes
return false;
}
if (Options.IsJSGenerator && function is Method { Kind: CXXMethodKind.Normal } && ret.Type.GetFinalPointee().IsClass())
{
function.ExplicitlyIgnore();
Diagnostics.Debug("Function '{0}' was ignored due to {1} return decl not yet implemented in JS generators",
function.Name, msg);
return false;
}
foreach (var param in function.Parameters)
{
if (HasInvalidDecl(param, out msg))

22
tests/Classes.h

@ -6,8 +6,8 @@ class Class @@ -6,8 +6,8 @@ class Class
{
public:
void ReturnsVoid() {}
int ReturnsInt() { return 0; }
// Class* PassAndReturnsClassPtr(Class* obj) { return obj; }
int ReturnsInt() const { return 0; }
Class* PassAndReturnsClassPtr(Class* obj) { return obj; }
};
class ClassWithField
@ -18,7 +18,21 @@ public: @@ -18,7 +18,21 @@ public:
{
}
int Field;
int ReturnsField() { return Field; }
int ReturnsField() const { return Field; }
};
class ClassWithProperty
{
public:
ClassWithProperty()
: Field(10)
{
}
int GetField() const { return Field; }
void SetField(int value) { Field = value; }
private:
int Field;
};
class ClassWithOverloads
@ -41,4 +55,4 @@ class ClassWithExternalInheritance : public ClassFromAnotherUnit @@ -41,4 +55,4 @@ class ClassWithExternalInheritance : public ClassFromAnotherUnit
};
// void FunctionPassClassByRef(Class* klass) { }
// Class* FunctionReturnsClassByRef() { return new Class(); }
// Class* FunctionReturnsClassByRef() { return new Class(); }

23
tests/emscripten/bindings.lua

@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
generator "emscripten"
platform "emscripten"
architecture "wasm32"
includedirs
{
"..",
"../../include",
}
output "gen"
module "tests"
namespace "test"
headers
{
"Builtins.h",
"Classes.h",
"Classes2.h",
"Delegates.h",
"Enums.h",
"Overloads.h"
}

9
tests/emscripten/test.mjs

@ -111,7 +111,14 @@ function classes() { @@ -111,7 +111,14 @@ function classes() {
var classWithField = new test.ClassWithField();
eq(classWithField.ReturnsField(), 10);
//eq(classWithField.Field, 10);
eq(classWithField.Field, 10);
classWithField.Field = 20;
eq(classWithField.ReturnsField(), 20);
var classWithProperty = new test.ClassWithProperty();
eq(classWithProperty.Field, 10);
classWithProperty.Field = 20;
eq(classWithProperty.Field, 20);
}
builtins();

76
tests/emscripten/test.sh

@ -1,27 +1,46 @@ @@ -1,27 +1,46 @@
#!/usr/bin/env bash
set -e
dir=$(cd "$(dirname "$0")"; pwd)
rootdir="$dir/../.."
dotnet_configuration=Release
configuration=debug
dotnet_configuration=DebugOpt
make_configuration=debug
platform=x64
jsinterp=$(which node)
for arg in "$@"; do
case $arg in
--with-node=*)
jsinterp="${arg#*=}"
shift
;;
-configuration)
configuration=$2
shift
;;
-dotnet_configuration)
dotnet_configuration=$2
shift
;;
esac
usage() {
cat <<EOF
Usage: $(basename $0) [--with-node=NODE] [--make-config CONFIG] [--dotnet-config CONFIG]
EOF
exit 1
}
while [[ $# -gt 0 ]]; do
case "$1" in
--with-node=*)
jsinterp="${1#*=}"
shift
;;
--with-node)
jsinterp="$2"
shift 2
;;
--make-config|--make-configuration)
make_configuration="$2"
shift 2
;;
--dotnet-config|--dotnet-configuration)
dotnet_configuration="$2"
shift 2
;;
-h|--help)
usage
;;
*)
echo "Unknown option: $1" >&2
usage
;;
esac
done
if [ "$CI" = "true" ]; then
@ -34,20 +53,21 @@ else @@ -34,20 +53,21 @@ else
reset=`tput sgr0`
fi
# 1) Generate
generate=true
if [ $generate = true ]; then
echo "${green}Generating bindings${reset}"
dotnet $rootdir/bin/${dotnet_configuration}/CppSharp.CLI.dll \
--gen=emscripten --platform=emscripten --arch=wasm32 \
-I$dir/.. -I$rootdir/include -o $dir/gen -m tests $dir/../*.h
echo "${green}Generating bindings with .NET configuration $dotnet_configuration${reset}"
dotnet "$rootdir/bin/${dotnet_configuration}/CppSharp.CLI.dll" --property=keywords \
"$dir/bindings.lua"
fi
echo "${green}Building generated binding files${reset}"
premake=$rootdir/build/premake.sh
config=$configuration $premake --file=$dir/premake5.lua gmake
emmake make -C $dir/gen
echo
# 2) Build
echo "${green}Building generated binding files (make config: $make_configuration)${reset}"
premake="$rootdir/build/premake.sh"
"$premake" --file=$dir/premake5.lua gmake2
config=$make_configuration emmake make -C "$dir/gen"
# 3) Test
echo
echo "${green}Executing JS tests with Node${reset}"
$jsinterp $dir/test.mjs
"$jsinterp" "$dir/test.mjs"

64
tests/napi/test.sh

@ -1,9 +1,47 @@ @@ -1,9 +1,47 @@
#!/usr/bin/env bash
set -e
dir=$(cd "$(dirname "$0")"; pwd)
rootdir="$dir/../.."
configuration=Release
dotnet_configuration=DebugOpt
make_configuration=debug
platform=x64
jsinterp=$(which node)
usage() {
cat <<EOF
Usage: $(basename $0) [--with-node=NODE] [--make-config CONFIG] [--dotnet-config CONFIG]
EOF
exit 1
}
while [[ $# -gt 0 ]]; do
case "$1" in
--with-node=*)
jsinterp="${1#*=}"
shift
;;
--with-node)
jsinterp="$2"
shift 2
;;
--make-config|--make-configuration)
make_configuration="$2"
shift 2
;;
--dotnet-config|--dotnet-configuration)
dotnet_configuration="$2"
shift 2
;;
-h|--help)
usage
;;
*)
echo "Unknown option: $1" >&2
usage
;;
esac
done
if [ "$CI" = "true" ]; then
red=""
@ -15,15 +53,21 @@ else @@ -15,15 +53,21 @@ else
reset=`tput sgr0`
fi
echo "${green}Generating bindings${reset}"
dotnet $rootdir/bin/${configuration}/CppSharp.CLI.dll \
--gen=napi -I$dir/.. -o $dir/gen -m tests $dir/../*.h
# 1) Generate
generate=true
if [ $generate = true ]; then
echo "${green}Generating bindings with .NET configuration $dotnet_configuration${reset}"
dotnet "$rootdir/bin/${dotnet_configuration}/CppSharp.CLI.dll" \
--gen=napi -I$dir/.. -o $dir/gen -m tests $dir/../*.h
fi
echo "${green}Building generated binding files${reset}"
premake=$rootdir/build/premake.sh
$premake --file=$dir/premake5.lua gmake
make -C $dir/gen
echo
# 2) Build
echo "${green}Building generated binding files (make config: $make_configuration)${reset}"
premake="$rootdir/build/premake.sh"
"$premake" --file=$dir/premake5.lua gmake
config=$make_configuration make -C "$dir/gen"
# 3) Test
echo
echo "${green}Executing JS tests with Node${reset}"
node $dir/test.js
"$jsinterp" "$dir/test.js"

75
tests/quickjs/test.sh

@ -1,25 +1,47 @@ @@ -1,25 +1,47 @@
#!/usr/bin/env bash
set -e
dir=$(cd "$(dirname "$0")"; pwd)
rootdir="$dir/../.."
dotnet_configuration=Release
configuration=debug
dotnet_configuration=DebugOpt
make_configuration=debug
platform=x64
jsinterp="$dir/runtime/build/qjs"
for arg in "$@"; do
case $arg in
-configuration)
configuration=$2
shift
;;
-dotnet_configuration)
dotnet_configuration=$2
shift
;;
esac
done
usage() {
cat <<EOF
Usage: $(basename $0) [--with-node=NODE] [--make-config CONFIG] [--dotnet-config CONFIG]
EOF
exit 1
}
cd $dir
while [[ $# -gt 0 ]]; do
case "$1" in
--with-node=*)
jsinterp="${1#*=}"
shift
;;
--with-node)
jsinterp="$2"
shift 2
;;
--make-config|--make-configuration)
make_configuration="$2"
shift 2
;;
--dotnet-config|--dotnet-configuration)
dotnet_configuration="$2"
shift 2
;;
-h|--help)
usage
;;
*)
echo "Unknown option: $1" >&2
usage
;;
esac
done
if [ "$CI" = "true" ]; then
red=""
@ -31,20 +53,21 @@ else @@ -31,20 +53,21 @@ else
reset=`tput sgr0`
fi
# 1) Generate
generate=true
if [ $generate = true ]; then
echo "${green}Generating bindings${reset}"
dotnet $rootdir/bin/${dotnet_configuration}/CppSharp.CLI.dll \
$dir/bindings.lua
echo "${green}Generating bindings with .NET configuration $dotnet_configuration${reset}"
dotnet "$rootdir/bin/${dotnet_configuration}/CppSharp.CLI.dll" "$dir/bindings.lua"
fi
echo "${green}Building generated binding files${reset}"
premake=$rootdir/build/premake.sh
config=$configuration $premake --file=$dir/premake5.lua gmake2
verbose=true make -C $dir/gen
echo
# 2) Build
echo "${green}Building generated binding files (make config: $make_configuration)${reset}"
premake="$rootdir/build/premake.sh"
"$premake" --file=$dir/premake5.lua gmake2
config=$make_configuration verbose=true make -C "$dir/gen"
# 3) Test
echo
echo "${green}Executing JS tests with QuickJS${reset}"
cp $dir/gen/bin/$configuration/libtest.so $dir
$jsinterp --std $dir/test.js
cp $dir/gen/bin/$make_configuration/libtest.so $dir
$jsinterp --std $dir/test.js

2
tests/test.sh

@ -2,6 +2,6 @@ @@ -2,6 +2,6 @@
set -e
dir=$(cd "$(dirname "$0")"; pwd)
$dir/napi/test.sh
# $dir/napi/test.sh
$dir/quickjs/test.sh
$dir/emscripten/test.sh

58
tests/ts/test.sh

@ -1,11 +1,47 @@ @@ -1,11 +1,47 @@
#!/usr/bin/env bash
set -e
dir=$(cd "$(dirname "$0")"; pwd)
rootdir="$dir/../.."
dotnet_configuration=Release
configuration=debug
dotnet_configuration=DebugOpt
make_configuration=debug
platform=x64
jsinterp="$rootdir/deps/quickjs/qjs-debug"
jsinterp="$dir/runtime/build/qjs"
usage() {
cat <<EOF
Usage: $(basename $0) [--with-node=NODE] [--make-config CONFIG] [--dotnet-config CONFIG]
EOF
exit 1
}
while [[ $# -gt 0 ]]; do
case "$1" in
--with-node=*)
jsinterp="${1#*=}"
shift
;;
--with-node)
jsinterp="$2"
shift 2
;;
--make-config|--make-configuration)
make_configuration="$2"
shift 2
;;
--dotnet-config|--dotnet-configuration)
dotnet_configuration="$2"
shift 2
;;
-h|--help)
usage
;;
*)
echo "Unknown option: $1" >&2
usage
;;
esac
done
if [ "$CI" = "true" ]; then
red=""
@ -17,22 +53,14 @@ else @@ -17,22 +53,14 @@ else
reset=`tput sgr0`
fi
# 1) Generate
generate=true
if [ $generate = true ]; then
echo "${green}Generating bindings${reset}"
dotnet $rootdir/bin/${dotnet_configuration}/CppSharp.CLI.dll \
echo "${green}Generating bindings with .NET configuration $dotnet_configuration${reset}"
dotnet "$rootdir/bin/${dotnet_configuration}/CppSharp.CLI.dll" \
--gen=ts -I$dir/.. -I$rootdir/include -o $dir/gen -m tests $dir/../*.h
fi
echo "${green}Building generated binding files${reset}"
#make -C $dir/gen
echo
# 2) Type-checking
echo "${green}Typechecking generated binding files with tsc${reset}"
#tsc --noEmit --strict --noImplicitAny --strictNullChecks --strictFunctionTypes --noImplicitThis gen/*.d.ts
# echo "${green}Executing JS tests with QuickJS${reset}"
# cp $dir/gen/bin/$configuration/libtest.so $dir
# #cp $dir/gen/bin/$configuration/libtest.dylib $dir
# $jsinterp --std $dir/test.js
Loading…
Cancel
Save