diff --git a/.editorconfig b/.editorconfig
index 1909f880..97af5d1d 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -244,7 +244,7 @@ dotnet_style_qualification_for_method = false:error
dotnet_style_qualification_for_property = false:error
dotnet_style_readonly_field = true:error
-dotnet_style_require_accessibility_modifiers = always:error
+dotnet_style_require_accessibility_modifiers = for_non_interface_members:error
###############################################################################
# Set dotnet style options to:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 1d215dc0..8d0d4eda 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -25,36 +25,46 @@ jobs:
path: |
./artifacts/bin/**/*
./artifacts/log/**/*
- ./artifacts/pkg/**/*
./artifacts/tst/**/*
if-no-files-found: error
- linux-x64:
- runs-on: ${{ matrix.os }}-latest
+ - uses: actions/upload-artifact@v4
+ with:
+ name: ${{ matrix.os }}_${{ matrix.configuration }}_${{ matrix.architecture }}_pkg
+ path: |
+ ./artifacts/pkg/**/*
+ if-no-files-found: error
+ windows-arm64:
+ runs-on: ${{ matrix.os }}-11-arm
strategy:
matrix:
- architecture: [ x64 ]
+ architecture: [ arm64 ]
configuration: [ debug, release ]
- os: [ ubuntu ]
+ os: [ windows ]
steps:
- uses: actions/checkout@v4
- - run: ./scripts/cibuild.sh --configuration ${{ matrix.configuration }} --architecture ${{ matrix.architecture }}
- shell: bash
+ - run: ./scripts/cibuild.cmd -configuration ${{ matrix.configuration }} -architecture ${{ matrix.architecture }}
+ shell: cmd
- uses: actions/upload-artifact@v4
with:
name: ${{ matrix.os }}_${{ matrix.configuration }}_${{ matrix.architecture }}
path: |
./artifacts/bin/**/*
./artifacts/log/**/*
- ./artifacts/pkg/**/*
./artifacts/tst/**/*
if-no-files-found: error
- macos-arm64:
+ - uses: actions/upload-artifact@v4
+ with:
+ name: ${{ matrix.os }}_${{ matrix.configuration }}_${{ matrix.architecture }}_pkg
+ path: |
+ ./artifacts/pkg/**/*.win-arm64.*
+ if-no-files-found: error
+ linux-x64:
runs-on: ${{ matrix.os }}-latest
strategy:
matrix:
- architecture: [ arm64 ]
+ architecture: [ x64 ]
configuration: [ debug, release ]
- os: [ macos ]
+ os: [ ubuntu ]
steps:
- uses: actions/checkout@v4
- run: ./scripts/cibuild.sh --configuration ${{ matrix.configuration }} --architecture ${{ matrix.architecture }}
@@ -65,100 +75,103 @@ jobs:
path: |
./artifacts/bin/**/*
./artifacts/log/**/*
- ./artifacts/pkg/**/*
./artifacts/tst/**/*
if-no-files-found: error
- build-nuget-preview:
- runs-on: windows-latest
+ - uses: actions/upload-artifact@v4
+ with:
+ name: ${{ matrix.os }}_${{ matrix.configuration }}_${{ matrix.architecture }}_pkg
+ path: |
+ ./artifacts/pkg/**/*.linux-x64.*
+ if-no-files-found: error
+ linux-arm64:
+ runs-on: ${{ matrix.os }}-24.04-arm
+ strategy:
+ matrix:
+ architecture: [ arm64 ]
+ configuration: [ debug, release ]
+ os: [ ubuntu ]
steps:
- uses: actions/checkout@v4
- - run: ./scripts/cibuild.cmd -configuration release -architecture x64
- shell: cmd
- env:
- EXCLUDE_RUN_ID_FROM_PACKAGE: true
- EXCLUDE_SUFFIX_FROM_VERSION: false
+ - run: ./scripts/cibuild.sh --configuration ${{ matrix.configuration }} --architecture ${{ matrix.architecture }}
+ shell: bash
- uses: actions/upload-artifact@v4
with:
- name: nuget_preview
+ name: ${{ matrix.os }}_${{ matrix.configuration }}_${{ matrix.architecture }}
path: |
./artifacts/bin/**/*
./artifacts/log/**/*
- ./artifacts/pkg/**/*
./artifacts/tst/**/*
if-no-files-found: error
- sign-nuget-preview:
- runs-on: windows-latest
- if: ${{ github.event_name == 'push' }}
- needs: [ build-nuget-preview ]
- permissions:
- id-token: write
- steps:
- - uses: actions/checkout@v4
- - uses: actions/download-artifact@v4
- with:
- name: nuget_preview
- path: ./artifacts
- - uses: actions/setup-dotnet@v4
- with:
- global-json-file: ./global.json
- - run: dotnet tool install --tool-path ./artifacts/tools sign --version 0.9.1-beta.24170.3
- - run: ./artifacts/tools/sign code azure-key-vault "**/*.nupkg" --timestamp-url "http://timestamp.digicert.com" --base-directory "${{ github.workspace }}/artifacts/pkg" --file-list "${{ github.workspace }}/scripts/SignClientFileList.txt" --publisher-name ".NET Foundation" --description "ClangSharp" --description-url "https://github.com/dotnet/clangsharp" --azure-key-vault-certificate "${{ secrets.SC_KEY_VAULT_CERTIFICATE_ID }}" --azure-key-vault-client-id "${{ secrets.SC_AZURE_CLIENT_ID }}" --azure-key-vault-client-secret "${{ secrets.SC_AZURE_CLIENT_SECRET }}" --azure-key-vault-tenant-id "${{ secrets.SC_AZURE_TENANT_ID }}" --azure-key-vault-url "${{ secrets.SC_KEY_VAULT_URL }}"
- uses: actions/upload-artifact@v4
with:
- name: sign_nuget_preview
+ name: ${{ matrix.os }}_${{ matrix.configuration }}_${{ matrix.architecture }}_pkg
path: |
- ./artifacts/pkg/**/*
+ ./artifacts/pkg/**/*.linux-arm64.*
if-no-files-found: error
- build-nuget-release:
- runs-on: windows-latest
+ macos-arm64:
+ runs-on: ${{ matrix.os }}-latest
+ strategy:
+ matrix:
+ architecture: [ arm64 ]
+ configuration: [ debug, release ]
+ os: [ macos ]
steps:
- uses: actions/checkout@v4
- - run: ./scripts/cibuild.cmd -configuration release -architecture x64
- shell: cmd
- env:
- EXCLUDE_RUN_ID_FROM_PACKAGE: true
- EXCLUDE_SUFFIX_FROM_VERSION: true
+ - run: ./scripts/cibuild.sh --configuration ${{ matrix.configuration }} --architecture ${{ matrix.architecture }}
+ shell: bash
- uses: actions/upload-artifact@v4
with:
- name: nuget_release
+ name: ${{ matrix.os }}_${{ matrix.configuration }}_${{ matrix.architecture }}
path: |
./artifacts/bin/**/*
./artifacts/log/**/*
- ./artifacts/pkg/**/*
./artifacts/tst/**/*
if-no-files-found: error
- sign-nuget-release:
+ - uses: actions/upload-artifact@v4
+ with:
+ name: ${{ matrix.os }}_${{ matrix.configuration }}_${{ matrix.architecture }}_pkg
+ path: |
+ ./artifacts/pkg/**/*.osx-arm64.*
+ if-no-files-found: error
+ sign-nuget:
runs-on: windows-latest
if: ${{ github.event_name == 'push' }}
- needs: [ build-nuget-release ]
+ needs: [ windows-x64, windows-arm64, linux-x64, linux-arm64, macos-arm64 ]
permissions:
id-token: write
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
- name: nuget_release
- path: ./artifacts
+ merge-multiple: true
+ path: ./artifacts/pkg
+ pattern: "*_release_*_pkg"
- uses: actions/setup-dotnet@v4
with:
global-json-file: ./global.json
- - run: dotnet tool install --tool-path ./artifacts/tools sign --version 0.9.1-beta.24170.3
- - run: ./artifacts/tools/sign code azure-key-vault "**/*.nupkg" --timestamp-url "http://timestamp.digicert.com" --base-directory "${{ github.workspace }}/artifacts/pkg" --file-list "${{ github.workspace }}/scripts/SignClientFileList.txt" --publisher-name ".NET Foundation" --description "ClangSharp" --description-url "https://github.com/dotnet/clangsharp" --azure-key-vault-certificate "${{ secrets.SC_KEY_VAULT_CERTIFICATE_ID }}" --azure-key-vault-client-id "${{ secrets.SC_AZURE_CLIENT_ID }}" --azure-key-vault-client-secret "${{ secrets.SC_AZURE_CLIENT_SECRET }}" --azure-key-vault-tenant-id "${{ secrets.SC_AZURE_TENANT_ID }}" --azure-key-vault-url "${{ secrets.SC_KEY_VAULT_URL }}"
+ - run: dotnet tool install --tool-path ./artifacts/tools --prerelease sign
+ - uses: azure/login@v2
+ with:
+ allow-no-subscriptions: true
+ client-id: "${{ secrets.AZURE_CLIENT_ID }}"
+ tenant-id: "${{ secrets.AZURE_TENANT_ID }}"
+ - run: ./artifacts/tools/sign code azure-key-vault "**/*.nupkg" --base-directory "${{ github.workspace }}/artifacts/pkg" --file-list "${{ github.workspace }}/scripts/SignClientFileList.txt" --publisher-name ".NET Foundation" --description "ClangSharp" --description-url "https://github.com/dotnet/clangsharp" --azure-credential-type "azure-cli" --azure-key-vault-url "${{ secrets.KEY_VAULT_URL }}" --azure-key-vault-certificate "${{ secrets.KEY_VAULT_CERTIFICATE_ID }}"
+ shell: pwsh
- uses: actions/upload-artifact@v4
with:
- name: sign_nuget_release
+ name: sign_nuget
path: |
./artifacts/pkg/**/*
if-no-files-found: error
publish-nightlies-azure:
runs-on: ubuntu-latest
if: ${{ github.event_name == 'push' }}
- needs: [ windows-x64, linux-x64, macos-arm64, sign-nuget-preview, sign-nuget-release ]
+ needs: [ sign-nuget ]
steps:
- uses: actions/download-artifact@v4
with:
- name: windows_release_x64
- path: ./artifacts
+ name: sign_nuget
+ path: ./artifacts/pkg
- uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
@@ -169,12 +182,12 @@ jobs:
publish-nightlies-github:
runs-on: ubuntu-latest
if: false
- needs: [ windows-x64, linux-x64, macos-arm64, sign-nuget-preview, sign-nuget-release ]
+ needs: [ sign-nuget ]
steps:
- uses: actions/download-artifact@v4
with:
- name: windows_release_x64
- path: ./artifacts
+ name: sign_nuget
+ path: ./artifacts/pkg
- uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
diff --git a/Directory.Build.props b/Directory.Build.props
index 4a822fa1..af219597 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -43,12 +43,12 @@
.NET Foundation
true
true
- $(BaseArtifactsPath)pkg/$(Configuration)/
+ $(BaseArtifactsPath)pkg/$(Configuration)/$(PACKAGE_PUBLISH_MODE)
17.0.0
ClangSharp
ClangSharp
- 20.1.2
- rc1
+ 20.1.2.3
+ rc1
pr
diff --git a/Directory.Build.targets b/Directory.Build.targets
index db24925f..bd6fed59 100644
--- a/Directory.Build.targets
+++ b/Directory.Build.targets
@@ -17,7 +17,7 @@
- $(Version).$(GITHUB_RUN_ID)
+ $(Version).$(GITHUB_RUN_ID)
@@ -31,10 +31,9 @@
true
-
-
+
+
$(NETCoreSdkRuntimeIdentifier)
- $(OVERRIDE_RUNTIME_IDENTIFIER)
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 0c1271a9..b9a02164 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -14,9 +14,9 @@
-
-
-
+
+
+
diff --git a/README.md b/README.md
index 570848c8..df4ca5f2 100644
--- a/README.md
+++ b/README.md
@@ -55,7 +55,7 @@ See [LICENSE.md](LICENSE.md) in the repository root for more information.
### Building Managed
-ClangSharp requires the [.NET 8 SDK](https://dotnet.microsoft.com/download/dotnet/8.0) and can be built simply with `dotnet build -c Release`.
+ClangSharp requires the [.NET 10 SDK](https://dotnet.microsoft.com/download/dotnet/10.0) and can be built simply with `dotnet build -c Release`.
You can reproduce what the CI environment does by running `./scripts/cibuild.cmd` on Windows or `./scripts.cibuild.sh` on Unix.
This will download the required .NET SDK locally and use that to build the repo; it will also run through all available actions in the appropriate order.
@@ -84,7 +84,7 @@ cd artifacts/bin
cmake -DCMAKE_INSTALL_PREFIX=../install -DLLVM_ENABLE_PROJECTS=clang -G "Visual Studio 17 2022" -A x64 -Thost=x64 ../../llvm
```
-You can then open `LLVM.sln` in Visual Studio, change the configuration to `Release` and build the `install` project.
+You can then open `LLVM.sln` in Visual Studio, change the configuration to `Release` and build the `INSTALL` project. You may need to build the `ALL_BUILD` first.
Afterwards, you can then build `libClangSharp` where the process followed is roughly:
```cmd
@@ -92,10 +92,10 @@ git clone https://github.com/dotnet/clangsharp
cd clangsharp
mkdir artifacts/bin/native
cd artifacts/bin/native
-cmake -DCMAKE_INSTALL_PREFIX=../install -DPATH_TO_LLVM=../../../../llvm-project/artifacts/install -G "Visual Studio 17 2022" -A x64 -Thost=x64 ../../..
+cmake -DCMAKE_INSTALL_PREFIX=../install -DPATH_TO_LLVM=absolute/path/to/repos/llvm-project/artifacts/install -G "Visual Studio 17 2022" -A x64 -Thost=x64 ../../..
```
-You can then open `libClangSharp.sln` in Visual Studio, change the configuration to `Release` and build the `install` project.
+You can then open `libClangSharp.sln` in Visual Studio, change the configuration to `Release` and build the `INSTALL` project.
#### Linux
@@ -133,7 +133,7 @@ This program will take a given set of C or C++ header files and generate C# bind
The simplest and recommended setup is to install the generator as a .NET tool and then use response files:
```
-dotnet tool install --global ClangSharpPInvokeGenerator --version 20.1.2
+dotnet tool install --global ClangSharpPInvokeGenerator
ClangSharpPInvokeGenerator @generate.rsp
```
@@ -202,9 +202,9 @@ Options:
# Codegen Options
compatible-codegen Bindings should be generated with .NET Standard 2.0 compatibility. Setting this disables preview code generation.
- default-codegen Bindings should be generated for the previous LTS version of .NET/C#. This is currently .NET 6/C# 10.
- latest-codegen Bindings should be generated for the current LTS/STS version of .NET/C#. This is currently .NET 8/C# 12.
- preview-codegen Bindings should be generated for the preview version of .NET/C#. This is currently .NET 9/C# 13.
+ default-codegen Bindings should be generated for the previous LTS version of .NET/C#. This is currently .NET 8/C# 12.
+ latest-codegen Bindings should be generated for the current LTS/STS version of .NET/C#. This is currently .NET 10/C# 14.
+ preview-codegen Bindings should be generated for the preview version of .NET/C#. This is currently .NET 10/C# 14.
# File Options
diff --git a/global.json b/global.json
index 61d88a71..ff239fd9 100644
--- a/global.json
+++ b/global.json
@@ -1,6 +1,6 @@
{
"sdk": {
- "version": "8.0.100",
+ "version": "10.0.100-preview",
"allowPrerelease": true,
"rollForward": "latestFeature"
}
diff --git a/scripts/build.ps1 b/scripts/build.ps1
index 882eba52..89ae10f3 100644
--- a/scripts/build.ps1
+++ b/scripts/build.ps1
@@ -56,8 +56,18 @@ function Help() {
}
function Pack() {
- $logFile = Join-Path -Path $LogDir -ChildPath "$configuration\pack.binlog"
- & dotnet pack -c "$configuration" --no-build --no-restore -v "$verbosity" /bl:"$logFile" /err $properties "$solution"
+ $logFile = Join-Path -Path $LogDir -ChildPath "$configuration\pack"
+
+ & dotnet pack -c "$configuration" --no-build --no-restore -v "$verbosity" /bl:"$logFile.binlog" /err $properties "$solution"
+ & dotnet pack -c "$configuration" --no-build --no-restore -v "$verbosity" /bl:"$logFile.agnostic.binlog" /err /p:SKIP_USE_CURRENT_RUNTIME=true $properties "$solution"
+
+ if ($ci) {
+ & dotnet pack -c "$configuration" --no-build --no-restore -v "$verbosity" /bl:"$logFile.preview.binlog" /err /p:PACKAGE_PUBLISH_MODE=preview $properties "$solution"
+ & dotnet pack -c "$configuration" --no-build --no-restore -v "$verbosity" /bl:"$logFile.stable.binlog" /err /p:PACKAGE_PUBLISH_MODE=stable $properties "$solution"
+
+ & dotnet pack -c "$configuration" --no-build --no-restore -v "$verbosity" /bl:"$logFile.agnostic.preview.binlog" /err /p:SKIP_USE_CURRENT_RUNTIME=true /p:PACKAGE_PUBLISH_MODE=preview $properties "$solution"
+ & dotnet pack -c "$configuration" --no-build --no-restore -v "$verbosity" /bl:"$logFile.agnostic.stable.binlog" /err /p:SKIP_USE_CURRENT_RUNTIME=true /p:PACKAGE_PUBLISH_MODE=stable $properties "$solution"
+ }
if ($LastExitCode -ne 0) {
throw "'Pack' failed for '$solution'"
@@ -172,7 +182,7 @@ try {
$DotNetInstallDirectory = Join-Path -Path $ArtifactsDir -ChildPath "dotnet"
Create-Directory -Path $DotNetInstallDirectory
- & $DotNetInstallScript -Channel 8.0 -Version latest -InstallDir $DotNetInstallDirectory -Architecture $architecture
+ & $DotNetInstallScript -Channel 10.0 -Version latest -InstallDir $DotNetInstallDirectory -Architecture $architecture
$env:PATH="$DotNetInstallDirectory;$env:PATH"
}
diff --git a/scripts/build.sh b/scripts/build.sh
index c435695f..81d483ba 100755
--- a/scripts/build.sh
+++ b/scripts/build.sh
@@ -115,14 +115,32 @@ function Help {
}
function Pack {
- logFile="$LogDir/$configuration/pack.binlog"
+ logFile="$LogDir/$configuration/pack"
if [[ -z "$properties" ]]; then
- dotnet pack -c "$configuration" --no-build --no-restore -v "$verbosity" /bl:"$logFile" /err "$solution"
+ dotnet pack -c "$configuration" --no-build --no-restore -v "$verbosity" /bl:"$logFile.binlog" /err "$solution"
+ dotnet pack -c "$configuration" --no-build --no-restore -v "$verbosity" /bl:"$logFile.agnostic.binlog" /err /p:SKIP_USE_CURRENT_RUNTIME=true "$solution"
else
- dotnet pack -c "$configuration" --no-build --no-restore -v "$verbosity" /bl:"$logFile" /err "${properties[@]}" "$solution"
+ dotnet pack -c "$configuration" --no-build --no-restore -v "$verbosity" /bl:"$logFile.binlog" /err "${properties[@]}" "$solution"
+ dotnet pack -c "$configuration" --no-build --no-restore -v "$verbosity" /bl:"$logFile.agnostic.binlog" /err /p:SKIP_USE_CURRENT_RUNTIME=true "${properties[@]}" "$solution"
fi
+if $ci; then
+ if [[ -z "$properties" ]]; then
+ dotnet pack -c "$configuration" --no-build --no-restore -v "$verbosity" /bl:"$logFile.preview.binlog" /err /p:PACKAGE_PUBLISH_MODE=preview "$solution"
+ dotnet pack -c "$configuration" --no-build --no-restore -v "$verbosity" /bl:"$logFile.stable.binlog" /err /p:PACKAGE_PUBLISH_MODE=stable "$solution"
+
+ dotnet pack -c "$configuration" --no-build --no-restore -v "$verbosity" /bl:"$logFile.agnostic.preview.binlog" /err /p:SKIP_USE_CURRENT_RUNTIME=true /p:PACKAGE_PUBLISH_MODE=preview "$solution"
+ dotnet pack -c "$configuration" --no-build --no-restore -v "$verbosity" /bl:"$logFile.agnostic.stable.binlog" /err /p:SKIP_USE_CURRENT_RUNTIME=true /p:PACKAGE_PUBLISH_MODE=stable "$solution"
+ else
+ dotnet pack -c "$configuration" --no-build --no-restore -v "$verbosity" /bl:"$logFile.preview.binlog" /err /p:PACKAGE_PUBLISH_MODE=preview "${properties[@]}" "$solution"
+ dotnet pack -c "$configuration" --no-build --no-restore -v "$verbosity" /bl:"$logFile.stable.binlog" /err /p:PACKAGE_PUBLISH_MODE=stable "${properties[@]}" "$solution"
+
+ dotnet pack -c "$configuration" --no-build --no-restore -v "$verbosity" /bl:"$logFile.agnostic.preview.binlog" /err /p:SKIP_USE_CURRENT_RUNTIME=true /p:PACKAGE_PUBLISH_MODE=preview "${properties[@]}" "$solution"
+ dotnet pack -c "$configuration" --no-build --no-restore -v "$verbosity" /bl:"$logFile.agnostic.stable.binlog" /err /p:SKIP_USE_CURRENT_RUNTIME=true /p:PACKAGE_PUBLISH_MODE=stable "${properties[@]}" "$solution"
+ fi
+fi
+
LASTEXITCODE=$?
if [ "$LASTEXITCODE" != 0 ]; then
@@ -204,7 +222,7 @@ if [[ ! -z "$architecture" ]]; then
DotNetInstallDirectory="$ArtifactsDir/dotnet"
CreateDirectory "$DotNetInstallDirectory"
- . "$DotNetInstallScript" --channel 8.0 --version latest --install-dir "$DotNetInstallDirectory" --architecture "$architecture"
+ . "$DotNetInstallScript" --channel 10.0 --version latest --install-dir "$DotNetInstallDirectory" --architecture "$architecture"
PATH="$DotNetInstallDirectory:$PATH:"
fi
diff --git a/sources/ClangSharp.Interop/ClangSharp.Interop.csproj b/sources/ClangSharp.Interop/ClangSharp.Interop.csproj
index 0e1fe8a1..dd01281b 100644
--- a/sources/ClangSharp.Interop/ClangSharp.Interop.csproj
+++ b/sources/ClangSharp.Interop/ClangSharp.Interop.csproj
@@ -2,7 +2,7 @@
- net8.0
+ net10.0
diff --git a/sources/ClangSharp.Interop/Extensions/CXCursor.cs b/sources/ClangSharp.Interop/Extensions/CXCursor.cs
index e767d1e1..0cccb0b3 100644
--- a/sources/ClangSharp.Interop/Extensions/CXCursor.cs
+++ b/sources/ClangSharp.Interop/Extensions/CXCursor.cs
@@ -37,7 +37,7 @@ public readonly string AttrKindSpelling
Debug.Assert(CX_AttrKind_FirstTypeAttr == CX_AttrKind_AddressSpace);
Debug.Assert(CX_AttrKind_LastTypeAttr == CX_AttrKind_WebAssemblyFuncref);
- Debug.Assert(CX_AttrKind_FirstStmtAttr == CX_AttrKind_CodeAlign);
+ Debug.Assert(CX_AttrKind_FirstStmtAttr == CX_AttrKind_CXXAssume);
Debug.Assert(CX_AttrKind_LastStmtAttr == CX_AttrKind_Unlikely);
Debug.Assert(CX_AttrKind_FirstDeclOrStmtAttr == CX_AttrKind_AlwaysInline);
@@ -49,14 +49,14 @@ public readonly string AttrKindSpelling
Debug.Assert(CX_AttrKind_FirstDeclOrTypeAttr == CX_AttrKind_AArch64SVEPcs);
Debug.Assert(CX_AttrKind_LastDeclOrTypeAttr == CX_AttrKind_VectorCall);
- Debug.Assert(CX_AttrKind_FirstInheritableParamAttr == CX_AttrKind_SwiftAsyncContext);
+ Debug.Assert(CX_AttrKind_FirstInheritableParamAttr == CX_AttrKind_Annotate);
Debug.Assert(CX_AttrKind_LastInheritableParamAttr == CX_AttrKind_UseHandle);
- Debug.Assert(CX_AttrKind_FirstParameterABIAttr == CX_AttrKind_SwiftAsyncContext);
+ Debug.Assert(CX_AttrKind_FirstParameterABIAttr == CX_AttrKind_HLSLParamModifier);
Debug.Assert(CX_AttrKind_LastParameterABIAttr == CX_AttrKind_SwiftIndirectResult);
- Debug.Assert(CX_AttrKind_FirstHLSLAnnotationAttr == CX_AttrKind_HLSLSV_DispatchThreadID);
- Debug.Assert(CX_AttrKind_LastHLSLAnnotationAttr == CX_AttrKind_HLSLSV_GroupIndex);
+ Debug.Assert(CX_AttrKind_FirstHLSLAnnotationAttr == CX_AttrKind_HLSLPackOffset);
+ Debug.Assert(CX_AttrKind_LastHLSLAnnotationAttr == CX_AttrKind_HLSLSV_GroupThreadID);
return AttrKind switch {
CX_AttrKind_Invalid => "Invalid",
@@ -1353,7 +1353,7 @@ public readonly string StmtClassSpelling
Debug.Assert(CX_StmtClass_LastSwitchCase == CX_StmtClass_CaseStmt);
Debug.Assert(CX_StmtClass_FirstOMPLoopTransformationDirective == CX_StmtClass_OMPUnrollDirective);
- Debug.Assert(CX_StmtClass_LastOMPLoopTransformationDirective == CX_StmtClass_OMPTileDirective);
+ Debug.Assert(CX_StmtClass_LastOMPLoopTransformationDirective == CX_StmtClass_OMPInterchangeDirective);
Debug.Assert(CX_StmtClass_FirstOMPLoopDirective == CX_StmtClass_OMPTeamsGenericLoopDirective);
Debug.Assert(CX_StmtClass_LastOMPLoopDirective == CX_StmtClass_OMPDistributeDirective);
@@ -1362,7 +1362,7 @@ public readonly string StmtClassSpelling
Debug.Assert(CX_StmtClass_LastOMPLoopBasedDirective == CX_StmtClass_OMPDistributeDirective);
Debug.Assert(CX_StmtClass_FirstOMPExecutableDirective == CX_StmtClass_OMPTeamsDirective);
- Debug.Assert(CX_StmtClass_LastOMPExecutableDirective == CX_StmtClass_OMPAtomicDirective);
+ Debug.Assert(CX_StmtClass_LastOMPExecutableDirective == CX_StmtClass_OMPAssumeDirective);
Debug.Assert(CX_StmtClass_FirstAsmStmt == CX_StmtClass_MSAsmStmt);
Debug.Assert(CX_StmtClass_LastAsmStmt == CX_StmtClass_GCCAsmStmt);
diff --git a/sources/ClangSharp.Interop/Extensions/CXUnsavedFile.cs b/sources/ClangSharp.Interop/Extensions/CXUnsavedFile.cs
index 3f61354a..cf2bce27 100644
--- a/sources/ClangSharp.Interop/Extensions/CXUnsavedFile.cs
+++ b/sources/ClangSharp.Interop/Extensions/CXUnsavedFile.cs
@@ -46,6 +46,41 @@ public static CXUnsavedFile Create(string filename, string contents)
};
}
+ public static CXUnsavedFile Create(string filename, CXTranslationUnit translationUnit, CXFile baseFile, string additionalContents)
+ {
+ sbyte* pFilename, pContents;
+ nuint contentsLength;
+
+ if (string.IsNullOrEmpty(filename))
+ {
+ pFilename = null;
+ }
+ else
+ {
+ var maxFilenameLength = Encoding.UTF8.GetMaxByteCount(filename.Length);
+ pFilename = (sbyte*)NativeMemory.Alloc((uint)maxFilenameLength + 1);
+ var filenameLength = (uint)Encoding.UTF8.GetBytes(filename, new Span(pFilename, maxFilenameLength));
+ pFilename[filenameLength] = 0;
+ }
+
+ var baseFileContents = translationUnit.GetFileContents(baseFile, out _);
+ var maxContentsLength = baseFileContents.Length + Encoding.UTF8.GetMaxByteCount((additionalContents?.Length).GetValueOrDefault());
+
+ pContents = (sbyte*)NativeMemory.Alloc((uint)maxContentsLength + 1);
+
+ var contents = new Span(pContents, maxContentsLength);
+ baseFileContents.CopyTo(contents);
+
+ contentsLength = (uint)(baseFileContents.Length + Encoding.UTF8.GetBytes(additionalContents, contents[baseFileContents.Length..]));
+ pContents[contentsLength] = 0;
+
+ return new CXUnsavedFile() {
+ Filename = pFilename,
+ Contents = pContents,
+ Length = contentsLength
+ };
+ }
+
public void Dispose()
{
if (Filename != null)
diff --git a/sources/ClangSharp.Interop/clang.cs b/sources/ClangSharp.Interop/clang.cs
index b6546231..037ab5b1 100644
--- a/sources/ClangSharp.Interop/clang.cs
+++ b/sources/ClangSharp.Interop/clang.cs
@@ -41,8 +41,8 @@ private static bool TryResolveClang(Assembly assembly, DllImportSearchPath? sear
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
- return NativeLibrary.TryLoad("libclang.so.18", assembly, searchPath, out nativeLibrary)
- || NativeLibrary.TryLoad("libclang-18", assembly, searchPath, out nativeLibrary)
+ return NativeLibrary.TryLoad("libclang.so.20", assembly, searchPath, out nativeLibrary)
+ || NativeLibrary.TryLoad("libclang-20", assembly, searchPath, out nativeLibrary)
|| NativeLibrary.TryLoad("libclang.so.1", assembly, searchPath, out nativeLibrary);
}
diff --git a/sources/ClangSharp.PInvokeGenerator/Abstractions/FunctionOrDelegateDesc.cs b/sources/ClangSharp.PInvokeGenerator/Abstractions/FunctionOrDelegateDesc.cs
index 0a33d680..8a369cb9 100644
--- a/sources/ClangSharp.PInvokeGenerator/Abstractions/FunctionOrDelegateDesc.cs
+++ b/sources/ClangSharp.PInvokeGenerator/Abstractions/FunctionOrDelegateDesc.cs
@@ -66,6 +66,21 @@ readonly get
}
}
+ public bool IsReadOnly
+ {
+ readonly get
+ {
+ return (Flags & FunctionOrDelegateFlags.IsReadOnly) != 0;
+ }
+
+ set
+ {
+ Flags = value
+ ? Flags | FunctionOrDelegateFlags.IsReadOnly
+ : Flags & ~FunctionOrDelegateFlags.IsReadOnly;
+ }
+ }
+
public bool HasFnPtrCodeGen
{
readonly get
diff --git a/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.Visit.cs b/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.Visit.cs
index 522d5c79..7ffda523 100644
--- a/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.Visit.cs
+++ b/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.Visit.cs
@@ -89,7 +89,7 @@ public void WriteIid(string name, Guid value)
WriteIndented("ReadOnlySpan data = ");
- if (_generator.Config.GenerateLatestCode)
+ if (!_generator.Config.GenerateCompatibleCode)
{
WriteLine('[');
}
@@ -124,7 +124,7 @@ public void WriteIid(string name, Guid value)
WriteNewline();
DecreaseIndentation();
- if (_generator.Config.GenerateLatestCode)
+ if (!_generator.Config.GenerateCompatibleCode)
{
WriteIndented(']');
}
diff --git a/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.VisitDecl.cs b/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.VisitDecl.cs
index 8380a582..5060f29d 100644
--- a/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.VisitDecl.cs
+++ b/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.VisitDecl.cs
@@ -517,6 +517,11 @@ public void BeginFunctionOrDelegate(in FunctionOrDelegateDesc desc, ref bool isM
Write("new ");
}
+ if (desc.IsReadOnly)
+ {
+ Write("readonly ");
+ }
+
if (desc.IsUnsafe)
{
if (!desc.IsCtxCxxRecord)
@@ -808,7 +813,7 @@ public void BeginStruct(in StructDesc desc)
Write(".Interface");
}
- if ((desc.Uuid is not null) && _generator.Config.GenerateGuidMember && _generator.Config.GenerateLatestCode)
+ if ((desc.Uuid is not null) && _generator.Config.GenerateGuidMember && !_generator.Config.GenerateCompatibleCode)
{
Write(desc.HasVtbl ? ", " : " : ");
Write("INativeGuid");
diff --git a/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.cs b/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.cs
index 30b8ccc1..f6d52daa 100644
--- a/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.cs
+++ b/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.cs
@@ -18,8 +18,8 @@ internal sealed partial class CSharpOutputBuilder(string name, PInvokeGenerator
private readonly PInvokeGenerator _generator = generator;
private readonly List _contents = [];
private readonly StringBuilder _currentLine = new StringBuilder();
- private readonly SortedSet _usingDirectives = [];
- private readonly SortedSet _staticUsingDirectives = [];
+ private readonly SortedSet _usingDirectives = new SortedSet(StringComparer.Ordinal);
+ private readonly SortedSet _staticUsingDirectives = new SortedSet(StringComparer.Ordinal);
private readonly string _indentationString = indentationString;
private readonly bool _isTestOutput = isTestOutput;
@@ -80,6 +80,8 @@ public void WriteBlockEnd()
public void Write(T value) => _ = _currentLine.Append(value);
+ public void Write(ReadOnlySpan value) => _ = _currentLine.Append(value);
+
public void WriteIndentation()
{
WriteNewlineIfNeeded();
@@ -102,6 +104,31 @@ public void WriteIndentedLine(T value)
WriteLine(value);
}
+ public void WriteLabel(string name)
+ {
+ if (_currentLine.Length >= _indentationString.Length)
+ {
+ var match = true;
+
+ for (var i = 0; i < _indentationString.Length; i++)
+ {
+ if (_currentLine[_currentLine.Length - i - 1] != _indentationString[_indentationString.Length - 1 - i])
+ {
+ match = false;
+ break;
+ }
+ }
+
+ if (match)
+ {
+ _ = _currentLine.Remove(_currentLine.Length - _indentationString.Length, _indentationString.Length);
+ }
+ }
+
+ Write(name);
+ WriteLine(':');
+ }
+
public void WriteLine(T value)
{
Write(value);
diff --git a/sources/ClangSharp.PInvokeGenerator/ClangSharp.PInvokeGenerator.csproj b/sources/ClangSharp.PInvokeGenerator/ClangSharp.PInvokeGenerator.csproj
index a8c82d4a..bfdcb5f6 100644
--- a/sources/ClangSharp.PInvokeGenerator/ClangSharp.PInvokeGenerator.csproj
+++ b/sources/ClangSharp.PInvokeGenerator/ClangSharp.PInvokeGenerator.csproj
@@ -3,7 +3,7 @@
ClangSharp
- net8.0
+ net10.0
diff --git a/sources/ClangSharp.PInvokeGenerator/FunctionOrDelegateFlags.cs b/sources/ClangSharp.PInvokeGenerator/FunctionOrDelegateFlags.cs
index aeb0eab6..3f4d5396 100644
--- a/sources/ClangSharp.PInvokeGenerator/FunctionOrDelegateFlags.cs
+++ b/sources/ClangSharp.PInvokeGenerator/FunctionOrDelegateFlags.cs
@@ -21,4 +21,5 @@ internal enum FunctionOrDelegateFlags
NeedsReturnFixup = 1 << 13,
IsCxxConstructor = 1 << 14,
IsManualImport = 1 << 15,
+ IsReadOnly = 1 << 16,
}
diff --git a/sources/ClangSharp.PInvokeGenerator/OutputBuilderFactory.cs b/sources/ClangSharp.PInvokeGenerator/OutputBuilderFactory.cs
index 5aa2100f..de4693e7 100644
--- a/sources/ClangSharp.PInvokeGenerator/OutputBuilderFactory.cs
+++ b/sources/ClangSharp.PInvokeGenerator/OutputBuilderFactory.cs
@@ -12,7 +12,7 @@ namespace ClangSharp;
internal sealed class OutputBuilderFactory(PInvokeGenerator generator)
{
private readonly bool _writeSourceLocation = generator.Config.GenerateSourceLocationAttribute;
- private readonly Dictionary _outputBuilders = [];
+ private readonly Dictionary _outputBuilders = new Dictionary(StringComparer.Ordinal);
public IEnumerable OutputBuilders => _outputBuilders.Values;
diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs
index 5a21f8d5..88992629 100644
--- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs
+++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs
@@ -7,11 +7,9 @@
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
-using System.Text.RegularExpressions;
using ClangSharp.Abstractions;
using ClangSharp.CSharp;
using static ClangSharp.Interop.CX_CastKind;
-using static ClangSharp.Interop.CX_CharacterKind;
using static ClangSharp.Interop.CX_DeclKind;
using static ClangSharp.Interop.CX_StmtClass;
using static ClangSharp.Interop.CX_StorageClass;
@@ -607,6 +605,7 @@ private void VisitFunctionDecl(FunctionDecl functionDecl)
IsCxx = cxxMethodDecl is not null,
IsStatic = isDllImport || (cxxMethodDecl is null) || cxxMethodDecl.IsStatic,
NeedsNewKeyword = NeedsNewKeyword(escapedName, functionDecl.Parameters),
+ IsReadOnly = IsReadonly(cxxMethodDecl),
IsUnsafe = IsUnsafe(functionDecl),
IsCtxCxxRecord = cxxRecordDecl is not null,
IsCxxRecordCtxUnsafe = cxxRecordDecl is not null && IsUnsafe(cxxRecordDecl),
@@ -627,7 +626,7 @@ private void VisitFunctionDecl(FunctionDecl functionDecl)
}
},
CustomAttrGeneratorData = (functionDecl, _outputBuilder, this),
- ParameterTypes = overloadCount > 1 ? functionDecl.Parameters.Select(param => GetTargetTypeName(param, out var _)).ToArray() : null,
+ ParameterTypes = overloadCount > 1 ? [.. functionDecl.Parameters.Select(param => GetTargetTypeName(param, out var _))] : null,
};
Debug.Assert(_outputBuilder is not null);
@@ -872,7 +871,7 @@ private void VisitIndirectFieldDecl(IndirectFieldDecl indirectFieldDecl)
return;
}
- if (IsPrevContextDecl(out var prevContext, out _) && prevContext.IsAnonymous)
+ if (IsPrevContextDecl(out var prevContext, out _) && prevContext.IsAnonymousStructOrUnion)
{
// We shouldn't process indirect fields where the prev context is an anonymous record decl
return;
@@ -887,7 +886,7 @@ private void VisitIndirectFieldDecl(IndirectFieldDecl indirectFieldDecl)
var contextNameParts = new Stack();
var contextTypeParts = new Stack();
- while (rootRecordDecl.IsAnonymous && (rootRecordDecl.Parent is RecordDecl parentRecordDecl))
+ while (rootRecordDecl.IsAnonymousStructOrUnion && (rootRecordDecl.Parent is RecordDecl parentRecordDecl))
{
// The name of a field of an anonymous type should be same as the type's name minus the
// type kind tag at the end and the leading `_`.
@@ -944,7 +943,7 @@ private void VisitIndirectFieldDecl(IndirectFieldDecl indirectFieldDecl)
ParentName = GetRemappedCursorName(parent),
Offset = null,
NeedsNewKeyword = false,
- NeedsUnscopedRef = _config.GenerateLatestCode && !fieldDecl.IsBitField,
+ NeedsUnscopedRef = !_config.GenerateCompatibleCode && !fieldDecl.IsBitField,
Location = fieldDecl.Location,
HasBody = true,
WriteCustomAttrs = static context => {
@@ -1110,13 +1109,13 @@ private void VisitIndirectFieldDecl(IndirectFieldDecl indirectFieldDecl)
code.Write(arraySize);
code.Write(')');
}
- else if (!_config.GenerateLatestCode || arraySize == 1)
+ else if (_config.GenerateCompatibleCode || arraySize == 1)
{
code.Write(".AsSpan(");
if (arraySize == 1)
{
- if (TryGetRemappedValue(indirectFieldDecl, _config.WithLengths, out var length))
+ if (TryGetRemappedValue(indirectFieldDecl, _config._withLengths, out var length))
{
code.Write(length);
}
@@ -1390,7 +1389,7 @@ private void VisitRecordDecl(RecordDecl recordDecl)
var maxAlignm = recordDecl.Fields.Any() ? recordDecl.Fields.Max((fieldDecl) => Math.Max(fieldDecl.Type.Handle.AlignOf, 1)) : alignment;
var isTopLevelStruct = _config.WithTypes.TryGetValue(name, out var withType) && withType.Equals("struct", StringComparison.Ordinal);
- var generateTestsClass = !recordDecl.IsAnonymous && recordDecl.DeclContext is not RecordDecl;
+ var generateTestsClass = !recordDecl.IsAnonymousStructOrUnion && recordDecl.DeclContext is not RecordDecl;
var testOutputStarted = false;
var nullableUuid = (Guid?)null;
@@ -1510,7 +1509,7 @@ private void VisitRecordDecl(RecordDecl recordDecl)
baseTypeNames = [.. baseTypeNamesBuilder];
}
- if (!TryGetRemappedValue(recordDecl, _config.WithPackings, out var pack))
+ if (!TryGetRemappedValue(recordDecl, _config._withPackings, out var pack))
{
pack = alignment < maxAlignm ? alignment.ToString(CultureInfo.InvariantCulture) : null;
}
@@ -1558,7 +1557,7 @@ private void VisitRecordDecl(RecordDecl recordDecl)
if (!_topLevelClassUsings.TryGetValue(name, out var withUsings))
{
- withUsings = [];
+ withUsings = new HashSet(StringComparer.Ordinal);
}
if (desc.LayoutAttribute is not null)
@@ -1657,9 +1656,23 @@ private void VisitRecordDecl(RecordDecl recordDecl)
_outputBuilder.BeginValue(in valueDesc);
var code = _outputBuilder.BeginCSharpCode();
- code.Write("(Guid*)Unsafe.AsPointer(ref Unsafe.AsRef(in ");
+
+ code.Write("(Guid*)Unsafe.AsPointer(");
+
+ if (!_config.GenerateLatestCode)
+ {
+ code.Write("ref Unsafe.AsRef(");
+ }
+
+ code.Write("in ");
code.Write(usableUuidName);
- code.Write("))");
+ code.Write(')');
+
+ if (!_config.GenerateLatestCode)
+ {
+ code.Write(')');
+ }
+
_outputBuilder.EndCSharpCode(code);
_outputBuilder.EndValue(in valueDesc);
@@ -2213,6 +2226,7 @@ void OutputVtblHelperMethod(CXXRecordDecl cxxRecordDecl, CXXMethodDecl cxxMethod
HasFnPtrCodeGen = !_config.ExcludeFnptrCodegen,
IsCtxCxxRecord = true,
IsCxxRecordCtxUnsafe = IsUnsafe(cxxRecordDecl),
+ IsReadOnly = IsReadonly(cxxMethodDecl),
IsUnsafe = true,
NeedsReturnFixup = needsReturnFixup,
ReturnType = returnTypeName,
@@ -2335,7 +2349,27 @@ void OutputVtblHelperMethod(CXXRecordDecl cxxRecordDecl, CXXMethodDecl cxxMethod
{
body.Write('(');
body.Write(escapedCXXRecordDeclName);
- body.Write("*)Unsafe.AsPointer(ref this)");
+ body.Write("*)Unsafe.AsPointer(");
+
+ if (IsReadonly(cxxMethodDecl))
+ {
+ if (!_config.GenerateLatestCode)
+ {
+ body.Write("ref Unsafe.AsRef(");
+ }
+
+ body.Write("in this");
+
+ if (!_config.GenerateLatestCode)
+ {
+ body.Write(')');
+ }
+ }
+ else
+ {
+ body.Write("ref this");
+ }
+ body.Write(')');
}
body.EndMarker("param");
@@ -2714,204 +2748,282 @@ void VisitBitfieldDecl(FieldDecl fieldDecl, BitfieldDesc[] bitfieldDescs, Record
_outputBuilder.BeginField(in desc);
_outputBuilder.WriteRegularField(typeName, escapedName);
_outputBuilder.BeginBody();
- _outputBuilder.BeginGetter(_config.GenerateAggressiveInlining, isReadOnly: !Config.GenerateCompatibleCode);
- var code = _outputBuilder.BeginCSharpCode();
-
- code.WriteIndented("return ");
var recordDeclName = GetCursorName(recordDecl);
- var isSmallType = currentSize < 4;
- var isRemappedToSelf = _config.RemappedNames.TryGetValue(typeName, out var remappedTypeName) && typeName.Equals(remappedTypeName, StringComparison.Ordinal);
- var isTypeMismatch = type != builtinTypeBacking;
- var isUnsignedToSigned = !isTypeBackingSigned && isTypeSigned;
+ // Small types become uint32/int32s after shifting
+ var isSmallType = fieldDecl.Type.Handle.SizeOf < 4;
+ var isSmallTypeBacking = currentSize < 4;
- var needsCast = isSmallType || isRemappedToSelf || isTypeMismatch || isUnsignedToSigned;
- var needsParenFirst = !isSmallType && isUnsignedToSigned;
- var needsParenSecond = !needsParenFirst || isRemappedToSelf;
+ // Check if field/backing types match
+ var isTypeMismatch = type != builtinTypeBacking;
+ // Signed types are sign extended when shifted
+ var isUnsignedToSigned = !isTypeBackingSigned && isTypeSigned;
+
+ // Check if type is directly shiftable/maskable
+ // Remapped types are not guaranteed to be shiftable or maskable
+ // Enums are maskable, but not shiftable
+ var isTypeLikelyRemapped = !isTypeMismatch && (typeName != typeNameBacking);
+ var isTypeAnEnum = IsType(fieldDecl);
+
+ // Main cases:
// backing int, current int (value << cns) >> cns
// backing int, current uint (uint)((value >> cns) & msk)
- // backing uint, current int ((int)value << cns) >> cns
+ // backing uint, current int (int)(value << cns) >> cns
// backing uint, current uint (value >> cns) & msk
// backing uint, current byte (byte)((value >> cns) & msk)
- // backing uint, current sbyte (sbyte)((value << cns) >> cns)
+ // backing uint, current sbyte (sbyte)((sbyte)(value << cns) >> cns)
- if (needsCast)
+ // Getter
{
- code.Write('(');
- code.BeginMarker("typeName");
- code.Write(typeName);
- code.EndMarker("typeName");
- code.Write(")(");
- }
+ _outputBuilder.BeginGetter(_config.GenerateAggressiveInlining,
+ isReadOnly: !Config.GenerateCompatibleCode);
+ var code = _outputBuilder.BeginCSharpCode();
- if ((!needsParenFirst && (bitfieldOffset != 0)) || (!isUnsignedToSigned && isTypeSigned))
- {
- code.Write('(');
- }
+ code.WriteIndented("return ");
- if (!string.IsNullOrWhiteSpace(contextName))
- {
- code.BeginMarker("contextName");
- code.Write(contextName);
- code.EndMarker("contextName");
- code.Write('.');
- }
+ var needsCastToFinal = (isSmallType || isTypeMismatch || isUnsignedToSigned) && !isTypeAnEnum;
- code.BeginMarker("bitfieldName");
- code.Write(bitfieldName);
- code.EndMarker("bitfieldName");
+ // This is to handle the "backing uint, current sbyte" case
+ var needsCastToFinalAgain = isUnsignedToSigned && isSmallType;
+ if (needsCastToFinalAgain)
+ {
+ code.Write('(');
+ code.BeginMarker("typeName");
+ code.Write(typeName);
+ code.EndMarker("typeName");
+ code.Write(")(");
+ }
- if (isTypeSigned)
- {
- code.Write(" << ");
- code.BeginMarker("remainingBitsMinusBitWidth");
- code.Write(remainingBits - fieldDecl.BitWidthValue);
- code.EndMarker("remainingBitsMinusBitWidth");
- code.Write(')');
+ if (needsCastToFinal)
+ {
+ code.Write('(');
+ code.BeginMarker("typeName");
+ code.Write(typeName);
+ code.EndMarker("typeName");
+ code.Write(")");
+ }
- code.Write(" >> ");
- code.BeginMarker("currentSizeMinusBitWidth");
- code.Write((currentSize * 8) - fieldDecl.BitWidthValue);
- code.EndMarker("currentSizeMinusBitWidth");
- }
- else
- {
- if (bitfieldOffset != 0)
+ var needsCastToFinalParen = needsCastToFinal && !isUnsignedToSigned;
+ if (needsCastToFinalParen)
{
- code.Write(" >> ");
- code.BeginMarker("bitfieldOffset");
- code.Write(bitfieldOffset);
- code.EndMarker("bitfieldOffset");
+ code.Write('(');
+ }
+
+ var needsCastBeforeOp = isTypeLikelyRemapped || isTypeAnEnum;
+ if (needsCastBeforeOp)
+ {
+ code.Write('(');
+ code.BeginMarker("typeName");
+ code.Write(typeName);
+ code.EndMarker("typeName");
+ code.Write(")(");
+ }
+
+ if (isTypeSigned)
+ {
+ code.Write('(');
+
+ if (!string.IsNullOrWhiteSpace(contextName))
+ {
+ code.BeginMarker("contextName");
+ code.Write(contextName);
+ code.EndMarker("contextName");
+ code.Write('.');
+ }
+
+ code.BeginMarker("bitfieldName");
+ code.Write(bitfieldName);
+ code.EndMarker("bitfieldName");
+
+ code.Write(" << ");
+ code.BeginMarker("remainingBitsMinusBitWidth");
+ code.Write(remainingBits - fieldDecl.BitWidthValue);
+ code.EndMarker("remainingBitsMinusBitWidth");
+
code.Write(')');
+
+ code.Write(" >> ");
+ code.BeginMarker("currentSizeMinusBitWidth");
+ code.Write((currentSize * 8) - fieldDecl.BitWidthValue);
+ code.EndMarker("currentSizeMinusBitWidth");
}
+ else
+ {
+ var needsOffset = bitfieldOffset != 0;
+ if (needsOffset)
+ {
+ code.Write('(');
+ }
- code.Write(" & 0x");
- code.BeginMarker("bitwidthHexStringBacking");
- code.Write(bitwidthHexStringBacking);
- code.EndMarker("bitwidthHexStringBacking");
- }
+ if (!string.IsNullOrWhiteSpace(contextName))
+ {
+ code.BeginMarker("contextName");
+ code.Write(contextName);
+ code.EndMarker("contextName");
+ code.Write('.');
+ }
- if (needsCast && needsParenSecond)
- {
- code.Write(')');
- }
+ code.BeginMarker("bitfieldName");
+ code.Write(bitfieldName);
+ code.EndMarker("bitfieldName");
- code.WriteSemicolon();
- code.WriteNewline();
- _outputBuilder.EndCSharpCode(code);
- _outputBuilder.EndGetter();
+ if (needsOffset)
+ {
+ code.Write(" >> ");
+ code.BeginMarker("bitfieldOffset");
+ code.Write(bitfieldOffset);
+ code.EndMarker("bitfieldOffset");
- _outputBuilder.BeginSetter(_config.GenerateAggressiveInlining);
- code = _outputBuilder.BeginCSharpCode();
- code.WriteIndentation();
+ code.Write(')');
+ }
- if (!string.IsNullOrWhiteSpace(contextName))
- {
- code.BeginMarker("contextName");
- code.Write(contextName);
- code.EndMarker("contextName");
- code.Write('.');
- }
+ code.Write(" & 0x");
+ code.BeginMarker("bitwidthHexStringBacking");
+ code.Write(bitwidthHexStringBacking);
+ code.EndMarker("bitwidthHexStringBacking");
+ }
- code.BeginMarker("bitfieldName");
- code.Write(bitfieldName);
- code.EndMarker("bitfieldName");
+ if (needsCastBeforeOp)
+ {
+ code.Write(')');
+ }
- code.Write(" = ");
+ if (needsCastToFinalParen)
+ {
+ code.Write(')');
+ }
- if (currentSize < 4)
- {
- code.Write('(');
- code.BeginMarker("typeNameBacking");
- code.Write(typeNameBacking);
- code.EndMarker("typeNameBacking");
- code.Write(")(");
- }
+ if (needsCastToFinalAgain)
+ {
+ code.Write(')');
+ }
- code.Write('(');
+ code.WriteSemicolon();
+ code.WriteNewline();
+ _outputBuilder.EndCSharpCode(code);
+ _outputBuilder.EndGetter();
+ }
- if (!string.IsNullOrWhiteSpace(contextName))
+ // Setter
{
- code.Write(contextName);
- code.Write('.');
- }
+ _outputBuilder.BeginSetter(_config.GenerateAggressiveInlining);
+ var code = _outputBuilder.BeginCSharpCode();
+ code.WriteIndentation();
+
+ if (!string.IsNullOrWhiteSpace(contextName))
+ {
+ code.BeginMarker("contextName");
+ code.Write(contextName);
+ code.EndMarker("contextName");
+ code.Write('.');
+ }
- code.Write(bitfieldName);
+ code.BeginMarker("bitfieldName");
+ code.Write(bitfieldName);
+ code.EndMarker("bitfieldName");
- code.Write(" & ~");
+ code.Write(" = ");
- if (bitfieldOffset != 0)
- {
+ var needsCastToFinal = isSmallTypeBacking;
+ if (needsCastToFinal)
+ {
+ code.Write('(');
+ code.BeginMarker("typeNameBacking");
+ code.Write(typeNameBacking);
+ code.EndMarker("typeNameBacking");
+ code.Write(")(");
+ }
+
+ // Zero out target bits
code.Write('(');
- }
- code.Write("0x");
- code.BeginMarker("bitwidthHexStringBacking");
- code.Write(bitwidthHexStringBacking);
- code.EndMarker("bitwidthHexStringBacking");
+ if (!string.IsNullOrWhiteSpace(contextName))
+ {
+ code.Write(contextName);
+ code.Write('.');
+ }
- if (bitfieldOffset != 0)
- {
- code.Write(" << ");
- code.BeginMarker("bitfieldOffset");
- code.Write(bitfieldOffset);
- code.EndMarker("bitfieldOffset");
- code.Write(')');
- }
+ code.Write(bitfieldName);
- code.Write(") | ");
+ code.Write(" & ~");
- if ((builtinType != builtinTypeBacking) && !IsType(fieldDecl))
- {
- code.Write('(');
- code.Write(typeNameBacking);
- code.Write(')');
- }
+ if (bitfieldOffset != 0)
+ {
+ code.Write('(');
+ }
+
+ code.Write("0x");
+ code.BeginMarker("bitwidthHexStringBacking");
+ code.Write(bitwidthHexStringBacking);
+ code.EndMarker("bitwidthHexStringBacking");
- code.Write('(');
+ if (bitfieldOffset != 0)
+ {
+ code.Write(" << ");
+ code.BeginMarker("bitfieldOffset");
+ code.Write(bitfieldOffset);
+ code.EndMarker("bitfieldOffset");
+ code.Write(')');
+ }
- if (bitfieldOffset != 0)
- {
- code.Write('(');
- }
+ // Write to target bits
+ code.Write(") | ");
+
+ var needsCastBeforeLogicalOr = isTypeMismatch && !isTypeAnEnum;
+ if (needsCastBeforeLogicalOr)
+ {
+ code.Write('(');
+ code.Write(typeNameBacking);
+ code.Write(")");
+ }
- if (IsType(fieldDecl) || isRemappedToSelf)
- {
code.Write('(');
- code.Write(typeNameBacking);
- code.Write(")(value)");
- }
- else
- {
- code.Write("value");
- }
- code.Write(" & 0x");
- code.BeginMarker("bitwidthHexString");
- code.Write(bitwidthHexString);
- code.EndMarker("bitwidthHexString");
+ if (bitfieldOffset != 0)
+ {
+ code.Write('(');
+ }
- if (bitfieldOffset != 0)
- {
- code.Write(") << ");
- code.Write(bitfieldOffset);
- }
+ var needsCastBeforeOp = isTypeLikelyRemapped || isTypeAnEnum;
+ if (needsCastBeforeOp)
+ {
+ code.Write('(');
+ code.Write(typeNameBacking);
+ code.Write(")(value)");
+ }
+ else
+ {
+ code.Write("value");
+ }
+
+ code.Write(" & 0x");
+ code.BeginMarker("bitwidthHexString");
+ code.Write(bitwidthHexString);
+ code.EndMarker("bitwidthHexString");
- code.Write(')');
+ if (bitfieldOffset != 0)
+ {
+ code.Write(") << ");
+ code.Write(bitfieldOffset);
+ }
- if (currentSize < 4)
- {
code.Write(')');
+
+ if (needsCastToFinal)
+ {
+ code.Write(')');
+ }
+
+ code.WriteSemicolon();
+ code.WriteNewline();
+ _outputBuilder.EndCSharpCode(code);
+ _outputBuilder.EndSetter();
}
- code.WriteSemicolon();
- code.WriteNewline();
- _outputBuilder.EndCSharpCode(code);
- _outputBuilder.EndSetter();
_outputBuilder.EndBody();
_outputBuilder.EndField(in desc);
_outputBuilder.WriteDivider();
@@ -2974,7 +3086,7 @@ void VisitConstantOrIncompleteArrayFieldDecl(RecordDecl recordDecl, FieldDecl co
AddDiagnostic(DiagnosticLevel.Info, $"{escapedName} (constant array field) has a size of 0", constantOrIncompleteArray);
}
- if (!_config.GenerateLatestCode || (totalSize <= 1) || isUnsafeElementType)
+ if (_config.GenerateCompatibleCode || (totalSize <= 1) || isUnsafeElementType)
{
totalSizeString = null;
}
@@ -3100,7 +3212,7 @@ void VisitConstantOrIncompleteArrayFieldDecl(RecordDecl recordDecl, FieldDecl co
}
else if (totalSizeString is null)
{
- _outputBuilder.BeginIndexer(AccessSpecifier.Public, isUnsafe: false, needsUnscopedRef: _config.GenerateLatestCode);
+ _outputBuilder.BeginIndexer(AccessSpecifier.Public, isUnsafe: false, needsUnscopedRef: !_config.GenerateCompatibleCode);
_outputBuilder.WriteIndexer($"ref {arrayTypeName}");
_outputBuilder.BeginIndexerParameters();
var param = new ParameterDesc {
@@ -3121,6 +3233,7 @@ void VisitConstantOrIncompleteArrayFieldDecl(RecordDecl recordDecl, FieldDecl co
if (arraySize == 1)
{
+ code.AddUsingDirective("System.Runtime.CompilerServices");
code.Write("Unsafe.Add(ref e0, index)");
}
else
@@ -3145,7 +3258,7 @@ void VisitConstantOrIncompleteArrayFieldDecl(RecordDecl recordDecl, FieldDecl co
ReturnType = $"Span<{arrayTypeName}>",
Location = constantOrIncompleteArray.Location,
HasBody = true,
- NeedsUnscopedRef = _config.GenerateLatestCode,
+ NeedsUnscopedRef = !_config.GenerateCompatibleCode,
};
var isUnsafe = false;
@@ -3310,7 +3423,7 @@ void ForUnderlyingType(TypedefDecl typedefDecl, Type underlyingType, bool onlyHa
{
if (!_allValidNameRemappings.TryGetValue(underlyingName, out var allRemappings))
{
- allRemappings = [];
+ allRemappings = new HashSet(QualifiedNameComparer.Default);
_allValidNameRemappings[underlyingName] = allRemappings;
}
_ = allRemappings.Add(typedefName);
@@ -3320,7 +3433,7 @@ void ForUnderlyingType(TypedefDecl typedefDecl, Type underlyingType, bool onlyHa
{
if (!_traversedValidNameRemappings.TryGetValue(underlyingName, out var traversedRemappings))
{
- traversedRemappings = [];
+ traversedRemappings = new HashSet(QualifiedNameComparer.Default);
_traversedValidNameRemappings[underlyingName] = traversedRemappings;
}
_ = traversedRemappings.Add(typedefName);
@@ -3462,7 +3575,7 @@ private void VisitVarDecl(VarDecl varDecl)
case CX_SLK_UTF32:
{
- typeName = (_config.GenerateLatestCode && flags.HasFlag(ValueFlags.Constant)) ? "ReadOnlySpan" : "uint[]";
+ typeName = (!_config.GenerateCompatibleCode && flags.HasFlag(ValueFlags.Constant)) ? "ReadOnlySpan" : "uint[]";
break;
}
diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs
index abd5cf64..5eaf6372 100644
--- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs
+++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs
@@ -41,7 +41,60 @@ private void VisitBinaryOperator(BinaryOperator binaryOperator)
outputBuilder.Write(' ');
outputBuilder.Write(binaryOperator.OpcodeStr);
outputBuilder.Write(' ');
- Visit(binaryOperator.RHS);
+
+ if (binaryOperator.IsShiftOp || binaryOperator.IsShiftAssignOp)
+ {
+ // RHS of shift operation in C# must be an int
+
+ // Negative shifts are undefined behavior in C/C++, but still needs to have a compilable output
+ var isNegated = binaryOperator.RHS is UnaryOperator { Opcode: CXUnaryOperator_Minus };
+ var sign = isNegated ? -1 : 1;
+
+ var rhs = isNegated ? ((UnaryOperator)binaryOperator.RHS).SubExpr : binaryOperator.RHS;
+ switch (rhs)
+ {
+ case IntegerLiteral literal:
+ {
+ var value = sign * literal.Value;
+ if (value is > int.MaxValue or < int.MinValue)
+ {
+ // Literal is in int32 range
+ outputBuilder.Write("(int)(");
+ outputBuilder.Write(sign * literal.Value);
+ outputBuilder.Write(")");
+ }
+ else
+ {
+ // Literal is not in int32 range
+ outputBuilder.Write(sign * literal.Value);
+ }
+
+ break;
+ }
+ // Already the correct type or implicitly castable
+ case { Type.Kind: CXType_Int or CXType_UShort or CXType_Short or CXType_Char_U or CXType_Char_S or CXType_UChar or CXType_SChar }:
+ case { Type.Kind: CXType_Long } when _config is { GenerateUnixTypes: false }:
+ case { Type.Kind: CXType_Char16 } when _config is { GenerateDisableRuntimeMarshalling: false }:
+ case { Type.Kind: CXType_WChar } when _config is { GenerateDisableRuntimeMarshalling: false, GenerateUnixTypes: false }:
+ {
+ Visit(binaryOperator.RHS);
+ break;
+ }
+ // Fallback
+ default:
+ {
+ outputBuilder.Write("(int)(");
+ Visit(binaryOperator.RHS);
+ outputBuilder.Write(")");
+ break;
+ }
+ }
+ }
+ else
+ {
+ Visit(binaryOperator.RHS);
+ }
+
StopCSharpCode();
}
@@ -194,7 +247,7 @@ private void VisitCallExpr(CallExpr callExpr)
{
var args = callExpr.Args;
- if (Config.GenerateLatestCode)
+ if (!Config.GenerateCompatibleCode)
{
outputBuilder.AddUsingDirective("System.Runtime.InteropServices");
outputBuilder.Write("NativeMemory.Copy");
@@ -253,13 +306,13 @@ private void VisitCallExpr(CallExpr callExpr)
break;
}
- if (Config.GenerateLatestCode)
+ if (!Config.GenerateCompatibleCode)
{
args = [args[0], args[2], args[1]];
}
}
- if (Config.GenerateLatestCode)
+ if (!Config.GenerateCompatibleCode)
{
outputBuilder.AddUsingDirective("System.Runtime.InteropServices");
outputBuilder.Write("NativeMemory.Fill");
@@ -356,7 +409,27 @@ void VisitArgs(CallExpr callExpr, IReadOnlyList? args = null)
outputBuilder.AddUsingDirective("System.Runtime.CompilerServices");
outputBuilder.Write('(');
outputBuilder.Write(referenceTypeName);
- outputBuilder.Write(")Unsafe.AsPointer(ref this)");
+ outputBuilder.Write(")Unsafe.AsPointer(");
+
+ if (referenceType.IsLocalConstQualified)
+ {
+ if (!_config.GenerateLatestCode)
+ {
+ outputBuilder.Write("ref Unsafe.AsRef(");
+ }
+
+ outputBuilder.Write("in this");
+
+ if (!_config.GenerateLatestCode)
+ {
+ outputBuilder.Write(')');
+ }
+ }
+ else
+ {
+ outputBuilder.Write("ref this");
+ }
+ outputBuilder.Write(')');
needsComma = true;
continue;
@@ -379,7 +452,27 @@ void VisitArgs(CallExpr callExpr, IReadOnlyList? args = null)
else if (IsStmtAsWritten(arg, out _) && (functionName == "memcpy"))
{
outputBuilder.AddUsingDirective("System.Runtime.CompilerServices");
- outputBuilder.Write("Unsafe.AsPointer(ref this)");
+ outputBuilder.Write("Unsafe.AsPointer(");
+
+ if (functionProtoType.ParamTypes[i].IsLocalConstQualified)
+ {
+ if (!_config.GenerateLatestCode)
+ {
+ outputBuilder.Write("ref Unsafe.AsRef(");
+ }
+
+ outputBuilder.Write("in this");
+
+ if (!_config.GenerateLatestCode)
+ {
+ outputBuilder.Write(')');
+ }
+ }
+ else
+ {
+ outputBuilder.Write("ref this");
+ }
+ outputBuilder.Write(')');
needsComma = true;
continue;
@@ -751,7 +844,7 @@ private void VisitCXXOperatorCallExpr(CXXOperatorCallExpr cxxOperatorCallExpr)
{
Visit(args[0]);
outputBuilder.Write(' ');
- outputBuilder.Write(functionDeclName[8..]);
+ outputBuilder.Write(functionDeclName.AsSpan()[8..]);
outputBuilder.Write(' ');
Visit(args[1]);
StopCSharpCode();
@@ -760,7 +853,7 @@ private void VisitCXXOperatorCallExpr(CXXOperatorCallExpr cxxOperatorCallExpr)
case "operator~":
{
- outputBuilder.Write(functionDeclName[8..]);
+ outputBuilder.Write(functionDeclName.AsSpan()[8..]);
Visit(args[0]);
StopCSharpCode();
return;
@@ -1108,7 +1201,7 @@ private void VisitFloatingLiteral(FloatingLiteral floatingLiteral)
var outputBuilder = StartCSharpCode();
if (floatingLiteral.ValueString.EndsWith(".f", StringComparison.Ordinal))
{
- outputBuilder.Write(floatingLiteral.ValueString[0..^1]);
+ outputBuilder.Write(floatingLiteral.ValueString.AsSpan()[..^1]);
outputBuilder.Write("0f");
}
else
@@ -1703,7 +1796,7 @@ void HandleUnmanagedConstant(CSharpOutputBuilder outputBuilder, InitListExpr ini
outputBuilder.WriteIndented("ReadOnlySpan data = ");
- if (_config.GenerateLatestCode)
+ if (!_config.GenerateCompatibleCode)
{
outputBuilder.WriteLine("[");
}
@@ -1719,7 +1812,7 @@ void HandleUnmanagedConstant(CSharpOutputBuilder outputBuilder, InitListExpr ini
outputBuilder.WriteNewline();
outputBuilder.DecreaseIndentation();
- if (_config.GenerateLatestCode)
+ if (!_config.GenerateCompatibleCode)
{
outputBuilder.WriteIndented(']');
}
@@ -1901,76 +1994,83 @@ void HandleUnmanagedConstant(CSharpOutputBuilder outputBuilder, InitListExpr ini
private void VisitIntegerLiteral(IntegerLiteral integerLiteral)
{
- var valueString = integerLiteral.ValueString;
+ var valueString = integerLiteral.ValueString.AsSpan();
+ var valueSuffix = "";
if (valueString.EndsWith("ui8", StringComparison.OrdinalIgnoreCase))
{
- valueString = valueString[0..^3];
+ valueString = valueString[..^3];
}
else if (valueString.EndsWith("i8", StringComparison.OrdinalIgnoreCase))
{
- valueString = valueString[0..^2];
+ valueString = valueString[..^2];
}
else if (valueString.EndsWith("ui16", StringComparison.OrdinalIgnoreCase))
{
- valueString = valueString[0..^4];
+ valueString = valueString[..^4];
}
else if (valueString.EndsWith("i16", StringComparison.OrdinalIgnoreCase))
{
- valueString = valueString[0..^3];
+ valueString = valueString[..^3];
}
else if (valueString.EndsWith("ui32", StringComparison.OrdinalIgnoreCase))
{
- valueString = valueString[0..^4] + "U";
+ valueString = valueString[..^4];
+ valueSuffix = "U";
}
else if (valueString.EndsWith("i32", StringComparison.OrdinalIgnoreCase))
{
- valueString = valueString[0..^3];
+ valueString = valueString[..^3];
}
else if (valueString.EndsWith("ui64", StringComparison.OrdinalIgnoreCase))
{
- valueString = valueString[0..^4] + "UL";
+ valueString = valueString[..^4];
+ valueSuffix = "UL";
}
else if (valueString.EndsWith("i64", StringComparison.OrdinalIgnoreCase))
{
- valueString = valueString[0..^3] + "L";
+ valueString = valueString[..^3];
+ valueSuffix = "L";
}
else if (
valueString.EndsWith("ull", StringComparison.OrdinalIgnoreCase) ||
valueString.EndsWith("llu", StringComparison.OrdinalIgnoreCase))
{
- valueString = valueString[0..^3] + "UL";
+ valueString = valueString[..^3];
+ valueSuffix = "UL";
}
else if (valueString.EndsWith("ll", StringComparison.OrdinalIgnoreCase))
{
- valueString = valueString[0..^2] + "L";
+ valueString = valueString[..^2];
+ valueSuffix = "L";
}
else if (
valueString.EndsWith("ul", StringComparison.OrdinalIgnoreCase) ||
valueString.EndsWith("lu", StringComparison.OrdinalIgnoreCase))
{
- valueString = valueString[0..^2] + "U";
+ valueString = valueString[..^2];
+ valueSuffix = "U";
}
else if (valueString.EndsWith('u') || valueString.EndsWith('U'))
{
- valueString = valueString[0..^1] + "U";
+ valueString = valueString[..^1];
+ valueSuffix = "U";
}
else if (valueString.EndsWith('l') || valueString.EndsWith('L'))
{
- valueString = valueString[0..^1];
+ valueString = valueString[..^1];
}
var outputBuilder = StartCSharpCode();
outputBuilder.Write(valueString);
+ outputBuilder.Write(valueSuffix);
StopCSharpCode();
}
private void VisitLabelStmt(LabelStmt labelStmt)
{
var outputBuilder = StartCSharpCode();
-
- outputBuilder.Write(labelStmt.Decl.Name);
- outputBuilder.WriteLine(':');
+ outputBuilder.WriteLabel(labelStmt.Decl.Name);
outputBuilder.WriteIndentation();
Visit(labelStmt.SubStmt);
@@ -2128,7 +2228,27 @@ private void VisitReturnStmt(ReturnStmt returnStmt)
outputBuilder.AddUsingDirective("System.Runtime.CompilerServices");
outputBuilder.Write('(');
outputBuilder.Write(referenceTypeName);
- outputBuilder.Write(")Unsafe.AsPointer(ref this)");
+ outputBuilder.Write(")Unsafe.AsPointer(");
+
+ if (referenceType.IsLocalConstQualified)
+ {
+ if (!_config.GenerateLatestCode)
+ {
+ outputBuilder.Write("ref Unsafe.AsRef(");
+ }
+
+ outputBuilder.Write("in this");
+
+ if (!_config.GenerateLatestCode)
+ {
+ outputBuilder.Write(')');
+ }
+ }
+ else
+ {
+ outputBuilder.Write("ref this");
+ }
+ outputBuilder.Write(')');
StopCSharpCode();
return;
@@ -2731,7 +2851,7 @@ private void VisitStringLiteral(StringLiteral stringLiteral)
case CX_SLK_Ordinary:
case CX_SLK_UTF8:
{
- if (Config.GenerateLatestCode)
+ if (!Config.GenerateCompatibleCode)
{
outputBuilder.Write('"');
outputBuilder.Write(EscapeString(stringLiteral.String));
@@ -2789,7 +2909,7 @@ private void VisitStringLiteral(StringLiteral stringLiteral)
case CX_SLK_UTF32:
{
- if (_config.GenerateLatestCode)
+ if (!_config.GenerateCompatibleCode)
{
outputBuilder.Write('[');
}
@@ -2810,7 +2930,7 @@ private void VisitStringLiteral(StringLiteral stringLiteral)
outputBuilder.Write("0x00000000");
- if (_config.GenerateLatestCode)
+ if (!_config.GenerateCompatibleCode)
{
outputBuilder.Write(']');
}
@@ -2944,12 +3064,12 @@ private void VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr unaryExprOrT
{
if ((parentType.Handle.SizeOf == 8) && IsPrevContextDecl(out var varDecl, out _))
{
- var cursorName = GetCursorName(varDecl);
+ var cursorName = GetCursorName(varDecl).AsSpan();
if (cursorName.StartsWith("ClangSharpMacro_", StringComparison.Ordinal))
{
cursorName = cursorName["ClangSharpMacro_".Length..];
- parentTypeIsVariableSized |= _config.WithTypes.TryGetValue(cursorName, out var remappedTypeName) && (remappedTypeName.Equals("int", StringComparison.Ordinal) || remappedTypeName.Equals("uint", StringComparison.Ordinal));
+ parentTypeIsVariableSized |= _config._withTypes.GetAlternateLookup>().TryGetValue(cursorName, out var remappedTypeName) && (remappedTypeName.Equals("int", StringComparison.Ordinal) || remappedTypeName.Equals("uint", StringComparison.Ordinal));
}
}
diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs
index ed878237..01eb5810 100644
--- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs
+++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs
@@ -7,6 +7,7 @@
using System.Globalization;
using System.IO;
using System.Linq;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
@@ -15,17 +16,17 @@
using ClangSharp.Interop;
using ClangSharp.XML;
using static ClangSharp.Interop.CX_AttrKind;
-using static ClangSharp.Interop.CXBinaryOperatorKind;
using static ClangSharp.Interop.CX_CXXAccessSpecifier;
using static ClangSharp.Interop.CX_StmtClass;
using static ClangSharp.Interop.CX_UnaryExprOrTypeTrait;
-using static ClangSharp.Interop.CXUnaryOperatorKind;
+using static ClangSharp.Interop.CXBinaryOperatorKind;
using static ClangSharp.Interop.CXCallingConv;
using static ClangSharp.Interop.CXDiagnosticSeverity;
using static ClangSharp.Interop.CXEvalResultKind;
using static ClangSharp.Interop.CXTemplateArgumentKind;
using static ClangSharp.Interop.CXTranslationUnit_Flags;
using static ClangSharp.Interop.CXTypeKind;
+using static ClangSharp.Interop.CXUnaryOperatorKind;
namespace ClangSharp;
@@ -61,6 +62,7 @@ public sealed partial class PInvokeGenerator : IDisposable
private readonly Dictionary _topLevelClassIsUnsafe;
private readonly Dictionary> _topLevelClassUsings;
private readonly Dictionary> _topLevelClassAttributes;
+ private readonly Dictionary _fileContents;
private readonly HashSet _topLevelClassNames;
private readonly HashSet _usedRemappings;
private readonly string _placeholderMacroType;
@@ -136,30 +138,31 @@ public PInvokeGenerator(PInvokeGeneratorConfiguration config, Func(StringComparer.Ordinal);
_diagnostics = [];
_context = new LinkedList<(Cursor, object?)>();
- _uuidsToGenerate = [];
- _generatedUuids = [];
+ _uuidsToGenerate = new Dictionary(StringComparer.Ordinal);
+ _generatedUuids = new HashSet(StringComparer.Ordinal);
_cursorNames = [];
_cursorQualifiedNames = [];
_typeNames = [];
- _allValidNameRemappings = new Dictionary>() {
+ _allValidNameRemappings = new Dictionary>(QualifiedNameComparer.Default) {
["intptr_t"] = ["IntPtr", "nint"],
["ptrdiff_t"] = ["IntPtr", "nint"],
["size_t"] = ["UIntPtr", "nuint"],
["uintptr_t"] = ["UIntPtr", "nuint"],
["_GUID"] = ["Guid"],
};
- _traversedValidNameRemappings = [];
+ _traversedValidNameRemappings = new Dictionary>(StringComparer.Ordinal);
_overloadIndices = [];
_isExcluded = [];
- _topLevelClassHasGuidMember = [];
- _topLevelClassIsUnsafe = [];
- _topLevelClassNames = [];
- _topLevelClassAttributes = [];
- _topLevelClassUsings = [];
- _usedRemappings = [];
+ _topLevelClassHasGuidMember = new Dictionary(StringComparer.Ordinal);
+ _topLevelClassIsUnsafe = new Dictionary(StringComparer.Ordinal);
+ _topLevelClassNames = new HashSet(StringComparer.Ordinal);
+ _topLevelClassAttributes = new Dictionary>(StringComparer.Ordinal);
+ _fileContents = [];
+ _topLevelClassUsings = new Dictionary>(StringComparer.Ordinal);
+ _usedRemappings = new HashSet(StringComparer.Ordinal);
_filePath = "";
_clangCommandLineArgs = [];
_placeholderMacroType = GetPlaceholderMacroType();
@@ -212,8 +215,8 @@ public void Close()
Stream? stream = null;
Stream? testStream = null;
- var methodClassOutputBuilders = new Dictionary();
- var methodClassTestOutputBuilders = new Dictionary();
+ var methodClassOutputBuilders = new Dictionary(StringComparer.Ordinal);
+ var methodClassTestOutputBuilders = new Dictionary(StringComparer.Ordinal);
var emitNamespaceDeclaration = true;
var leaveStreamOpen = false;
@@ -1409,7 +1412,7 @@ static void GenerateTransparentStructs(PInvokeGenerator generator, Stream? strea
sw.Write(indentString);
sw.WriteLine(" {");
sw.Write(indentString);
- sw.Write(" if (obj is ");
+ sw.Write(" if (obj is ");
sw.Write(name);
sw.WriteLine(" other)");
sw.Write(indentString);
@@ -1697,14 +1700,8 @@ public void GenerateBindings(TranslationUnit translationUnit, string filePath, s
{
if (_config.GenerateMacroBindings)
{
- var translationUnitHandle = translationUnit.Handle;
-
- var file = translationUnitHandle.GetFile(_filePath);
- var fileContents = translationUnitHandle.GetFileContents(file, out var size);
var fileContentsBuilder = _fileContentsBuilder;
- _ = fileContentsBuilder.Append(Encoding.UTF8.GetString(fileContents));
-
foreach (var cursor in translationUnit.TranslationUnitDecl.CursorChildren)
{
if (cursor is PreprocessedEntity preprocessedEntity)
@@ -1716,7 +1713,10 @@ public void GenerateBindings(TranslationUnit translationUnit, string filePath, s
var unsavedFileContents = fileContentsBuilder.ToString();
_ = fileContentsBuilder.Clear();
- using var unsavedFile = CXUnsavedFile.Create(_filePath, unsavedFileContents);
+ var translationUnitHandle = translationUnit.Handle;
+ var file = translationUnitHandle.GetFile(_filePath);
+
+ using var unsavedFile = CXUnsavedFile.Create(_filePath, translationUnitHandle, file, unsavedFileContents);
var unsavedFiles = new CXUnsavedFile[] { unsavedFile };
translationFlags = _translationFlags & ~CXTranslationUnit_DetailedPreprocessingRecord;
@@ -1734,53 +1734,61 @@ public void GenerateBindings(TranslationUnit translationUnit, string filePath, s
{
foreach (var kvp in _traversedValidNameRemappings)
{
- var name = kvp.Key;
+ var name = kvp.Key.AsSpan();
var remappings = kvp.Value;
- if (name.Contains('<', StringComparison.OrdinalIgnoreCase))
+ if (name.Contains('<'))
{
var parts = name.Split('<');
- if (parts.Length >= 2)
+ if (parts.MoveNext())
{
- name = parts[0];
+ var part = parts.Current;
+
+ if (parts.MoveNext())
+ {
+ name = name[part];
+ }
}
}
- if (!_config.RemappedNames.TryGetValue(name, out _))
+ var remappedNamesLookup = _config._remappedNames.GetAlternateLookup>();
+
+ if (!remappedNamesLookup.TryGetValue(name, out _))
{
- var addDiag = false;
+ var addDiag = true;
- var altName = name;
var smlName = name;
+ var lastSeparatorIndex = smlName.LastIndexOf("::", StringComparison.Ordinal);
- if (name.Contains("::", StringComparison.Ordinal))
+ if (lastSeparatorIndex != -1)
{
- altName = name.Replace("::", ".", StringComparison.Ordinal);
- smlName = altName.Split('.')[^1];
+ smlName = smlName[(lastSeparatorIndex + 2)..];
+ addDiag = false;
}
- else if (name.Contains('.', StringComparison.Ordinal))
+
+ lastSeparatorIndex = smlName.LastIndexOf('.');
+
+ if (lastSeparatorIndex != -1)
{
- altName = name.Replace(".", "::", StringComparison.Ordinal);
- smlName = altName.Split("::")[^1];
+ smlName = smlName[(lastSeparatorIndex + 1)..];
+ addDiag = false;
}
- else
+
+ if (!addDiag && !remappedNamesLookup.TryGetValue(smlName, out _))
{
addDiag = true;
}
- if (!addDiag && !_config.RemappedNames.TryGetValue(altName, out _))
+ if (addDiag)
{
- if (!_config.RemappedNames.TryGetValue(smlName, out _))
+ var remappingsLookup = remappings.GetAlternateLookup>();
+
+ if (!remappingsLookup.Contains(name) && !remappingsLookup.Contains(smlName))
{
- addDiag = true;
+ AddDiagnostic(DiagnosticLevel.Info, $"Potential missing remapping '{name}'. {GetFoundRemappingString(name, remappings)}");
}
}
-
- if (addDiag && !remappings.Contains(altName) && !remappings.Contains(smlName))
- {
- AddDiagnostic(DiagnosticLevel.Info, $"Potential missing remapping '{name}'. {GetFoundRemappingString(name, remappings)}");
- }
}
}
@@ -1789,34 +1797,32 @@ public void GenerateBindings(TranslationUnit translationUnit, string filePath, s
var name = kvp.Key;
var remappings = kvp.Value;
- if (_config.RemappedNames.TryGetValue(name, out var remappedName) && !remappings.Contains(remappedName) && (name != remappedName) && !_config.ForceRemappedNames.Contains(name))
+ var remappedNamesLookup = _config._remappedNames.GetAlternateLookup>();
+
+ if (remappedNamesLookup.TryGetValue(name, out var remappedName) && !remappings.Contains(remappedName) && (name != remappedName) && !_config.ForceRemappedNames.Contains(name))
{
- var addDiag = false;
+ var addDiag = true;
- var altName = name;
var smlName = name;
+ var lastSeparatorIndex = smlName.LastIndexOf("::", StringComparison.Ordinal);
- if (name.Contains("::", StringComparison.Ordinal))
+ if (lastSeparatorIndex != -1)
{
- altName = name.Replace("::", ".", StringComparison.Ordinal);
- smlName = altName.Split('.')[^1];
+ smlName = smlName[(lastSeparatorIndex + 2)..];
+ addDiag = false;
}
- else if (name.Contains('.', StringComparison.Ordinal))
- {
- altName = name.Replace(".", "::", StringComparison.Ordinal);
- smlName = altName.Split("::")[^1];
- }
- else
+
+ lastSeparatorIndex = smlName.LastIndexOf('.');
+
+ if (lastSeparatorIndex != -1)
{
- addDiag = true;
+ smlName = smlName[(lastSeparatorIndex + 1)..];
+ addDiag = false;
}
- if (!addDiag && _config.RemappedNames.TryGetValue(altName, out remappedName) && !remappings.Contains(remappedName) && (altName != remappedName) && !_config.ForceRemappedNames.Contains(altName))
+ if (!addDiag && remappedNamesLookup.TryGetValue(smlName, out remappedName) && !remappings.Contains(remappedName) && (smlName != remappedName) && !_config.ForceRemappedNames.Contains(smlName))
{
- if (_config.RemappedNames.TryGetValue(smlName, out remappedName) && !remappings.Contains(remappedName) && (smlName != remappedName) && !_config.ForceRemappedNames.Contains(smlName))
- {
- addDiag = true;
- }
+ addDiag = true;
}
if (addDiag)
@@ -1830,34 +1836,32 @@ public void GenerateBindings(TranslationUnit translationUnit, string filePath, s
{
var remappedName = _config.RemappedNames[name];
- if (!_allValidNameRemappings.ContainsKey(name) && (name != remappedName) && !_config.ForceRemappedNames.Contains(name))
+ var allValidNameRemappingsLookup = _allValidNameRemappings.GetAlternateLookup>();
+
+ if (!allValidNameRemappingsLookup.ContainsKey(name) && (name != remappedName) && !_config.ForceRemappedNames.Contains(name))
{
- var addDiag = false;
+ var addDiag = true;
- var altName = name;
var smlName = name;
+ var lastSeparatorIndex = smlName.LastIndexOf("::", StringComparison.Ordinal);
- if (name.Contains("::", StringComparison.Ordinal))
- {
- altName = name.Replace("::", ".", StringComparison.Ordinal);
- smlName = altName.Split('.')[^1];
- }
- else if (name.Contains('.', StringComparison.Ordinal))
+ if (lastSeparatorIndex != -1)
{
- altName = name.Replace(".", "::", StringComparison.Ordinal);
- smlName = altName.Split("::")[^1];
+ smlName = smlName[(lastSeparatorIndex + 2)..];
+ addDiag = false;
}
- else
+
+ lastSeparatorIndex = smlName.LastIndexOf('.');
+
+ if (lastSeparatorIndex != -1)
{
- addDiag = true;
+ smlName = smlName[(lastSeparatorIndex + 1)..];
+ addDiag = false;
}
- if (!addDiag && !_allValidNameRemappings.ContainsKey(altName) && (altName != remappedName) && !_config.ForceRemappedNames.Contains(altName))
+ if (!addDiag && !allValidNameRemappingsLookup.ContainsKey(smlName) && (smlName != remappedName) && !_config.ForceRemappedNames.Contains(smlName))
{
- if (!_allValidNameRemappings.ContainsKey(smlName) && (smlName != remappedName) && !_config.ForceRemappedNames.Contains(smlName))
- {
- addDiag = true;
- }
+ addDiag = true;
}
if (addDiag)
@@ -1867,62 +1871,71 @@ public void GenerateBindings(TranslationUnit translationUnit, string filePath, s
}
}
- static string GetFoundRemappingString(string name, HashSet remappings)
+ static ReadOnlySpan GetFoundRemappingString(ReadOnlySpan name, HashSet remappings)
{
- var recommendedRemapping = "";
+ var recommendedRemappingString = "";
+ var recommendedRemapping = recommendedRemappingString.AsSpan();
if (remappings.Count == 1)
{
- recommendedRemapping = remappings.Single();
+ recommendedRemappingString = remappings.Single();
+ recommendedRemapping = recommendedRemappingString;
}
- if (string.IsNullOrEmpty(recommendedRemapping) && name.StartsWith('_'))
+ var remappingsLookup = remappings.GetAlternateLookup>();
+
+ if (recommendedRemapping.IsWhiteSpace() && name.StartsWith('_'))
{
var remapping = name[1..];
- if (remappings.Contains(remapping))
+ if (remappingsLookup.Contains(remapping))
{
recommendedRemapping = remapping;
+ recommendedRemappingString = null;
}
}
- if (string.IsNullOrEmpty(recommendedRemapping) && name.StartsWith("tag", StringComparison.Ordinal))
+ if (recommendedRemapping.IsWhiteSpace() && name.StartsWith("tag", StringComparison.Ordinal))
{
var remapping = name[3..];
- if (remappings.Contains(remapping))
+ if (remappingsLookup.Contains(remapping))
{
recommendedRemapping = remapping;
+ recommendedRemappingString = null;
}
}
- if (string.IsNullOrEmpty(recommendedRemapping) && name.EndsWith('_'))
+ if (recommendedRemapping.IsWhiteSpace() && name.EndsWith('_'))
{
- var remapping = name[0..^1];
+ var remapping = name[..^1];
- if (remappings.Contains(remapping))
+ if (remappingsLookup.Contains(remapping))
{
recommendedRemapping = remapping;
+ recommendedRemappingString = null;
}
}
- if (string.IsNullOrEmpty(recommendedRemapping) && name.EndsWith("tag", StringComparison.Ordinal))
+ if (recommendedRemapping.IsWhiteSpace() && name.EndsWith("tag", StringComparison.Ordinal))
{
- var remapping = name[0..^3];
+ var remapping = name[..^3];
- if (remappings.Contains(remapping))
+ if (remappingsLookup.Contains(remapping))
{
recommendedRemapping = remapping;
+ recommendedRemappingString = null;
}
}
- if (string.IsNullOrEmpty(recommendedRemapping))
+ if (recommendedRemapping.IsWhiteSpace())
{
- var remapping = name.ToUpperInvariant();
+ var remapping = name.ToString().ToUpperInvariant();
- if (remappings.Contains(remapping))
+ if (remappingsLookup.Contains(remapping))
{
- recommendedRemapping = remapping;
+ recommendedRemappingString = remapping;
+ recommendedRemapping = recommendedRemappingString;
}
}
@@ -1930,7 +1943,7 @@ static string GetFoundRemappingString(string name, HashSet remappings)
var remainingRemappings = (IEnumerable)remappings;
var remainingString = "Found";
- if (!string.IsNullOrEmpty(recommendedRemapping))
+ if (!recommendedRemapping.IsWhiteSpace())
{
result += $"Recommended remapping: '{name}={recommendedRemapping}'.";
@@ -1941,7 +1954,7 @@ static string GetFoundRemappingString(string name, HashSet remappings)
else
{
result += ' ';
- remainingRemappings = remappings.Except([recommendedRemapping]);
+ remainingRemappings = remappings.Except([recommendedRemappingString ?? recommendedRemapping.ToString()]);
remainingString = "Other";
}
}
@@ -2036,9 +2049,9 @@ private void CloseOutputBuilder(Stream stream, IOutputBuilder outputBuilder, boo
if (isMethodClass)
{
- var nonTestName = outputBuilder.IsTestOutput ? outputBuilder.Name[0..^5] : outputBuilder.Name;
+ var nonTestName = outputBuilder.IsTestOutput ? outputBuilder.Name.AsSpan()[..^5] : outputBuilder.Name;
- if (_topLevelClassUsings.TryGetValue(nonTestName, out var withUsings))
+ if (_topLevelClassUsings.GetAlternateLookup>().TryGetValue(nonTestName, out var withUsings))
{
foreach (var withUsing in withUsings)
{
@@ -2101,17 +2114,17 @@ private void CloseOutputBuilder(Stream stream, IOutputBuilder outputBuilder, boo
}
}
- void ForCSharp(CSharpOutputBuilder csharpOutputBuilder)
+ void ForCSharp(CSharpOutputBuilder outputBuilder)
{
- var indentationString = csharpOutputBuilder.IndentationString;
- var nonTestName = outputBuilder.IsTestOutput ? outputBuilder.Name[0..^5] : outputBuilder.Name;
+ var indentationString = outputBuilder.IndentationString;
+ var nonTestName = outputBuilder.IsTestOutput ? outputBuilder.Name.AsSpan()[..^5] : outputBuilder.Name;
if (emitNamespaceDeclaration)
{
sw.Write("namespace ");
sw.Write(GetNamespace(nonTestName));
- if (csharpOutputBuilder.IsTestOutput)
+ if (outputBuilder.IsTestOutput)
{
sw.Write(".UnitTests");
}
@@ -2135,7 +2148,7 @@ void ForCSharp(CSharpOutputBuilder csharpOutputBuilder)
if (isMethodClass)
{
- var isTopLevelStruct = _config.WithTypes.TryGetValue(nonTestName, out var withType) && withType.Equals("struct", StringComparison.Ordinal);
+ var isTopLevelStruct = _config._withTypes.GetAlternateLookup>().TryGetValue(nonTestName, out var withType) && withType.Equals("struct", StringComparison.Ordinal);
if (outputBuilder.IsTestOutput)
{
@@ -2156,7 +2169,7 @@ void ForCSharp(CSharpOutputBuilder csharpOutputBuilder)
sw.WriteLine(".");
}
- if (_topLevelClassAttributes.TryGetValue(nonTestName, out var withAttributes))
+ if (_topLevelClassAttributes.GetAlternateLookup>().TryGetValue(nonTestName, out var withAttributes))
{
if (withAttributes.Count != 0)
{
@@ -2184,7 +2197,7 @@ void ForCSharp(CSharpOutputBuilder csharpOutputBuilder)
sw.Write("static ");
}
- if ((_topLevelClassIsUnsafe.TryGetValue(nonTestName, out var isUnsafe) && isUnsafe) || (outputBuilder.IsTestOutput && isTopLevelStruct))
+ if ((_topLevelClassIsUnsafe.GetAlternateLookup>().TryGetValue(nonTestName, out var isUnsafe) && isUnsafe) || (outputBuilder.IsTestOutput && isTopLevelStruct))
{
sw.Write("unsafe ");
}
@@ -2211,15 +2224,15 @@ void ForCSharp(CSharpOutputBuilder csharpOutputBuilder)
sw.Write(indentationString);
sw.Write('{');
- if ((!outputBuilder.IsTestOutput && !isTopLevelStruct) || !string.IsNullOrEmpty(csharpOutputBuilder.Contents.First()))
+ if ((!outputBuilder.IsTestOutput && !isTopLevelStruct) || !string.IsNullOrEmpty(outputBuilder.Contents.First()))
{
sw.WriteLine();
}
- indentationString += csharpOutputBuilder.IndentationString;
+ indentationString += outputBuilder.IndentationString;
}
- foreach (var line in csharpOutputBuilder.Contents)
+ foreach (var line in outputBuilder.Contents)
{
if (string.IsNullOrWhiteSpace(line))
{
@@ -2234,7 +2247,7 @@ void ForCSharp(CSharpOutputBuilder csharpOutputBuilder)
if (isMethodClass)
{
- indentationString = indentationString[..^csharpOutputBuilder.IndentationString.Length];
+ indentationString = indentationString[..^outputBuilder.IndentationString.Length];
sw.Write(indentationString);
sw.WriteLine('}');
@@ -2246,7 +2259,7 @@ void ForCSharp(CSharpOutputBuilder csharpOutputBuilder)
}
}
- void ForXml(XmlOutputBuilder xmlOutputBuilder)
+ void ForXml(XmlOutputBuilder outputBuilder)
{
const string Indent = " ";
var indentationString = Indent;
@@ -2255,7 +2268,7 @@ void ForXml(XmlOutputBuilder xmlOutputBuilder)
{
sw.Write(indentationString);
sw.Write("");
}
@@ -2265,10 +2278,10 @@ void ForXml(XmlOutputBuilder xmlOutputBuilder)
{
sw.Write(indentationString);
sw.Write(" value.Replace(@"\0", "\0",
private AccessSpecifier GetAccessSpecifier(NamedDecl namedDecl, bool matchStar)
{
- if (!TryGetRemappedValue(namedDecl, _config.WithAccessSpecifiers, out var accessSpecifier, matchStar) || (accessSpecifier == AccessSpecifier.None))
+ if (!TryGetRemappedValue(namedDecl, _config._withAccessSpecifiers, out var accessSpecifier, matchStar) || (accessSpecifier == AccessSpecifier.None))
{
switch (namedDecl.Access)
{
@@ -2629,7 +2642,7 @@ private CallConv GetCallingConvention(Cursor? cursor, Cursor? context, Type type
if (cursor is NamedDecl namedDecl)
{
- if (TryGetRemappedValue(namedDecl, _config.WithCallConvs, out var callConv, matchStar: true))
+ if (TryGetRemappedValue(namedDecl, _config._withCallConvs, out var callConv, matchStar: true))
{
if (Enum.TryParse(callConv, true, out var remappedCallingConvention))
{
@@ -2773,22 +2786,26 @@ private CallConv GetCallingConvention(Cursor? cursor, Cursor? context, Type type
private string GetCursorName(NamedDecl namedDecl)
{
- if (!_cursorNames.TryGetValue(namedDecl, out var name))
+ if (!_cursorNames.TryGetValue(namedDecl, out var nameString))
{
- name = namedDecl.Name.NormalizePath();
+ nameString = namedDecl.Name.NormalizePath();
+ var name = nameString.AsSpan();
// strip the prefix
if (name.StartsWith("enum ", StringComparison.Ordinal))
{
name = name[5..];
+ nameString = null;
}
else if (name.StartsWith("struct ", StringComparison.Ordinal))
{
name = name[7..];
+ nameString = null;
}
else if (name.StartsWith("union ", StringComparison.Ordinal))
{
name = name[6..];
+ nameString = null;
}
var anonymousNameStartIndex = name.IndexOf("::(", StringComparison.Ordinal);
@@ -2797,21 +2814,26 @@ private string GetCursorName(NamedDecl namedDecl)
{
anonymousNameStartIndex += 2;
name = name[anonymousNameStartIndex..];
+ nameString = null;
}
if (namedDecl is CXXConstructorDecl cxxConstructorDecl)
{
var parent = cxxConstructorDecl.Parent;
Debug.Assert(parent is not null);
- name = GetCursorName(parent);
+
+ nameString = GetCursorName(parent);
+ name = nameString;
}
else if (namedDecl is CXXDestructorDecl cxxDestructorDecl)
{
var parent = cxxDestructorDecl.Parent;
Debug.Assert(parent is not null);
- name = $"~{GetCursorName(parent)}";
+
+ nameString = $"~{GetCursorName(parent)}";
+ name = nameString;
}
- else if (string.IsNullOrWhiteSpace(name) || name.StartsWith('('))
+ else if (name.IsWhiteSpace() || name.StartsWith('('))
{
#if DEBUG
if (name.StartsWith('('))
@@ -2829,17 +2851,20 @@ private string GetCursorName(NamedDecl namedDecl)
if (namedDecl is TypeDecl typeDecl)
{
- name = (typeDecl is TagDecl tagDecl) && tagDecl.Handle.IsAnonymous
- ? GetAnonymousName(tagDecl, tagDecl.TypeForDecl.KindSpelling)
- : GetTypeName(namedDecl, context: null, type: typeDecl.TypeForDecl, ignoreTransparentStructsWhereRequired: false, isTemplate: false, nativeTypeName: out _);
+ nameString = (typeDecl is TagDecl tagDecl) && tagDecl.Handle.IsAnonymous
+ ? GetAnonymousName(tagDecl, tagDecl.TypeForDecl.KindSpelling)
+ : GetTypeName(namedDecl, context: null, type: typeDecl.TypeForDecl, ignoreTransparentStructsWhereRequired: false, isTemplate: false, nativeTypeName: out _);
+ name = nameString;
}
else if (namedDecl is ParmVarDecl)
{
- name = "param";
+ nameString = "param";
+ name = nameString;
}
else if (namedDecl is FieldDecl fieldDecl)
{
- name = GetAnonymousName(fieldDecl, fieldDecl.CursorKindSpelling);
+ nameString = GetAnonymousName(fieldDecl, fieldDecl.CursorKindSpelling);
+ name = nameString;
}
else
{
@@ -2847,11 +2872,12 @@ private string GetCursorName(NamedDecl namedDecl)
}
}
- _cursorNames[namedDecl] = name;
+ nameString ??= name.ToString();
+ _cursorNames[namedDecl] = nameString;
}
- Debug.Assert(!string.IsNullOrWhiteSpace(name));
- return name;
+ Debug.Assert(!string.IsNullOrWhiteSpace(nameString));
+ return nameString;
}
private string GetCursorQualifiedName(NamedDecl namedDecl, bool truncateParameters = false)
@@ -3156,15 +3182,6 @@ private string GetRemappedCursorName(NamedDecl namedDecl, out string nativeTypeN
return remappedName;
}
- name = name.Replace("::", ".", StringComparison.Ordinal);
- remappedName = GetRemappedName(name, namedDecl, tryRemapOperatorName: true, out wasRemapped, skipUsing);
-
- if (wasRemapped)
- {
- return remappedName;
- }
-
-
name = GetCursorQualifiedName(namedDecl, truncateParameters: true);
remappedName = GetRemappedName(name, namedDecl, tryRemapOperatorName: true, out wasRemapped, skipUsing);
@@ -3173,14 +3190,6 @@ private string GetRemappedCursorName(NamedDecl namedDecl, out string nativeTypeN
return remappedName;
}
- name = name.Replace("::", ".", StringComparison.Ordinal);
- remappedName = GetRemappedName(name, namedDecl, tryRemapOperatorName: true, out wasRemapped, skipUsing);
-
- if (wasRemapped)
- {
- return remappedName;
- }
-
name = GetCursorName(namedDecl);
remappedName = GetRemappedName(name, namedDecl, tryRemapOperatorName: true, out wasRemapped, skipUsing);
@@ -3201,7 +3210,7 @@ private string GetRemappedCursorName(NamedDecl namedDecl, out string nativeTypeN
}
else if ((namedDecl is FieldDecl fieldDecl) && name.StartsWith("__AnonymousFieldDecl_", StringComparison.Ordinal))
{
- if (fieldDecl.Type.AsCXXRecordDecl?.IsAnonymous == true)
+ if (fieldDecl.Type.AsCXXRecordDecl?.IsAnonymousStructOrUnion == true)
{
// For fields of anonymous types, use the name of the type but clean off the type
// kind tag at the end.
@@ -3233,12 +3242,65 @@ private string GetRemappedCursorName(NamedDecl namedDecl, out string nativeTypeN
return remappedName;
}
+ private static int GetAnonymousRecordIndex(RecordDecl recordDecl, RecordDecl parentRecordDecl)
+ {
+ var index = -1;
+ var parentAnonRecordCount = parentRecordDecl.AnonymousRecords.Count;
+
+ if (parentAnonRecordCount != 0)
+ {
+ index = parentRecordDecl.AnonymousRecords.IndexOf(recordDecl);
+
+ if (index != -1)
+ {
+ if (parentAnonRecordCount > 1)
+ {
+ index++;
+ }
+
+ if (parentRecordDecl.Parent is RecordDecl grandparentRecordDecl)
+ {
+ var parentIndex = GetAnonymousRecordIndex(parentRecordDecl, grandparentRecordDecl);
+
+ // We can't have the nested anonymous record have the same name as the parent
+ // so skip that index and just go one higher instead. This could still conflict
+ // with another anonymous record at a different level, but that is less likely
+ // and will still be unambiguous in total.
+
+ if ((parentIndex == index) || ((parentIndex > 0) && (index > parentIndex)))
+ {
+ if (recordDecl.IsUnion == parentRecordDecl.IsUnion)
+ {
+ index++;
+ }
+ }
+ }
+ }
+ }
+
+ return index;
+ }
+
private string GetRemappedNameForAnonymousRecord(RecordDecl recordDecl)
{
if (recordDecl.Parent is RecordDecl parentRecordDecl)
{
var remappedNameBuilder = new StringBuilder();
- var matchingField = parentRecordDecl.Fields.Where((fieldDecl) => fieldDecl.Type.CanonicalType == recordDecl.TypeForDecl.CanonicalType).FirstOrDefault();
+ var matchingField = null as FieldDecl;
+
+ if (!recordDecl.IsAnonymousStructOrUnion)
+ {
+ matchingField = parentRecordDecl.Fields.Where((fieldDecl) => {
+ var fieldType = fieldDecl.Type.CanonicalType;
+
+ if (fieldType is ArrayType arrayType)
+ {
+ fieldType = arrayType.ElementType.CanonicalType;
+ }
+
+ return fieldType == recordDecl.TypeForDecl.CanonicalType;
+ }).FirstOrDefault();
+ }
if ((matchingField is not null) && !matchingField.IsAnonymousField)
{
@@ -3249,28 +3311,11 @@ private string GetRemappedNameForAnonymousRecord(RecordDecl recordDecl)
{
_ = remappedNameBuilder.Append("_Anonymous");
- // If there is more than one anonymous type, then add a numeral to differentiate.
- if (parentRecordDecl.AnonymousRecords.Count > 1)
- {
- var index = parentRecordDecl.AnonymousRecords.IndexOf(recordDecl) + 1;
- Debug.Assert(index > 0);
- _ = remappedNameBuilder.Append(index);
- }
+ var index = GetAnonymousRecordIndex(recordDecl, parentRecordDecl);
- // C# doesn't allow a nested type to have the same name as the parent, so if the
- // parent is also anonymous, add the nesting depth as a way to avoid conflicts with
- // the parent's name.
- if (parentRecordDecl.IsAnonymous)
+ if (index != 0)
{
- var depth = 1;
- var currentParent = parentRecordDecl.Parent;
- while ((currentParent is RecordDecl currentParentRecordDecl) && currentParentRecordDecl.IsAnonymous)
- {
- depth++;
- currentParent = currentParentRecordDecl.Parent;
- }
- _ = remappedNameBuilder.Append('_');
- _ = remappedNameBuilder.Append(depth);
+ _ = remappedNameBuilder.Append(index);
}
}
@@ -3290,7 +3335,9 @@ private string GetRemappedName(string name, Cursor? cursor, bool tryRemapOperato
private string GetRemappedName(string name, Cursor? cursor, bool tryRemapOperatorName, out bool wasRemapped, bool skipUsing, bool skipUsingIfNotRemapped)
{
- if (_config.RemappedNames.TryGetValue(name, out var remappedName))
+ var remappedNamesLookup = _config._remappedNames.GetAlternateLookup>();
+
+ if (remappedNamesLookup.TryGetValue(name, out var remappedName))
{
wasRemapped = true;
_ = _usedRemappings.Add(name);
@@ -3299,13 +3346,13 @@ private string GetRemappedName(string name, Cursor? cursor, bool tryRemapOperato
if (name.StartsWith("const ", StringComparison.Ordinal))
{
- var tmpName = name[6..];
+ var tmpName = name.AsSpan()[6..];
- if (_config.RemappedNames.TryGetValue(tmpName, out remappedName))
+ if (remappedNamesLookup.TryGetValue(tmpName, out remappedName))
{
wasRemapped = true;
- _ = _usedRemappings.Add(tmpName);
+ _ = _usedRemappings.Add(tmpName.ToString());
return AddUsingDirectiveIfNeeded(_outputBuilder, remappedName, skipUsing);
}
}
@@ -3363,38 +3410,32 @@ private string GetRemappedTypeName(Cursor? cursor, Cursor? context, Type type, o
if (!wasRemapped)
{
- nameToCheck = nameToCheck.Replace("::", ".", StringComparison.Ordinal);
- remappedName = GetRemappedName(nameToCheck, cursor, tryRemapOperatorName: false, out wasRemapped, skipUsing, skipUsingIfNotRemapped: true);
+ nameToCheck = name;
+ remappedName = GetRemappedName(nameToCheck, cursor, tryRemapOperatorName: false, out wasRemapped, skipUsing);
if (!wasRemapped)
{
- nameToCheck = name;
- remappedName = GetRemappedName(nameToCheck, cursor, tryRemapOperatorName: false, out wasRemapped, skipUsing);
-
- if (!wasRemapped)
+ if (IsTypeConstantOrIncompleteArray(cursor, type, out var arrayType) && IsType(cursor, arrayType.ElementType))
{
- if (IsTypeConstantOrIncompleteArray(cursor, type, out var arrayType) && IsType(cursor, arrayType.ElementType))
- {
- type = arrayType.ElementType;
- }
+ type = arrayType.ElementType;
+ }
- if (IsType(cursor, type, out var recordType) && remappedName.StartsWith("__AnonymousRecord_", StringComparison.Ordinal))
- {
- var recordDecl = recordType.Decl;
- remappedName = GetRemappedNameForAnonymousRecord(recordDecl);
- }
- else if (IsType(cursor, type, out var enumType) && remappedName.StartsWith("__AnonymousEnum_", StringComparison.Ordinal))
- {
- remappedName = GetRemappedTypeName(enumType.Decl, context: null, enumType.Decl.IntegerType, out _, skipUsing);
- }
- else if (cursor is EnumDecl enumDecl)
- {
- // Even though some types have entries with names like *_FORCE_DWORD or *_FORCE_UINT
- // MSVC and Clang both still treat this as "signed" values and thus we don't want
- // to specially handle it as uint, as that can break ABI handling on some platforms.
+ if (IsType(cursor, type, out var recordType) && remappedName.StartsWith("__AnonymousRecord_", StringComparison.Ordinal))
+ {
+ var recordDecl = recordType.Decl;
+ remappedName = GetRemappedNameForAnonymousRecord(recordDecl);
+ }
+ else if (IsType(cursor, type, out var enumType) && remappedName.StartsWith("__AnonymousEnum_", StringComparison.Ordinal))
+ {
+ remappedName = GetRemappedTypeName(enumType.Decl, context: null, enumType.Decl.IntegerType, out _, skipUsing);
+ }
+ else if (cursor is EnumDecl enumDecl)
+ {
+ // Even though some types have entries with names like *_FORCE_DWORD or *_FORCE_UINT
+ // MSVC and Clang both still treat this as "signed" values and thus we don't want
+ // to specially handle it as uint, as that can break ABI handling on some platforms.
- WithType(enumDecl, ref remappedName, ref nativeTypeName);
- }
+ WithType(enumDecl, ref remappedName, ref nativeTypeName);
}
}
}
@@ -3418,7 +3459,19 @@ private string GetRemappedTypeName(Cursor? cursor, Cursor? context, Type type, o
return remappedName;
}
- private static string GetSourceRangeContents(CXTranslationUnit translationUnit, CXSourceRange sourceRange)
+ private unsafe ReadOnlySpan GetFileContents(CXTranslationUnit translationUnit, CXFile file)
+ {
+ if (!_fileContents.TryGetValue(file, out var fileContentsMetadata))
+ {
+ var fileContents = translationUnit.GetFileContents(file, out _);
+ fileContentsMetadata = ((nuint)Unsafe.AsPointer(ref MemoryMarshal.GetReference(fileContents)), (uint)fileContents.Length);
+ _fileContents.Add(file, fileContentsMetadata);
+ }
+
+ return new ReadOnlySpan((byte*)fileContentsMetadata.Address, (int)fileContentsMetadata.Length);
+ }
+
+ private string GetSourceRangeContents(CXTranslationUnit translationUnit, CXSourceRange sourceRange)
{
sourceRange.Start.GetFileLocation(out var startFile, out _, out _, out var startOffset);
sourceRange.End.GetFileLocation(out var endFile, out _, out _, out var endOffset);
@@ -3428,9 +3481,9 @@ private static string GetSourceRangeContents(CXTranslationUnit translationUnit,
return string.Empty;
}
- var fileContents = translationUnit.GetFileContents(startFile, out _);
- fileContents = fileContents.Slice(unchecked((int)startOffset), unchecked((int)(endOffset - startOffset)));
- return Encoding.UTF8.GetString(fileContents);
+ var contents = GetFileContents(translationUnit, startFile);
+ contents = contents.Slice(unchecked((int)startOffset), unchecked((int)(endOffset - startOffset)));
+ return Encoding.UTF8.GetString(contents);
}
private string GetTargetTypeName(Cursor cursor, out string nativeTypeName)
@@ -3466,13 +3519,13 @@ private string GetTargetTypeName(Cursor cursor, out string nativeTypeName)
else
{
var type = varDecl.Type;
- var cursorName = GetCursorName(varDecl);
+ var cursorName = GetCursorName(varDecl).AsSpan();
if (cursorName.StartsWith("ClangSharpMacro_", StringComparison.Ordinal))
{
cursorName = cursorName["ClangSharpMacro_".Length..];
- if (_config.WithTypes.TryGetValue(cursorName, out targetTypeName))
+ if (_config._withTypes.GetAlternateLookup>().TryGetValue(cursorName, out targetTypeName))
{
return targetTypeName;
}
@@ -4586,7 +4639,7 @@ private void GetTypeSize(Cursor cursor, Type type, ref long alignment32, ref lon
}
private bool HasSuppressGCTransition(Cursor? cursor)
- => (cursor is NamedDecl namedDecl) && HasRemapping(namedDecl, _config.WithSuppressGCTransitions);
+ => (cursor is NamedDecl namedDecl) && HasRemapping(namedDecl, _config._withSuppressGCTransitions);
private bool HasBaseField(CXXRecordDecl cxxRecordDecl)
{
@@ -4608,7 +4661,7 @@ private bool HasBaseField(CXXRecordDecl cxxRecordDecl)
private bool HasField(RecordDecl recordDecl)
{
- var hasField = recordDecl.Fields.Any() || recordDecl.Decls.Any((decl) => (decl is RecordDecl nestedRecordDecl) && nestedRecordDecl.IsAnonymous && HasField(nestedRecordDecl));
+ var hasField = recordDecl.Fields.Any() || recordDecl.Decls.Any((decl) => (decl is RecordDecl nestedRecordDecl) && nestedRecordDecl.IsAnonymousStructOrUnion && HasField(nestedRecordDecl));
if (!hasField && (recordDecl is CXXRecordDecl cxxRecordDecl))
{
@@ -4865,9 +4918,7 @@ bool IsExcludedByName(Cursor cursor, ref uint isExcludedValue)
}
}
- var dottedQualifiedName = qualifiedName.Replace("::", ".", StringComparison.Ordinal);
-
- if (_config.ExcludedNames.Contains(qualifiedName) || _config.ExcludedNames.Contains(dottedQualifiedName))
+ if (_config.ExcludedNames.Contains(qualifiedName))
{
if (_config.LogExclusions)
{
@@ -4887,9 +4938,7 @@ bool IsExcludedByName(Cursor cursor, ref uint isExcludedValue)
return true;
}
- var dottedQualifiedNameWithoutParameters = qualifiedNameWithoutParameters.Replace("::", ".", StringComparison.Ordinal);
-
- if (_config.ExcludedNames.Contains(qualifiedNameWithoutParameters) || _config.ExcludedNames.Contains(dottedQualifiedNameWithoutParameters) || _config.ExcludedNames.Contains(name))
+ if (_config.ExcludedNames.Contains(qualifiedNameWithoutParameters) || _config.ExcludedNames.Contains(name))
{
if (_config.LogExclusions)
{
@@ -4919,9 +4968,7 @@ bool IsExcludedByName(Cursor cursor, ref uint isExcludedValue)
}
if (_config.IncludedNames.Count != 0 && !_config.IncludedNames.Contains(qualifiedName)
- && !_config.IncludedNames.Contains(dottedQualifiedName)
&& !_config.IncludedNames.Contains(qualifiedNameWithoutParameters)
- && !_config.IncludedNames.Contains(dottedQualifiedNameWithoutParameters)
&& !_config.IncludedNames.Contains(name))
{
var semanticParentCursor = cursor.SemanticParentCursor;
@@ -5159,7 +5206,7 @@ bool IsEmptyRecord(RecordDecl recordDecl)
foreach (var decl in recordDecl.Decls)
{
- if ((decl is RecordDecl nestedRecordDecl) && nestedRecordDecl.IsAnonymous && !IsEmptyRecord(nestedRecordDecl))
+ if ((decl is RecordDecl nestedRecordDecl) && nestedRecordDecl.IsAnonymousStructOrUnion && !IsEmptyRecord(nestedRecordDecl))
{
return false;
}
@@ -5210,9 +5257,7 @@ private bool IsBaseExcluded(CXXRecordDecl cxxRecordDecl, CXXRecordDecl baseCxxRe
baseFieldName = GetRemappedName(baseFieldName, cxxBaseSpecifier, tryRemapOperatorName: true, out _, skipUsing: true);
var qualifiedName = $"{GetCursorQualifiedName(cxxRecordDecl)}::{baseFieldName}";
- var dottedQualifiedName = qualifiedName.Replace("::", ".", StringComparison.Ordinal);
-
- return _config.ExcludedNames.Contains(qualifiedName) || _config.ExcludedNames.Contains(dottedQualifiedName);
+ return _config.ExcludedNames.Contains(qualifiedName);
}
private bool IsFixedSize(Cursor cursor, Type type)
@@ -5350,6 +5395,15 @@ private bool IsPrevContextStmt([MaybeNullWhen(false)] out T cursor, out objec
}
}
+ private bool IsReadonly(CXXMethodDecl? cxxMethodDecl)
+ {
+ if (cxxMethodDecl is not null)
+ {
+ return cxxMethodDecl.IsConst || HasRemapping(cxxMethodDecl, _config._withReadonlys, matchStar: true);
+ }
+ return false;
+ }
+
private static bool IsStmtAsWritten(Cursor cursor, [MaybeNullWhen(false)] out T value, bool removeParens = false)
where T : Stmt
{
@@ -5536,7 +5590,7 @@ internal bool IsSupportedFixedSizedBufferType(string typeName)
case "ulong":
{
// We want to prefer InlineArray in modern code, as it is safer and supports more features
- return !Config.GenerateLatestCode;
+ return Config.GenerateCompatibleCode;
}
default:
@@ -6186,7 +6240,7 @@ private bool IsUnsafe(RecordDecl recordDecl)
{
return true;
}
- else if ((decl is RecordDecl nestedRecordDecl) && nestedRecordDecl.IsAnonymous && (IsUnsafe(nestedRecordDecl) || Config.GenerateCompatibleCode))
+ else if ((decl is RecordDecl nestedRecordDecl) && nestedRecordDecl.IsAnonymousStructOrUnion && (IsUnsafe(nestedRecordDecl) || Config.GenerateCompatibleCode))
{
return true;
}
@@ -6473,7 +6527,7 @@ private void StopUsingOutputBuilder()
private bool TryGetUuid(RecordDecl recordDecl, out Guid uuid)
{
- if (TryGetRemappedValue(recordDecl, _config.WithGuids, out var guid))
+ if (TryGetRemappedValue(recordDecl, _config._withGuids, out var guid))
{
uuid = guid;
return true;
@@ -6807,7 +6861,7 @@ private void WithAttributes(NamedDecl namedDecl, bool onlySupportedOSPlatform =
var outputBuilder = isTestOutput ? _testOutputBuilder : _outputBuilder;
Debug.Assert(outputBuilder is not null);
- if (TryGetRemappedValue(namedDecl, _config.WithAttributes, out var attributes, matchStar: true))
+ if (TryGetRemappedValue(namedDecl, _config._withAttributes, out var attributes, matchStar: true))
{
foreach (var attribute in attributes.Where((a) => !onlySupportedOSPlatform || a.StartsWith("SupportedOSPlatform(", StringComparison.Ordinal)))
{
@@ -6907,16 +6961,16 @@ private string GetClass(string remappedName, bool disallowPrefixMatch = false)
return className;
}
- private bool TryGetClass(string remappedName, [MaybeNullWhen(false)] out string className, bool disallowPrefixMatch = false)
+ private bool TryGetClass(ReadOnlySpan remappedName, [MaybeNullWhen(false)] out string className, bool disallowPrefixMatch = false)
{
- var index = remappedName.IndexOf('*', StringComparison.Ordinal);
+ var index = remappedName.IndexOf('*');
if (index != -1)
{
remappedName = remappedName[..index];
}
- if (_config.WithClasses.TryGetValue(remappedName, out className))
+ if (_config._withClasses.GetAlternateLookup>().TryGetValue(remappedName, out className))
{
_ = _topLevelClassNames.Add(className);
_ = _topLevelClassNames.Add($"{className}Tests");
@@ -6928,14 +6982,14 @@ private bool TryGetClass(string remappedName, [MaybeNullWhen(false)] out string
return false;
}
- foreach (var withClass in _config.WithClasses)
+ foreach (var withClass in _config._withClasses)
{
if (!withClass.Key.EndsWith('*'))
{
continue;
}
- var prefix = withClass.Key[0..^1];
+ var prefix = withClass.Key.AsSpan()[..^1];
if (remappedName.StartsWith(prefix, StringComparison.Ordinal))
{
@@ -6948,7 +7002,7 @@ private bool TryGetClass(string remappedName, [MaybeNullWhen(false)] out string
return false;
}
- private string GetNamespace(string remappedName, NamedDecl? namedDecl = null)
+ private string GetNamespace(ReadOnlySpan remappedName, NamedDecl? namedDecl = null)
{
if (!TryGetNamespace(remappedName, out var namespaceName))
{
@@ -6965,37 +7019,32 @@ private string GetNamespace(string remappedName, NamedDecl? namedDecl = null)
return namespaceName;
}
- private bool TryGetNamespace(string remappedName, [MaybeNullWhen(false)] out string namespaceName)
+ private bool TryGetNamespace(ReadOnlySpan remappedName, [MaybeNullWhen(false)] out string namespaceName)
{
- var index = remappedName.IndexOf('*', StringComparison.Ordinal);
+ var index = remappedName.IndexOf('*');
if (index != -1)
{
remappedName = remappedName[..index];
}
- return _config.WithNamespaces.TryGetValue(remappedName, out namespaceName);
+ return _config._withNamespaces.GetAlternateLookup>().TryGetValue(remappedName, out namespaceName);
}
- private bool GetSetLastError(NamedDecl namedDecl) => HasRemapping(namedDecl, _config.WithSetLastErrors, matchStar: true);
+ private bool GetSetLastError(NamedDecl namedDecl) => HasRemapping(namedDecl, _config._withSetLastErrors, matchStar: true);
- private bool HasRemapping(NamedDecl namedDecl, IReadOnlyCollection entries, bool matchStar = false)
+ private bool HasRemapping(NamedDecl namedDecl, HashSet entries, bool matchStar = false)
{
- var name = GetCursorQualifiedName(namedDecl);
+ var name = GetCursorQualifiedName(namedDecl).AsSpan();
if (name.StartsWith("ClangSharpMacro_", StringComparison.Ordinal))
{
name = name["ClangSharpMacro_".Length..];
}
- if (entries.Contains(name))
- {
- return true;
- }
-
- name = name.Replace("::", ".", StringComparison.Ordinal);
+ var entriesLookup = entries.GetAlternateLookup>();
- if (entries.Contains(name))
+ if (entriesLookup.Contains(name))
{
return true;
}
@@ -7007,14 +7056,7 @@ private bool HasRemapping(NamedDecl namedDecl, IReadOnlyCollection entri
name = name["ClangSharpMacro_".Length..];
}
- if (entries.Contains(name))
- {
- return true;
- }
-
- name = name.Replace("::", ".", StringComparison.Ordinal);
-
- if (entries.Contains(name))
+ if (entriesLookup.Contains(name))
{
return true;
}
@@ -7026,26 +7068,21 @@ private bool HasRemapping(NamedDecl namedDecl, IReadOnlyCollection entri
name = name["ClangSharpMacro_".Length..];
}
- return entries.Contains(name) || (matchStar && entries.Contains("*"));
+ return entriesLookup.Contains(name) || (matchStar && entriesLookup.Contains("*"));
}
- private bool TryGetRemappedValue(NamedDecl namedDecl, IReadOnlyDictionary remappings, [MaybeNullWhen(false)] out T value, bool matchStar = false)
+ private bool TryGetRemappedValue(NamedDecl namedDecl, Dictionary remappings, [MaybeNullWhen(false)] out T value, bool matchStar = false)
{
- var name = GetCursorQualifiedName(namedDecl);
+ var name = GetCursorQualifiedName(namedDecl).AsSpan();
if (name.StartsWith("ClangSharpMacro_", StringComparison.Ordinal))
{
name = name["ClangSharpMacro_".Length..];
}
- if (remappings.TryGetValue(name, out value))
- {
- return true;
- }
+ var remappingsLookup = remappings.GetAlternateLookup>();
- name = name.Replace("::", ".", StringComparison.Ordinal);
-
- if (remappings.TryGetValue(name, out value))
+ if (remappingsLookup.TryGetValue(name, out value))
{
return true;
}
@@ -7057,14 +7094,7 @@ private bool TryGetRemappedValue(NamedDecl namedDecl, IReadOnlyDictionary(NamedDecl namedDecl, IReadOnlyDictionary _excludedNames;
- private readonly SortedSet _forceRemappedNames;
- private readonly SortedSet _includedNames;
- private readonly SortedSet _nativeTypeNamesToStrip;
- private readonly SortedSet _withManualImports;
- private readonly SortedSet _traversalNames;
- private readonly SortedSet _withSetLastErrors;
- private readonly SortedSet _withSuppressGCTransitions;
-
- private readonly SortedDictionary _remappedNames;
- private readonly SortedDictionary _withAccessSpecifiers;
- private readonly SortedDictionary> _withAttributes;
- private readonly SortedDictionary _withCallConvs;
- private readonly SortedDictionary _withClasses;
- private readonly SortedDictionary _withGuids;
- private readonly SortedDictionary _withLengths;
- private readonly SortedDictionary _withLibraryPaths;
- private readonly SortedDictionary _withNamespaces;
- private readonly SortedDictionary _withTransparentStructs;
- private readonly SortedDictionary _withTypes;
- private readonly SortedDictionary> _withUsings;
- private readonly SortedDictionary _withPackings;
+ internal readonly HashSet _excludedNames;
+ private readonly HashSet _forceRemappedNames;
+ private readonly HashSet _includedNames;
+ private readonly HashSet _nativeTypeNamesToStrip;
+ private readonly HashSet _withManualImports;
+ private readonly HashSet _traversalNames;
+ internal readonly HashSet _withReadonlys;
+ internal readonly HashSet _withSetLastErrors;
+ internal readonly HashSet _withSuppressGCTransitions;
+
+ internal readonly Dictionary _remappedNames;
+ internal readonly Dictionary _withAccessSpecifiers;
+ internal readonly Dictionary> _withAttributes;
+ internal readonly Dictionary _withCallConvs;
+ internal readonly Dictionary _withClasses;
+ internal readonly Dictionary _withGuids;
+ internal readonly Dictionary _withLengths;
+ private readonly Dictionary _withLibraryPaths;
+ internal readonly Dictionary _withNamespaces;
+ private readonly Dictionary _withTransparentStructs;
+ internal readonly Dictionary _withTypes;
+ internal readonly Dictionary> _withUsings;
+ internal readonly Dictionary _withPackings;
private PInvokeGeneratorConfigurationOptions _options;
@@ -81,28 +82,29 @@ public PInvokeGeneratorConfiguration(string language, string languageStandard, s
_methodPrefixToStrip = DefaultMethodPrefixToStripValue;
_testOutputLocation = DefaultTestOutputLocationValue;
- _excludedNames = [];
- _forceRemappedNames = [];
- _includedNames = [];
- _nativeTypeNamesToStrip = [];
- _withManualImports = [];
- _traversalNames = [];
- _withSetLastErrors = [];
- _withSuppressGCTransitions = [];
-
- _remappedNames = [];
- _withAccessSpecifiers = [];
- _withAttributes = [];
- _withCallConvs = [];
- _withClasses = [];
- _withGuids = [];
- _withLengths = [];
- _withLibraryPaths = [];
- _withNamespaces = [];
- _withTransparentStructs = [];
- _withTypes = [];
- _withUsings = [];
- _withPackings = [];
+ _excludedNames = new HashSet(QualifiedNameComparer.Default);
+ _forceRemappedNames = new HashSet(QualifiedNameComparer.Default);
+ _includedNames = new HashSet(QualifiedNameComparer.Default);
+ _nativeTypeNamesToStrip = new HashSet(StringComparer.Ordinal);
+ _withManualImports = new HashSet(StringComparer.Ordinal);
+ _traversalNames = new HashSet(StringComparer.Ordinal);
+ _withReadonlys = new HashSet(QualifiedNameComparer.Default);
+ _withSetLastErrors = new HashSet(QualifiedNameComparer.Default);
+ _withSuppressGCTransitions = new HashSet(QualifiedNameComparer.Default);
+
+ _remappedNames = new Dictionary(QualifiedNameComparer.Default);
+ _withAccessSpecifiers = new Dictionary(QualifiedNameComparer.Default);
+ _withAttributes = new Dictionary>(QualifiedNameComparer.Default);
+ _withCallConvs = new Dictionary(QualifiedNameComparer.Default);
+ _withClasses = new Dictionary(StringComparer.Ordinal);
+ _withGuids = new Dictionary(QualifiedNameComparer.Default);
+ _withLengths = new Dictionary(QualifiedNameComparer.Default);
+ _withLibraryPaths = new Dictionary(StringComparer.Ordinal);
+ _withNamespaces = new Dictionary(StringComparer.Ordinal);
+ _withTransparentStructs = new Dictionary(StringComparer.Ordinal);
+ _withTypes = new Dictionary(QualifiedNameComparer.Default);
+ _withUsings = new Dictionary>(QualifiedNameComparer.Default);
+ _withPackings = new Dictionary(QualifiedNameComparer.Default);
if ((outputMode == PInvokeGeneratorOutputMode.Xml) && !options.HasFlag(PInvokeGeneratorConfigurationOptions.GenerateMultipleFiles) && (options.HasFlag(PInvokeGeneratorConfigurationOptions.GenerateTestsNUnit) || options.HasFlag(PInvokeGeneratorConfigurationOptions.GenerateTestsXUnit)))
{
@@ -516,6 +518,20 @@ public IReadOnlyDictionary WithNamespaces
}
}
+ [AllowNull]
+ public IReadOnlyCollection WithReadonlys
+ {
+ get
+ {
+ return _withReadonlys;
+ }
+
+ init
+ {
+ AddRange(_withReadonlys, value);
+ }
+ }
+
[AllowNull]
public IReadOnlyCollection WithSetLastErrors
{
@@ -612,7 +628,7 @@ public static AccessSpecifier ConvertStringToAccessSpecifier(string input)
: input.Equals("public", StringComparison.OrdinalIgnoreCase) ? AccessSpecifier.Public : AccessSpecifier.None;
}
- private static void AddRange(SortedDictionary dictionary, IEnumerable>? keyValuePairs)
+ private static void AddRange(Dictionary dictionary, IEnumerable>? keyValuePairs)
{
if (keyValuePairs != null)
{
@@ -625,7 +641,7 @@ private static void AddRange(SortedDictionary dictionary
}
}
- private static void AddRange(SortedDictionary dictionary, IEnumerable>? keyValuePairs, Func convert)
+ private static void AddRange(Dictionary dictionary, IEnumerable>? keyValuePairs, Func convert)
{
if (keyValuePairs != null)
{
@@ -638,7 +654,7 @@ private static void AddRange(SortedDictionary di
}
}
- private static void AddRange(SortedSet hashSet, IEnumerable? keys)
+ private static void AddRange(HashSet hashSet, IEnumerable? keys)
{
if (keys != null)
{
@@ -649,7 +665,7 @@ private static void AddRange(SortedSet hashSet, IEnumerable(SortedSet hashSet, IEnumerable? keys, Func convert)
+ private static void AddRange(HashSet hashSet, IEnumerable? keys, Func convert)
{
if (keys != null)
{
@@ -660,7 +676,7 @@ private static void AddRange(SortedSet hashSet, IEnumera
}
}
- private static void AddRange(SortedSet hashSet, IEnumerable>? keyValuePairs, Func shouldAdd)
+ private static void AddRange(HashSet hashSet, IEnumerable>? keyValuePairs, Func shouldAdd)
{
if (keyValuePairs != null)
{
diff --git a/sources/ClangSharp.PInvokeGenerator/QualifiedNameComparer.cs b/sources/ClangSharp.PInvokeGenerator/QualifiedNameComparer.cs
new file mode 100644
index 00000000..820522ac
--- /dev/null
+++ b/sources/ClangSharp.PInvokeGenerator/QualifiedNameComparer.cs
@@ -0,0 +1,105 @@
+// Copyright © Tanner Gooding and Contributors. Licensed under the MIT License (MIT). See License.md in the repository root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+
+namespace ClangSharp;
+
+internal class QualifiedNameComparer : IEqualityComparer, IAlternateEqualityComparer, string>
+{
+ public static readonly QualifiedNameComparer Default = new QualifiedNameComparer();
+
+ public string Create(ReadOnlySpan alternate) => alternate.ToString();
+
+ public bool Equals(ReadOnlySpan alternate, string other) => Equals(alternate, other.AsSpan());
+
+ public static bool Equals(ReadOnlySpan alternate, ReadOnlySpan other)
+ {
+ while ((alternate.Length != 0) && (other.Length != 0))
+ {
+ if (alternate.StartsWith("::", StringComparison.Ordinal))
+ {
+ if (other.StartsWith('.'))
+ {
+ alternate = alternate[2..];
+ other = other[1..];
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else if (alternate.StartsWith('.'))
+ {
+ if (other.StartsWith("::", StringComparison.Ordinal))
+ {
+ alternate = alternate[1..];
+ other = other[2..];
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ var prefixLength = alternate.CommonPrefixLength(other);
+
+ if (prefixLength == 0)
+ {
+ break;
+ }
+
+ alternate = alternate[prefixLength..];
+ other = other[prefixLength..];
+ }
+
+ return (alternate.Length == 0) && (other.Length == 0);
+ }
+
+ public bool Equals(string? x, string? y) => Equals(x.AsSpan(), y.AsSpan());
+
+ public int GetHashCode(ReadOnlySpan alternate)
+ {
+ var hashCode = new HashCode();
+
+ while (alternate.Length != 0)
+ {
+ var part = alternate;
+ var separatorLength = 0;
+
+ var colonSeparatorIndex = part.IndexOf("::", StringComparison.Ordinal);
+
+ if (colonSeparatorIndex != -1)
+ {
+ part = part[..colonSeparatorIndex];
+ separatorLength = 2;
+ }
+
+ var dotSeparatorIndex = part.IndexOf('.');
+
+ if (dotSeparatorIndex != -1)
+ {
+ part = part[..dotSeparatorIndex];
+ separatorLength = 1;
+ }
+
+ hashCode.Add(string.GetHashCode(part, StringComparison.Ordinal));
+
+ if (separatorLength != 0)
+ {
+ hashCode.Add('.');
+ alternate = alternate[(part.Length + separatorLength)..];
+ }
+ else
+ {
+ alternate = alternate[part.Length..];
+ }
+ }
+
+ return hashCode.ToHashCode();
+ }
+
+ public int GetHashCode([DisallowNull] string obj) => GetHashCode(obj.AsSpan());
+}
diff --git a/sources/ClangSharp.PInvokeGenerator/XML/XmlOutputBuilder.VisitDecl.cs b/sources/ClangSharp.PInvokeGenerator/XML/XmlOutputBuilder.VisitDecl.cs
index 4b9ca7a2..6a4a6f4a 100644
--- a/sources/ClangSharp.PInvokeGenerator/XML/XmlOutputBuilder.VisitDecl.cs
+++ b/sources/ClangSharp.PInvokeGenerator/XML/XmlOutputBuilder.VisitDecl.cs
@@ -142,6 +142,11 @@ public void BeginFunctionOrDelegate(in FunctionOrDelegateDesc desc, ref bool isM
_ = _sb.Append(" static=\"true\"");
}
+ if (desc.IsReadOnly)
+ {
+ _ = _sb.Append(" readonly=\"true\"");
+ }
+
if (desc.IsUnsafe)
{
_ = _sb.Append(" unsafe=\"true\"");
diff --git a/sources/ClangSharp/ClangSharp.csproj b/sources/ClangSharp/ClangSharp.csproj
index d5fee39f..919628d5 100644
--- a/sources/ClangSharp/ClangSharp.csproj
+++ b/sources/ClangSharp/ClangSharp.csproj
@@ -2,14 +2,15 @@
- net8.0
+ net10.0
- $(NoWarn);CA1034;CA1040;CA1720
+
+ $(NoWarn);CA1034;CA1040;CA1720;IDE0130
diff --git a/sources/ClangSharp/Cursors/Cursor.cs b/sources/ClangSharp/Cursors/Cursor.cs
index 95783bcb..4a60a899 100644
--- a/sources/ClangSharp/Cursors/Cursor.cs
+++ b/sources/ClangSharp/Cursors/Cursor.cs
@@ -13,11 +13,11 @@ namespace ClangSharp;
[DebuggerDisplay("{Handle.DebuggerDisplayString,nq}")]
public unsafe class Cursor : IEquatable
{
- private readonly Lazy _kindSpelling;
- private readonly Lazy _lexicalParentCursor;
- private readonly Lazy _semanticParentCursor;
- private readonly Lazy _spelling;
- private readonly Lazy _translationUnit;
+ private readonly ValueLazy _kindSpelling;
+ private readonly ValueLazy _lexicalParentCursor;
+ private readonly ValueLazy _semanticParentCursor;
+ private readonly ValueLazy _spelling;
+ private readonly ValueLazy _translationUnit;
private List? _cursorChildren;
private protected Cursor(CXCursor handle, CXCursorKind expectedCursorKind)
@@ -28,11 +28,11 @@ private protected Cursor(CXCursor handle, CXCursorKind expectedCursorKind)
}
Handle = handle;
- _kindSpelling = new Lazy(Handle.KindSpelling.ToString);
- _lexicalParentCursor = new Lazy(() => !Handle.LexicalParent.IsNull ? TranslationUnit.GetOrCreate(Handle.LexicalParent) : null);
- _semanticParentCursor = new Lazy(() => !Handle.SemanticParent.IsNull ? TranslationUnit.GetOrCreate(Handle.SemanticParent) : null);
- _spelling = new Lazy(Handle.Spelling.ToString);
- _translationUnit = new Lazy(() => TranslationUnit.GetOrCreate(Handle.TranslationUnit));
+ _kindSpelling = new ValueLazy(Handle.KindSpelling.ToString);
+ _lexicalParentCursor = new ValueLazy(() => !Handle.LexicalParent.IsNull ? TranslationUnit.GetOrCreate(Handle.LexicalParent) : null);
+ _semanticParentCursor = new ValueLazy(() => !Handle.SemanticParent.IsNull ? TranslationUnit.GetOrCreate