diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c98fea6f9..08ae12054 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -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: - 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 diff --git a/src/CLI/Generator.cs b/src/CLI/Generator.cs index 7dfce75c5..917fe565b 100644 --- a/src/CLI/Generator.cs +++ b/src/CLI/Generator.cs @@ -90,6 +90,13 @@ public bool ValidateOptions(List messages) 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"); diff --git a/src/Generator/Driver.cs b/src/Generator/Driver.cs index f9be9845b..cf20a268d 100644 --- a/src/Generator/Driver.cs +++ b/src/Generator/Driver.cs @@ -252,7 +252,12 @@ public void SetupPasses(ILibrary library) 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 @@ public void SetupPasses(ILibrary library) 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()); } diff --git a/src/Generator/Generators/Emscripten/EmscriptenSources.cs b/src/Generator/Generators/Emscripten/EmscriptenSources.cs index 3918ef2a5..c1c99a1d9 100644 --- a/src/Generator/Generators/Emscripten/EmscriptenSources.cs +++ b/src/Generator/Generators/Emscripten/EmscriptenSources.cs @@ -117,11 +117,6 @@ public override void VisitClassConstructors(IEnumerable ctors) } } - public override bool VisitProperty(Property property) - { - return true; - } - public override bool VisitMethodDecl(Method method) { Indent(); @@ -130,9 +125,33 @@ public override bool VisitMethodDecl(Method method) 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; } diff --git a/src/Generator/Options.cs b/src/Generator/Options.cs index 0d9a00084..1c078b474 100644 --- a/src/Generator/Options.cs +++ b/src/Generator/Options.cs @@ -205,6 +205,8 @@ public bool DoAllModulesHaveLibraries() => public bool IsCLIGenerator => GeneratorKind == GeneratorKind.CLI; + public bool IsJSGenerator => GeneratorKind == GeneratorKind.Emscripten || GeneratorKind == GeneratorKind.QuickJS; + public readonly List DependentNameSpaces = new List(); public bool MarshalCharAsManagedChar { get; set; } public bool MarshalConstCharArrayAsString { get; set; } = true; diff --git a/src/Generator/Passes/CheckIgnoredDecls.cs b/src/Generator/Passes/CheckIgnoredDecls.cs index 0ebf99f15..84721f208 100644 --- a/src/Generator/Passes/CheckIgnoredDecls.cs +++ b/src/Generator/Passes/CheckIgnoredDecls.cs @@ -184,6 +184,14 @@ public override bool VisitFunctionDecl(Function function) 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)) diff --git a/tests/Classes.h b/tests/Classes.h index 7a43d8a11..718e12ce1 100644 --- a/tests/Classes.h +++ b/tests/Classes.h @@ -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 @@ class ClassWithField { } 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 }; // void FunctionPassClassByRef(Class* klass) { } -// Class* FunctionReturnsClassByRef() { return new Class(); } \ No newline at end of file +// Class* FunctionReturnsClassByRef() { return new Class(); } diff --git a/tests/emscripten/bindings.lua b/tests/emscripten/bindings.lua new file mode 100644 index 000000000..ee1cb53bc --- /dev/null +++ b/tests/emscripten/bindings.lua @@ -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" + } diff --git a/tests/emscripten/test.mjs b/tests/emscripten/test.mjs index fc65f55a5..f6098892f 100644 --- a/tests/emscripten/test.mjs +++ b/tests/emscripten/test.mjs @@ -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(); diff --git a/tests/emscripten/test.sh b/tests/emscripten/test.sh index d57a13df4..11741c453 100755 --- a/tests/emscripten/test.sh +++ b/tests/emscripten/test.sh @@ -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 <&2 + usage + ;; + esac done if [ "$CI" = "true" ]; then @@ -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 \ No newline at end of file +"$jsinterp" "$dir/test.mjs" diff --git a/tests/napi/test.sh b/tests/napi/test.sh index 2f85fa34b..c2244913e 100755 --- a/tests/napi/test.sh +++ b/tests/napi/test.sh @@ -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 <&2 + usage + ;; + esac +done if [ "$CI" = "true" ]; then red="" @@ -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" diff --git a/tests/quickjs/test.sh b/tests/quickjs/test.sh index 102c72696..6326b0dd1 100755 --- a/tests/quickjs/test.sh +++ b/tests/quickjs/test.sh @@ -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 <&2 + usage + ;; + esac +done if [ "$CI" = "true" ]; then red="" @@ -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 \ No newline at end of file +cp $dir/gen/bin/$make_configuration/libtest.so $dir +$jsinterp --std $dir/test.js diff --git a/tests/test.sh b/tests/test.sh index f5abc712b..d9d645413 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -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 diff --git a/tests/ts/test.sh b/tests/ts/test.sh index c4d39ba6a..a96e87589 100755 --- a/tests/ts/test.sh +++ b/tests/ts/test.sh @@ -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 <&2 + usage + ;; + esac +done if [ "$CI" = "true" ]; then red="" @@ -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 \ No newline at end of file